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