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