FreeRDP
Loading...
Searching...
No Matches
nsc_encode.c
1
22#include <freerdp/config.h>
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include <winpr/assert.h>
29#include <winpr/cast.h>
30#include <winpr/crt.h>
31
32#include <freerdp/codec/nsc.h>
33#include <freerdp/codec/color.h>
34
35#include "nsc_types.h"
36#include "nsc_encode.h"
37
38typedef struct
39{
40 UINT32 x;
41 UINT32 y;
42 UINT32 width;
43 UINT32 height;
44 const BYTE* data;
45 UINT32 scanline;
46 BYTE* PlaneBuffer;
47 UINT32 MaxPlaneSize;
48 BYTE* PlaneBuffers[5];
49 UINT32 OrgByteCount[4];
50
51 UINT32 LumaPlaneByteCount;
52 UINT32 OrangeChromaPlaneByteCount;
53 UINT32 GreenChromaPlaneByteCount;
54 UINT32 AlphaPlaneByteCount;
55 UINT8 ColorLossLevel;
56 UINT8 ChromaSubsamplingLevel;
57} NSC_MESSAGE;
58
59static BOOL nsc_write_message(NSC_CONTEXT* WINPR_RESTRICT context, wStream* WINPR_RESTRICT s,
60 const NSC_MESSAGE* WINPR_RESTRICT message);
61
62static BOOL nsc_context_initialize_encode(NSC_CONTEXT* WINPR_RESTRICT context)
63{
64 UINT32 length = 0;
65 UINT32 tempWidth = 0;
66 UINT32 tempHeight = 0;
67 tempWidth = ROUND_UP_TO(context->width, 8);
68 tempHeight = ROUND_UP_TO(context->height, 2);
69 /* The maximum length a decoded plane can reach in all cases */
70 length = tempWidth * tempHeight + 16;
71
72 if (length > context->priv->PlaneBuffersLength)
73 {
74 for (int i = 0; i < 5; i++)
75 {
76 BYTE* tmp = (BYTE*)winpr_aligned_recalloc(context->priv->PlaneBuffers[i], length,
77 sizeof(BYTE), 32);
78
79 if (!tmp)
80 goto fail;
81
82 context->priv->PlaneBuffers[i] = tmp;
83 }
84
85 context->priv->PlaneBuffersLength = length;
86 }
87
88 if (context->ChromaSubsamplingLevel)
89 {
90 context->OrgByteCount[0] = tempWidth * context->height;
91 context->OrgByteCount[1] = tempWidth * tempHeight / 4;
92 context->OrgByteCount[2] = tempWidth * tempHeight / 4;
93 context->OrgByteCount[3] = context->width * context->height;
94 }
95 else
96 {
97 context->OrgByteCount[0] = context->width * context->height;
98 context->OrgByteCount[1] = context->width * context->height;
99 context->OrgByteCount[2] = context->width * context->height;
100 context->OrgByteCount[3] = context->width * context->height;
101 }
102
103 return TRUE;
104fail:
105
106 if (length > context->priv->PlaneBuffersLength)
107 {
108 for (int i = 0; i < 5; i++)
109 winpr_aligned_free(context->priv->PlaneBuffers[i]);
110 }
111
112 return FALSE;
113}
114
115static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* WINPR_RESTRICT context,
116 const BYTE* WINPR_RESTRICT data, UINT32 scanline)
117{
118 size_t y = 0;
119 const BYTE* src = NULL;
120 BYTE* yplane = NULL;
121 BYTE* coplane = NULL;
122 BYTE* cgplane = NULL;
123 BYTE* aplane = NULL;
124 INT16 r_val = 0;
125 INT16 g_val = 0;
126 INT16 b_val = 0;
127 BYTE a_val = 0;
128
129 UINT16 tempWidth = ROUND_UP_TO(context->width, 8);
130 const UINT16 rw = (context->ChromaSubsamplingLevel ? tempWidth : context->width);
131 const BYTE ccl = WINPR_ASSERTING_INT_CAST(BYTE, context->ColorLossLevel);
132
133 for (; y < context->height; y++)
134 {
135 src = data + (context->height - 1 - y) * scanline;
136 yplane = context->priv->PlaneBuffers[0] + y * rw;
137 coplane = context->priv->PlaneBuffers[1] + y * rw;
138 cgplane = context->priv->PlaneBuffers[2] + y * rw;
139 aplane = context->priv->PlaneBuffers[3] + y * context->width;
140
141 UINT16 x = 0;
142 for (; x < context->width; x++)
143 {
144 switch (context->format)
145 {
146 case PIXEL_FORMAT_BGRX32:
147 b_val = *src++;
148 g_val = *src++;
149 r_val = *src++;
150 src++;
151 a_val = 0xFF;
152 break;
153
154 case PIXEL_FORMAT_BGRA32:
155 b_val = *src++;
156 g_val = *src++;
157 r_val = *src++;
158 a_val = *src++;
159 break;
160
161 case PIXEL_FORMAT_RGBX32:
162 r_val = *src++;
163 g_val = *src++;
164 b_val = *src++;
165 src++;
166 a_val = 0xFF;
167 break;
168
169 case PIXEL_FORMAT_RGBA32:
170 r_val = *src++;
171 g_val = *src++;
172 b_val = *src++;
173 a_val = *src++;
174 break;
175
176 case PIXEL_FORMAT_BGR24:
177 b_val = *src++;
178 g_val = *src++;
179 r_val = *src++;
180 a_val = 0xFF;
181 break;
182
183 case PIXEL_FORMAT_RGB24:
184 r_val = *src++;
185 g_val = *src++;
186 b_val = *src++;
187 a_val = 0xFF;
188 break;
189
190 case PIXEL_FORMAT_BGR16:
191 b_val = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
192 g_val = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
193 r_val = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
194 a_val = 0xFF;
195 src += 2;
196 break;
197
198 case PIXEL_FORMAT_RGB16:
199 r_val = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
200 g_val = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
201 b_val = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
202 a_val = 0xFF;
203 src += 2;
204 break;
205
206 case PIXEL_FORMAT_A4:
207 {
208 int shift = 0;
209 BYTE idx = 0;
210 shift = (7 - (x % 8));
211 idx = ((*src) >> shift) & 1;
212 idx |= (((*(src + 1)) >> shift) & 1) << 1;
213 idx |= (((*(src + 2)) >> shift) & 1) << 2;
214 idx |= (((*(src + 3)) >> shift) & 1) << 3;
215 idx *= 3;
216 r_val = (INT16)context->palette[idx];
217 g_val = (INT16)context->palette[idx + 1];
218 b_val = (INT16)context->palette[idx + 2];
219
220 if (shift == 0)
221 src += 4;
222 }
223
224 a_val = 0xFF;
225 break;
226
227 case PIXEL_FORMAT_RGB8:
228 {
229 int idx = (*src) * 3;
230 r_val = (INT16)context->palette[idx];
231 g_val = (INT16)context->palette[idx + 1];
232 b_val = (INT16)context->palette[idx + 2];
233 src++;
234 }
235
236 a_val = 0xFF;
237 break;
238
239 default:
240 r_val = g_val = b_val = a_val = 0;
241 break;
242 }
243
244 *yplane++ = (BYTE)((r_val >> 2) + (g_val >> 1) + (b_val >> 2));
245 /* Perform color loss reduction here */
246 *coplane++ = (BYTE)((r_val - b_val) >> ccl);
247 *cgplane++ = (BYTE)((-(r_val >> 1) + g_val - (b_val >> 1)) >> ccl);
248 *aplane++ = a_val;
249 }
250
251 if (context->ChromaSubsamplingLevel && (x % 2) == 1)
252 {
253 *yplane = *(yplane - 1);
254 *coplane = *(coplane - 1);
255 *cgplane = *(cgplane - 1);
256 }
257 }
258
259 if (context->ChromaSubsamplingLevel && (y % 2) == 1)
260 {
261 yplane = context->priv->PlaneBuffers[0] + y * rw;
262 coplane = context->priv->PlaneBuffers[1] + y * rw;
263 cgplane = context->priv->PlaneBuffers[2] + y * rw;
264 CopyMemory(yplane, yplane - rw, rw);
265 CopyMemory(coplane, coplane - rw, rw);
266 CopyMemory(cgplane, cgplane - rw, rw);
267 }
268
269 return TRUE;
270}
271
272static BOOL nsc_encode_subsampling(NSC_CONTEXT* WINPR_RESTRICT context)
273{
274 UINT32 tempWidth = 0;
275 UINT32 tempHeight = 0;
276
277 if (!context)
278 return FALSE;
279
280 tempWidth = ROUND_UP_TO(context->width, 8);
281 tempHeight = ROUND_UP_TO(context->height, 2);
282
283 if (tempHeight == 0)
284 return FALSE;
285
286 if (tempWidth > context->priv->PlaneBuffersLength / tempHeight)
287 return FALSE;
288
289 for (size_t y = 0; y < tempHeight >> 1; y++)
290 {
291 BYTE* co_dst = context->priv->PlaneBuffers[1] + y * (tempWidth >> 1);
292 BYTE* cg_dst = context->priv->PlaneBuffers[2] + y * (tempWidth >> 1);
293 const INT8* co_src0 = (INT8*)context->priv->PlaneBuffers[1] + (y << 1) * tempWidth;
294 const INT8* co_src1 = co_src0 + tempWidth;
295 const INT8* cg_src0 = (INT8*)context->priv->PlaneBuffers[2] + (y << 1) * tempWidth;
296 const INT8* cg_src1 = cg_src0 + tempWidth;
297
298 for (UINT32 x = 0; x < tempWidth >> 1; x++)
299 {
300 *co_dst++ = (BYTE)(((INT16)*co_src0 + (INT16) * (co_src0 + 1) + (INT16)*co_src1 +
301 (INT16) * (co_src1 + 1)) >>
302 2);
303 *cg_dst++ = (BYTE)(((INT16)*cg_src0 + (INT16) * (cg_src0 + 1) + (INT16)*cg_src1 +
304 (INT16) * (cg_src1 + 1)) >>
305 2);
306 co_src0 += 2;
307 co_src1 += 2;
308 cg_src0 += 2;
309 cg_src1 += 2;
310 }
311 }
312
313 return TRUE;
314}
315
316BOOL nsc_encode(NSC_CONTEXT* WINPR_RESTRICT context, const BYTE* WINPR_RESTRICT bmpdata,
317 UINT32 rowstride)
318{
319 if (!context || !bmpdata || (rowstride == 0))
320 return FALSE;
321
322 if (!nsc_encode_argb_to_aycocg(context, bmpdata, rowstride))
323 return FALSE;
324
325 if (context->ChromaSubsamplingLevel)
326 {
327 if (!nsc_encode_subsampling(context))
328 return FALSE;
329 }
330
331 return TRUE;
332}
333
334static UINT32 nsc_rle_encode(const BYTE* WINPR_RESTRICT in, BYTE* WINPR_RESTRICT out,
335 UINT32 originalSize)
336{
337 UINT32 left = 0;
338 UINT32 runlength = 1;
339 UINT32 planeSize = 0;
340 left = originalSize;
341
346 while (left > 4 && planeSize < originalSize - 4)
347 {
348 if (left > 5 && *in == *(in + 1))
349 {
350 runlength++;
351 }
352 else if (runlength == 1)
353 {
354 *out++ = *in;
355 planeSize++;
356 }
357 else if (runlength < 256)
358 {
359 *out++ = *in;
360 *out++ = *in;
361 WINPR_ASSERT(runlength >= 2);
362 *out++ = WINPR_ASSERTING_INT_CAST(BYTE, runlength - 2);
363 runlength = 1;
364 planeSize += 3;
365 }
366 else
367 {
368 *out++ = *in;
369 *out++ = *in;
370 *out++ = 0xFF;
371 *out++ = (runlength & 0x000000FF);
372 *out++ = (runlength & 0x0000FF00) >> 8;
373 *out++ = (runlength & 0x00FF0000) >> 16;
374 *out++ = (runlength & 0xFF000000) >> 24;
375 runlength = 1;
376 planeSize += 7;
377 }
378
379 in++;
380 left--;
381 }
382
383 if (planeSize < originalSize - 4)
384 CopyMemory(out, in, 4);
385
386 planeSize += 4;
387 return planeSize;
388}
389
390static void nsc_rle_compress_data(NSC_CONTEXT* WINPR_RESTRICT context)
391{
392 UINT32 planeSize = 0;
393 UINT32 originalSize = 0;
394
395 for (UINT16 i = 0; i < 4; i++)
396 {
397 originalSize = context->OrgByteCount[i];
398
399 if (originalSize == 0)
400 {
401 planeSize = 0;
402 }
403 else
404 {
405 planeSize = nsc_rle_encode(context->priv->PlaneBuffers[i],
406 context->priv->PlaneBuffers[4], originalSize);
407
408 if (planeSize < originalSize)
409 CopyMemory(context->priv->PlaneBuffers[i], context->priv->PlaneBuffers[4],
410 planeSize);
411 else
412 planeSize = originalSize;
413 }
414
415 context->PlaneByteCount[i] = planeSize;
416 }
417}
418
419BOOL nsc_write_message(WINPR_ATTR_UNUSED NSC_CONTEXT* WINPR_RESTRICT context,
420 wStream* WINPR_RESTRICT s, const NSC_MESSAGE* WINPR_RESTRICT message)
421{
422 UINT32 totalPlaneByteCount = 0;
423 totalPlaneByteCount = message->LumaPlaneByteCount + message->OrangeChromaPlaneByteCount +
424 message->GreenChromaPlaneByteCount + message->AlphaPlaneByteCount;
425
426 if (!Stream_EnsureRemainingCapacity(s, 20 + totalPlaneByteCount))
427 return FALSE;
428
429 Stream_Write_UINT32(s, message->LumaPlaneByteCount); /* LumaPlaneByteCount (4 bytes) */
430 Stream_Write_UINT32(
431 s, message->OrangeChromaPlaneByteCount); /* OrangeChromaPlaneByteCount (4 bytes) */
432 Stream_Write_UINT32(
433 s, message->GreenChromaPlaneByteCount); /* GreenChromaPlaneByteCount (4 bytes) */
434 Stream_Write_UINT32(s, message->AlphaPlaneByteCount); /* AlphaPlaneByteCount (4 bytes) */
435 Stream_Write_UINT8(s, message->ColorLossLevel); /* ColorLossLevel (1 byte) */
436 Stream_Write_UINT8(s, message->ChromaSubsamplingLevel); /* ChromaSubsamplingLevel (1 byte) */
437 Stream_Write_UINT16(s, 0); /* Reserved (2 bytes) */
438
439 if (message->LumaPlaneByteCount)
440 Stream_Write(s, message->PlaneBuffers[0], message->LumaPlaneByteCount); /* LumaPlane */
441
442 if (message->OrangeChromaPlaneByteCount)
443 Stream_Write(s, message->PlaneBuffers[1],
444 message->OrangeChromaPlaneByteCount); /* OrangeChromaPlane */
445
446 if (message->GreenChromaPlaneByteCount)
447 Stream_Write(s, message->PlaneBuffers[2],
448 message->GreenChromaPlaneByteCount); /* GreenChromaPlane */
449
450 if (message->AlphaPlaneByteCount)
451 Stream_Write(s, message->PlaneBuffers[3], message->AlphaPlaneByteCount); /* AlphaPlane */
452
453 return TRUE;
454}
455
456BOOL nsc_compose_message(NSC_CONTEXT* WINPR_RESTRICT context, wStream* WINPR_RESTRICT s,
457 const BYTE* WINPR_RESTRICT data, UINT32 width, UINT32 height,
458 UINT32 scanline)
459{
460 BOOL rc = 0;
461 NSC_MESSAGE message = { 0 };
462
463 if (!context || !s || !data)
464 return FALSE;
465
466 context->width = WINPR_ASSERTING_INT_CAST(UINT16, width);
467 context->height = WINPR_ASSERTING_INT_CAST(UINT16, height);
468
469 if (!nsc_context_initialize_encode(context))
470 return FALSE;
471
472 /* ARGB to AYCoCg conversion, chroma subsampling and colorloss reduction */
473 PROFILER_ENTER(context->priv->prof_nsc_encode)
474 rc = context->encode(context, data, scanline);
475 PROFILER_EXIT(context->priv->prof_nsc_encode)
476 if (!rc)
477 return FALSE;
478
479 /* RLE encode */
480 PROFILER_ENTER(context->priv->prof_nsc_rle_compress_data)
481 nsc_rle_compress_data(context);
482 PROFILER_EXIT(context->priv->prof_nsc_rle_compress_data)
483 message.PlaneBuffers[0] = context->priv->PlaneBuffers[0];
484 message.PlaneBuffers[1] = context->priv->PlaneBuffers[1];
485 message.PlaneBuffers[2] = context->priv->PlaneBuffers[2];
486 message.PlaneBuffers[3] = context->priv->PlaneBuffers[3];
487 message.LumaPlaneByteCount = context->PlaneByteCount[0];
488 message.OrangeChromaPlaneByteCount = context->PlaneByteCount[1];
489 message.GreenChromaPlaneByteCount = context->PlaneByteCount[2];
490 message.AlphaPlaneByteCount = context->PlaneByteCount[3];
491
492 message.ColorLossLevel = WINPR_ASSERTING_INT_CAST(BYTE, context->ColorLossLevel);
493 message.ChromaSubsamplingLevel =
494 WINPR_ASSERTING_INT_CAST(BYTE, context->ChromaSubsamplingLevel);
495 return nsc_write_message(context, s, &message);
496}
497
498BOOL nsc_decompose_message(NSC_CONTEXT* WINPR_RESTRICT context, wStream* WINPR_RESTRICT s,
499 BYTE* WINPR_RESTRICT bmpdata, UINT32 x, UINT32 y, UINT32 width,
500 UINT32 height, UINT32 rowstride, UINT32 format, UINT32 flip)
501{
502 size_t size = Stream_GetRemainingLength(s);
503
504 if (size > UINT32_MAX)
505 return FALSE;
506
507 if (!nsc_process_message(context, (UINT16)FreeRDPGetBitsPerPixel(context->format), width,
508 height, Stream_Pointer(s), (UINT32)size, bmpdata, format, rowstride, x,
509 y, width, height, flip))
510 return FALSE;
511 Stream_Seek(s, size);
512 return TRUE;
513}