1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * NSCodec Encoder
4 *
5 * Copyright 2012 Vic Lee
6 * Copyright 2016 Armin Novak <armin.novak@thincast.com>
7 * Copyright 2016 Thincast Technologies GmbH
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
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
38 struct _NSC_MESSAGE
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 };
58 typedef struct _NSC_MESSAGE NSC_MESSAGE;
59
60 static BOOL nsc_write_message(NSC_CONTEXT* context, wStream* s, const NSC_MESSAGE* message);
61
nsc_context_initialize_encode(NSC_CONTEXT * context)62 static BOOL nsc_context_initialize_encode(NSC_CONTEXT* context)
63 {
64 int i;
65 UINT32 length;
66 UINT32 tempWidth;
67 UINT32 tempHeight;
68 tempWidth = ROUND_UP_TO(context->width, 8);
69 tempHeight = ROUND_UP_TO(context->height, 2);
70 /* The maximum length a decoded plane can reach in all cases */
71 length = tempWidth * tempHeight + 16;
72
73 if (length > context->priv->PlaneBuffersLength)
74 {
75 for (i = 0; i < 5; i++)
76 {
77 BYTE* tmp = (BYTE*)realloc(context->priv->PlaneBuffers[i], length);
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;
104 fail:
105
106 if (length > context->priv->PlaneBuffersLength)
107 {
108 for (i = 0; i < 5; i++)
109 free(context->priv->PlaneBuffers[i]);
110 }
111
112 return FALSE;
113 }
114
nsc_encode_argb_to_aycocg(NSC_CONTEXT * context,const BYTE * data,UINT32 scanline)115 static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* context, const BYTE* data, UINT32 scanline)
116 {
117 UINT16 x;
118 UINT16 y;
119 UINT16 rw;
120 BYTE ccl;
121 const BYTE* src;
122 BYTE* yplane = NULL;
123 BYTE* coplane = NULL;
124 BYTE* cgplane = NULL;
125 BYTE* aplane = NULL;
126 INT16 r_val;
127 INT16 g_val;
128 INT16 b_val;
129 BYTE a_val;
130 UINT32 tempWidth;
131
132 tempWidth = ROUND_UP_TO(context->width, 8);
133 rw = (context->ChromaSubsamplingLevel ? tempWidth : context->width);
134 ccl = context->ColorLossLevel;
135
136 for (y = 0; y < context->height; y++)
137 {
138 src = data + (context->height - 1 - y) * scanline;
139 yplane = context->priv->PlaneBuffers[0] + y * rw;
140 coplane = context->priv->PlaneBuffers[1] + y * rw;
141 cgplane = context->priv->PlaneBuffers[2] + y * rw;
142 aplane = context->priv->PlaneBuffers[3] + y * context->width;
143
144 for (x = 0; x < context->width; x++)
145 {
146 switch (context->format)
147 {
148 case PIXEL_FORMAT_BGRX32:
149 b_val = *src++;
150 g_val = *src++;
151 r_val = *src++;
152 src++;
153 a_val = 0xFF;
154 break;
155
156 case PIXEL_FORMAT_BGRA32:
157 b_val = *src++;
158 g_val = *src++;
159 r_val = *src++;
160 a_val = *src++;
161 break;
162
163 case PIXEL_FORMAT_RGBX32:
164 r_val = *src++;
165 g_val = *src++;
166 b_val = *src++;
167 src++;
168 a_val = 0xFF;
169 break;
170
171 case PIXEL_FORMAT_RGBA32:
172 r_val = *src++;
173 g_val = *src++;
174 b_val = *src++;
175 a_val = *src++;
176 break;
177
178 case PIXEL_FORMAT_BGR24:
179 b_val = *src++;
180 g_val = *src++;
181 r_val = *src++;
182 a_val = 0xFF;
183 break;
184
185 case PIXEL_FORMAT_RGB24:
186 r_val = *src++;
187 g_val = *src++;
188 b_val = *src++;
189 a_val = 0xFF;
190 break;
191
192 case PIXEL_FORMAT_BGR16:
193 b_val = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
194 g_val = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
195 r_val = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
196 a_val = 0xFF;
197 src += 2;
198 break;
199
200 case PIXEL_FORMAT_RGB16:
201 r_val = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5));
202 g_val = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3));
203 b_val = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07));
204 a_val = 0xFF;
205 src += 2;
206 break;
207
208 case PIXEL_FORMAT_A4:
209 {
210 int shift;
211 BYTE idx;
212 shift = (7 - (x % 8));
213 idx = ((*src) >> shift) & 1;
214 idx |= (((*(src + 1)) >> shift) & 1) << 1;
215 idx |= (((*(src + 2)) >> shift) & 1) << 2;
216 idx |= (((*(src + 3)) >> shift) & 1) << 3;
217 idx *= 3;
218 r_val = (INT16)context->palette[idx];
219 g_val = (INT16)context->palette[idx + 1];
220 b_val = (INT16)context->palette[idx + 2];
221
222 if (shift == 0)
223 src += 4;
224 }
225
226 a_val = 0xFF;
227 break;
228
229 case PIXEL_FORMAT_RGB8:
230 {
231 int idx = (*src) * 3;
232 r_val = (INT16)context->palette[idx];
233 g_val = (INT16)context->palette[idx + 1];
234 b_val = (INT16)context->palette[idx + 2];
235 src++;
236 }
237
238 a_val = 0xFF;
239 break;
240
241 default:
242 r_val = g_val = b_val = a_val = 0;
243 break;
244 }
245
246 *yplane++ = (BYTE)((r_val >> 2) + (g_val >> 1) + (b_val >> 2));
247 /* Perform color loss reduction here */
248 *coplane++ = (BYTE)((r_val - b_val) >> ccl);
249 *cgplane++ = (BYTE)((-(r_val >> 1) + g_val - (b_val >> 1)) >> ccl);
250 *aplane++ = a_val;
251 }
252
253 if (context->ChromaSubsamplingLevel && (x % 2) == 1)
254 {
255 *yplane = *(yplane - 1);
256 *coplane = *(coplane - 1);
257 *cgplane = *(cgplane - 1);
258 }
259 }
260
261 if (context->ChromaSubsamplingLevel && (y % 2) == 1)
262 {
263 yplane = context->priv->PlaneBuffers[0] + y * rw;
264 coplane = context->priv->PlaneBuffers[1] + y * rw;
265 cgplane = context->priv->PlaneBuffers[2] + y * rw;
266 CopyMemory(yplane, yplane - rw, rw);
267 CopyMemory(coplane, coplane - rw, rw);
268 CopyMemory(cgplane, cgplane - rw, rw);
269 }
270
271 return TRUE;
272 }
273
nsc_encode_subsampling(NSC_CONTEXT * context)274 static BOOL nsc_encode_subsampling(NSC_CONTEXT* context)
275 {
276 UINT32 y;
277 UINT32 tempWidth;
278 UINT32 tempHeight;
279
280 if (!context)
281 return FALSE;
282
283 tempWidth = ROUND_UP_TO(context->width, 8);
284 tempHeight = ROUND_UP_TO(context->height, 2);
285
286 if (tempHeight == 0)
287 return FALSE;
288
289 if (tempWidth > context->priv->PlaneBuffersLength / tempHeight)
290 return FALSE;
291
292 for (y = 0; y<tempHeight>> 1; y++)
293 {
294 UINT32 x;
295 BYTE* co_dst = context->priv->PlaneBuffers[1] + y * (tempWidth >> 1);
296 BYTE* cg_dst = context->priv->PlaneBuffers[2] + y * (tempWidth >> 1);
297 const INT8* co_src0 = (INT8*)context->priv->PlaneBuffers[1] + (y << 1) * tempWidth;
298 const INT8* co_src1 = co_src0 + tempWidth;
299 const INT8* cg_src0 = (INT8*)context->priv->PlaneBuffers[2] + (y << 1) * tempWidth;
300 const INT8* cg_src1 = cg_src0 + tempWidth;
301
302 for (x = 0; x<tempWidth>> 1; x++)
303 {
304 *co_dst++ = (BYTE)(((INT16)*co_src0 + (INT16) * (co_src0 + 1) + (INT16)*co_src1 +
305 (INT16) * (co_src1 + 1)) >>
306 2);
307 *cg_dst++ = (BYTE)(((INT16)*cg_src0 + (INT16) * (cg_src0 + 1) + (INT16)*cg_src1 +
308 (INT16) * (cg_src1 + 1)) >>
309 2);
310 co_src0 += 2;
311 co_src1 += 2;
312 cg_src0 += 2;
313 cg_src1 += 2;
314 }
315 }
316
317 return TRUE;
318 }
319
nsc_encode(NSC_CONTEXT * context,const BYTE * bmpdata,UINT32 rowstride)320 BOOL nsc_encode(NSC_CONTEXT* context, const BYTE* bmpdata, UINT32 rowstride)
321 {
322 if (!context || !bmpdata || (rowstride == 0))
323 return FALSE;
324
325 if (!nsc_encode_argb_to_aycocg(context, bmpdata, rowstride))
326 return FALSE;
327
328 if (context->ChromaSubsamplingLevel)
329 {
330 if (!nsc_encode_subsampling(context))
331 return FALSE;
332 }
333
334 return TRUE;
335 }
336
nsc_rle_encode(const BYTE * in,BYTE * out,UINT32 originalSize)337 static UINT32 nsc_rle_encode(const BYTE* in, BYTE* out, UINT32 originalSize)
338 {
339 UINT32 left;
340 UINT32 runlength = 1;
341 UINT32 planeSize = 0;
342 left = originalSize;
343
344 /**
345 * We quit the loop if the running compressed size is larger than the original.
346 * In such cases data will be sent uncompressed.
347 */
348 while (left > 4 && planeSize < originalSize - 4)
349 {
350 if (left > 5 && *in == *(in + 1))
351 {
352 runlength++;
353 }
354 else if (runlength == 1)
355 {
356 *out++ = *in;
357 planeSize++;
358 }
359 else if (runlength < 256)
360 {
361 *out++ = *in;
362 *out++ = *in;
363 *out++ = runlength - 2;
364 runlength = 1;
365 planeSize += 3;
366 }
367 else
368 {
369 *out++ = *in;
370 *out++ = *in;
371 *out++ = 0xFF;
372 *out++ = (runlength & 0x000000FF);
373 *out++ = (runlength & 0x0000FF00) >> 8;
374 *out++ = (runlength & 0x00FF0000) >> 16;
375 *out++ = (runlength & 0xFF000000) >> 24;
376 runlength = 1;
377 planeSize += 7;
378 }
379
380 in++;
381 left--;
382 }
383
384 if (planeSize < originalSize - 4)
385 CopyMemory(out, in, 4);
386
387 planeSize += 4;
388 return planeSize;
389 }
390
nsc_rle_compress_data(NSC_CONTEXT * context)391 static void nsc_rle_compress_data(NSC_CONTEXT* context)
392 {
393 UINT16 i;
394 UINT32 planeSize;
395 UINT32 originalSize;
396
397 for (i = 0; i < 4; i++)
398 {
399 originalSize = context->OrgByteCount[i];
400
401 if (originalSize == 0)
402 {
403 planeSize = 0;
404 }
405 else
406 {
407 planeSize = nsc_rle_encode(context->priv->PlaneBuffers[i],
408 context->priv->PlaneBuffers[4], originalSize);
409
410 if (planeSize < originalSize)
411 CopyMemory(context->priv->PlaneBuffers[i], context->priv->PlaneBuffers[4],
412 planeSize);
413 else
414 planeSize = originalSize;
415 }
416
417 context->PlaneByteCount[i] = planeSize;
418 }
419 }
420
nsc_compute_byte_count(NSC_CONTEXT * context,UINT32 * ByteCount,UINT32 width,UINT32 height)421 static UINT32 nsc_compute_byte_count(NSC_CONTEXT* context, UINT32* ByteCount, UINT32 width,
422 UINT32 height)
423 {
424 UINT32 tempWidth;
425 UINT32 tempHeight;
426 UINT32 maxPlaneSize;
427 tempWidth = ROUND_UP_TO(width, 8);
428 tempHeight = ROUND_UP_TO(height, 2);
429 maxPlaneSize = tempWidth * tempHeight + 16;
430
431 if (context->ChromaSubsamplingLevel)
432 {
433 ByteCount[0] = tempWidth * height;
434 ByteCount[1] = tempWidth * tempHeight / 4;
435 ByteCount[2] = tempWidth * tempHeight / 4;
436 ByteCount[3] = width * height;
437 }
438 else
439 {
440 ByteCount[0] = width * height;
441 ByteCount[1] = width * height;
442 ByteCount[2] = width * height;
443 ByteCount[3] = width * height;
444 }
445
446 return maxPlaneSize;
447 }
448
nsc_write_message(NSC_CONTEXT * context,wStream * s,const NSC_MESSAGE * message)449 BOOL nsc_write_message(NSC_CONTEXT* context, wStream* s, const NSC_MESSAGE* message)
450 {
451 UINT32 totalPlaneByteCount;
452 totalPlaneByteCount = message->LumaPlaneByteCount + message->OrangeChromaPlaneByteCount +
453 message->GreenChromaPlaneByteCount + message->AlphaPlaneByteCount;
454
455 if (!Stream_EnsureRemainingCapacity(s, 20 + totalPlaneByteCount))
456 return FALSE;
457
458 Stream_Write_UINT32(s, message->LumaPlaneByteCount); /* LumaPlaneByteCount (4 bytes) */
459 Stream_Write_UINT32(
460 s, message->OrangeChromaPlaneByteCount); /* OrangeChromaPlaneByteCount (4 bytes) */
461 Stream_Write_UINT32(
462 s, message->GreenChromaPlaneByteCount); /* GreenChromaPlaneByteCount (4 bytes) */
463 Stream_Write_UINT32(s, message->AlphaPlaneByteCount); /* AlphaPlaneByteCount (4 bytes) */
464 Stream_Write_UINT8(s, message->ColorLossLevel); /* ColorLossLevel (1 byte) */
465 Stream_Write_UINT8(s, message->ChromaSubsamplingLevel); /* ChromaSubsamplingLevel (1 byte) */
466 Stream_Write_UINT16(s, 0); /* Reserved (2 bytes) */
467
468 if (message->LumaPlaneByteCount)
469 Stream_Write(s, message->PlaneBuffers[0], message->LumaPlaneByteCount); /* LumaPlane */
470
471 if (message->OrangeChromaPlaneByteCount)
472 Stream_Write(s, message->PlaneBuffers[1],
473 message->OrangeChromaPlaneByteCount); /* OrangeChromaPlane */
474
475 if (message->GreenChromaPlaneByteCount)
476 Stream_Write(s, message->PlaneBuffers[2],
477 message->GreenChromaPlaneByteCount); /* GreenChromaPlane */
478
479 if (message->AlphaPlaneByteCount)
480 Stream_Write(s, message->PlaneBuffers[3], message->AlphaPlaneByteCount); /* AlphaPlane */
481
482 return TRUE;
483 }
484
nsc_compose_message(NSC_CONTEXT * context,wStream * s,const BYTE * data,UINT32 width,UINT32 height,UINT32 scanline)485 BOOL nsc_compose_message(NSC_CONTEXT* context, wStream* s, const BYTE* data, UINT32 width,
486 UINT32 height, UINT32 scanline)
487 {
488 BOOL rc;
489 NSC_MESSAGE message = { 0 };
490
491 if (!context || !s || !data)
492 return FALSE;
493
494 context->width = width;
495 context->height = height;
496
497 if (!nsc_context_initialize_encode(context))
498 return FALSE;
499
500 /* ARGB to AYCoCg conversion, chroma subsampling and colorloss reduction */
501 PROFILER_ENTER(context->priv->prof_nsc_encode)
502 rc = context->encode(context, data, scanline);
503 PROFILER_EXIT(context->priv->prof_nsc_encode)
504 if (!rc)
505 return FALSE;
506
507 /* RLE encode */
508 PROFILER_ENTER(context->priv->prof_nsc_rle_compress_data)
509 nsc_rle_compress_data(context);
510 PROFILER_EXIT(context->priv->prof_nsc_rle_compress_data)
511 message.PlaneBuffers[0] = context->priv->PlaneBuffers[0];
512 message.PlaneBuffers[1] = context->priv->PlaneBuffers[1];
513 message.PlaneBuffers[2] = context->priv->PlaneBuffers[2];
514 message.PlaneBuffers[3] = context->priv->PlaneBuffers[3];
515 message.LumaPlaneByteCount = context->PlaneByteCount[0];
516 message.OrangeChromaPlaneByteCount = context->PlaneByteCount[1];
517 message.GreenChromaPlaneByteCount = context->PlaneByteCount[2];
518 message.AlphaPlaneByteCount = context->PlaneByteCount[3];
519 message.ColorLossLevel = context->ColorLossLevel;
520 message.ChromaSubsamplingLevel = context->ChromaSubsamplingLevel;
521 return nsc_write_message(context, s, &message);
522 }
523
nsc_decompose_message(NSC_CONTEXT * context,wStream * s,BYTE * bmpdata,UINT32 x,UINT32 y,UINT32 width,UINT32 height,UINT32 rowstride,UINT32 format,UINT32 flip)524 BOOL nsc_decompose_message(NSC_CONTEXT* context, wStream* s, BYTE* bmpdata, UINT32 x, UINT32 y,
525 UINT32 width, UINT32 height, UINT32 rowstride, UINT32 format,
526 UINT32 flip)
527 {
528 size_t size = Stream_GetRemainingLength(s);
529 if (size > UINT32_MAX)
530 return FALSE;
531
532 if (!nsc_process_message(context, (UINT16)GetBitsPerPixel(context->format), width, height,
533 Stream_Pointer(s), (UINT32)size, bmpdata, format, rowstride, x, y,
534 width, height, flip))
535 return FALSE;
536 Stream_Seek(s, size);
537 return TRUE;
538 }
539