1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Color Conversion Routines
4  *
5  * Copyright 2010 Marc-Andre Moreau <marcandre.moreau@gmail.com>
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 <string.h>
28 #include <stdlib.h>
29 
30 #include <winpr/crt.h>
31 
32 #include <freerdp/log.h>
33 #include <freerdp/freerdp.h>
34 #include <freerdp/primitives.h>
35 
36 #if defined(CAIRO_FOUND)
37 #include <cairo.h>
38 #endif
39 
40 #if defined(SWSCALE_FOUND)
41 #include <libswscale/swscale.h>
42 #endif
43 
44 #define TAG FREERDP_TAG("color")
45 
freerdp_glyph_convert(UINT32 width,UINT32 height,const BYTE * data)46 BYTE* freerdp_glyph_convert(UINT32 width, UINT32 height, const BYTE* data)
47 {
48 	UINT32 x, y;
49 	const BYTE* srcp;
50 	BYTE* dstp;
51 	BYTE* dstData;
52 	UINT32 scanline;
53 	/*
54 	 * converts a 1-bit-per-pixel glyph to a one-byte-per-pixel glyph:
55 	 * this approach uses a little more memory, but provides faster
56 	 * means of accessing individual pixels in blitting operations
57 	 */
58 	scanline = (width + 7) / 8;
59 	dstData = (BYTE*)_aligned_malloc(width * height * 1ULL, 16);
60 
61 	if (!dstData)
62 		return NULL;
63 
64 	ZeroMemory(dstData, width * height);
65 	dstp = dstData;
66 
67 	for (y = 0; y < height; y++)
68 	{
69 		srcp = data + (y * scanline);
70 
71 		for (x = 0; x < width; x++)
72 		{
73 			if ((*srcp & (0x80 >> (x % 8))) != 0)
74 				*dstp = 0xFF;
75 
76 			dstp++;
77 
78 			if (((x + 1) % 8 == 0) && x != 0)
79 				srcp++;
80 		}
81 	}
82 
83 	return dstData;
84 }
85 
freerdp_image_copy_from_monochrome(BYTE * pDstData,UINT32 DstFormat,UINT32 nDstStep,UINT32 nXDst,UINT32 nYDst,UINT32 nWidth,UINT32 nHeight,const BYTE * pSrcData,UINT32 backColor,UINT32 foreColor,const gdiPalette * palette)86 BOOL freerdp_image_copy_from_monochrome(BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep,
87                                         UINT32 nXDst, UINT32 nYDst, UINT32 nWidth, UINT32 nHeight,
88                                         const BYTE* pSrcData, UINT32 backColor, UINT32 foreColor,
89                                         const gdiPalette* palette)
90 {
91 	UINT32 x, y;
92 	BOOL vFlip;
93 	UINT32 monoStep;
94 	const UINT32 dstBytesPerPixel = GetBytesPerPixel(DstFormat);
95 
96 	if (!pDstData || !pSrcData || !palette)
97 		return FALSE;
98 
99 	if (nDstStep == 0)
100 		nDstStep = dstBytesPerPixel * nWidth;
101 
102 	vFlip = FALSE;
103 	monoStep = (nWidth + 7) / 8;
104 
105 	for (y = 0; y < nHeight; y++)
106 	{
107 		const BYTE* monoBits;
108 		BYTE* pDstLine = &pDstData[((nYDst + y) * nDstStep)];
109 		UINT32 monoBit = 0x80;
110 
111 		if (!vFlip)
112 			monoBits = &pSrcData[monoStep * y];
113 		else
114 			monoBits = &pSrcData[monoStep * (nHeight - y - 1)];
115 
116 		for (x = 0; x < nWidth; x++)
117 		{
118 			BYTE* pDstPixel = &pDstLine[((nXDst + x) * GetBytesPerPixel(DstFormat))];
119 			BOOL monoPixel = (*monoBits & monoBit) ? TRUE : FALSE;
120 
121 			if (!(monoBit >>= 1))
122 			{
123 				monoBits++;
124 				monoBit = 0x80;
125 			}
126 
127 			if (monoPixel)
128 				WriteColor(pDstPixel, DstFormat, backColor);
129 			else
130 				WriteColor(pDstPixel, DstFormat, foreColor);
131 		}
132 	}
133 
134 	return TRUE;
135 }
136 
freerdp_image_inverted_pointer_color(UINT32 x,UINT32 y,UINT32 format)137 static INLINE UINT32 freerdp_image_inverted_pointer_color(UINT32 x, UINT32 y, UINT32 format)
138 {
139 #if 1
140 	/**
141 	 * Inverted pointer colors (where individual pixels can change their
142 	 * color to accommodate the background behind them) only seem to be
143 	 * supported on Windows.
144 	 * Using a static replacement color for these pixels (e.g. black)
145 	 * might result in invisible pointers depending on the background.
146 	 * This function returns either black or white, depending on the
147 	 * pixel's position.
148 	 */
149 	BYTE fill = (x + y) & 1 ? 0x00 : 0xFF;
150 #else
151 	BYTE fill = 0x00;
152 #endif
153 	return FreeRDPGetColor(format, fill, fill, fill, 0xFF);
154 }
155 
156 /*
157  * DIB color palettes are arrays of RGBQUAD structs with colors in BGRX format.
158  * They are used only by 1, 2, 4, and 8-bit bitmaps.
159  */
fill_gdi_palette_for_icon(const BYTE * colorTable,UINT16 cbColorTable,gdiPalette * palette)160 static void fill_gdi_palette_for_icon(const BYTE* colorTable, UINT16 cbColorTable,
161                                       gdiPalette* palette)
162 {
163 	UINT16 i;
164 	palette->format = PIXEL_FORMAT_BGRX32;
165 	ZeroMemory(palette->palette, sizeof(palette->palette));
166 
167 	if (!cbColorTable)
168 		return;
169 
170 	if ((cbColorTable % 4 != 0) || (cbColorTable / 4 > 256))
171 	{
172 		WLog_WARN(TAG, "weird palette size: %u", cbColorTable);
173 		return;
174 	}
175 
176 	for (i = 0; i < cbColorTable / 4; i++)
177 	{
178 		palette->palette[i] = ReadColor(&colorTable[4 * i], palette->format);
179 	}
180 }
181 
div_ceil(UINT32 a,UINT32 b)182 static INLINE UINT32 div_ceil(UINT32 a, UINT32 b)
183 {
184 	return (a + (b - 1)) / b;
185 }
186 
round_up(UINT32 a,UINT32 b)187 static INLINE UINT32 round_up(UINT32 a, UINT32 b)
188 {
189 	return b * div_ceil(a, b);
190 }
191 
freerdp_image_copy_from_icon_data(BYTE * pDstData,UINT32 DstFormat,UINT32 nDstStep,UINT32 nXDst,UINT32 nYDst,UINT16 nWidth,UINT16 nHeight,const BYTE * bitsColor,UINT16 cbBitsColor,const BYTE * bitsMask,UINT16 cbBitsMask,const BYTE * colorTable,UINT16 cbColorTable,UINT32 bpp)192 BOOL freerdp_image_copy_from_icon_data(BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep,
193                                        UINT32 nXDst, UINT32 nYDst, UINT16 nWidth, UINT16 nHeight,
194                                        const BYTE* bitsColor, UINT16 cbBitsColor,
195                                        const BYTE* bitsMask, UINT16 cbBitsMask,
196                                        const BYTE* colorTable, UINT16 cbColorTable, UINT32 bpp)
197 {
198 	DWORD format;
199 	gdiPalette palette;
200 
201 	if (!pDstData || !bitsColor)
202 		return FALSE;
203 
204 	/*
205 	 * Color formats used by icons are DIB bitmap formats (2-bit format
206 	 * is not used by MS-RDPERP). Note that 16-bit is RGB555, not RGB565,
207 	 * and that 32-bit format uses BGRA order.
208 	 */
209 	switch (bpp)
210 	{
211 		case 1:
212 		case 4:
213 			/*
214 			 * These formats are not supported by freerdp_image_copy().
215 			 * PIXEL_FORMAT_MONO and PIXEL_FORMAT_A4 are *not* correct
216 			 * color formats for this. Please fix freerdp_image_copy()
217 			 * if you came here to fix a broken icon of some weird app
218 			 * that still uses 1 or 4bpp format in the 21st century.
219 			 */
220 			WLog_WARN(TAG, "1bpp and 4bpp icons are not supported");
221 			return FALSE;
222 
223 		case 8:
224 			format = PIXEL_FORMAT_RGB8;
225 			break;
226 
227 		case 16:
228 			format = PIXEL_FORMAT_RGB15;
229 			break;
230 
231 		case 24:
232 			format = PIXEL_FORMAT_RGB24;
233 			break;
234 
235 		case 32:
236 			format = PIXEL_FORMAT_BGRA32;
237 			break;
238 
239 		default:
240 			WLog_WARN(TAG, "invalid icon bpp: %d", bpp);
241 			return FALSE;
242 	}
243 
244 	/* Ensure we have enough source data bytes for image copy. */
245 	if (cbBitsColor < nWidth * nHeight * GetBytesPerPixel(format))
246 		return FALSE;
247 
248 	fill_gdi_palette_for_icon(colorTable, cbColorTable, &palette);
249 	if (!freerdp_image_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, bitsColor,
250 	                        format, 0, 0, 0, &palette, FREERDP_FLIP_VERTICAL))
251 		return FALSE;
252 
253 	/* apply alpha mask */
254 	if (ColorHasAlpha(DstFormat) && cbBitsMask)
255 	{
256 		BYTE nextBit;
257 		const BYTE* maskByte;
258 		UINT32 x, y;
259 		UINT32 stride;
260 		BYTE r, g, b;
261 		BYTE* dstBuf = pDstData;
262 		UINT32 dstBpp = GetBytesPerPixel(DstFormat);
263 
264 		/*
265 		 * Each byte encodes 8 adjacent pixels (with LSB padding as needed).
266 		 * And due to hysterical raisins, stride of DIB bitmaps must be
267 		 * a multiple of 4 bytes.
268 		 */
269 		stride = round_up(div_ceil(nWidth, 8), 4);
270 
271 		for (y = 0; y < nHeight; y++)
272 		{
273 			maskByte = &bitsMask[stride * (nHeight - 1 - y)];
274 			nextBit = 0x80;
275 
276 			for (x = 0; x < nWidth; x++)
277 			{
278 				UINT32 color;
279 				BYTE alpha = (*maskByte & nextBit) ? 0x00 : 0xFF;
280 
281 				/* read color back, add alpha and write it back */
282 				color = ReadColor(dstBuf, DstFormat);
283 				SplitColor(color, DstFormat, &r, &g, &b, NULL, &palette);
284 				color = FreeRDPGetColor(DstFormat, r, g, b, alpha);
285 				WriteColor(dstBuf, DstFormat, color);
286 
287 				nextBit >>= 1;
288 				dstBuf += dstBpp;
289 				if (!nextBit)
290 				{
291 					nextBit = 0x80;
292 					maskByte++;
293 				}
294 			}
295 		}
296 	}
297 
298 	return TRUE;
299 }
300 
freerdp_image_copy_from_pointer_data_1bpp(BYTE * pDstData,UINT32 DstFormat,UINT32 nDstStep,UINT32 nXDst,UINT32 nYDst,UINT32 nWidth,UINT32 nHeight,const BYTE * xorMask,UINT32 xorMaskLength,const BYTE * andMask,UINT32 andMaskLength,UINT32 xorBpp)301 static BOOL freerdp_image_copy_from_pointer_data_1bpp(BYTE* pDstData, UINT32 DstFormat,
302                                                       UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
303                                                       UINT32 nWidth, UINT32 nHeight,
304                                                       const BYTE* xorMask, UINT32 xorMaskLength,
305                                                       const BYTE* andMask, UINT32 andMaskLength,
306                                                       UINT32 xorBpp)
307 {
308 	UINT32 x, y;
309 	BOOL vFlip;
310 	UINT32 xorStep;
311 	UINT32 andStep;
312 	UINT32 xorBit;
313 	UINT32 andBit;
314 	UINT32 xorPixel;
315 	UINT32 andPixel;
316 	UINT32 dstBitsPerPixel;
317 	UINT32 dstBytesPerPixel;
318 	dstBitsPerPixel = GetBitsPerPixel(DstFormat);
319 	dstBytesPerPixel = GetBytesPerPixel(DstFormat);
320 
321 	vFlip = (xorBpp == 1) ? FALSE : TRUE;
322 	andStep = (nWidth + 7) / 8;
323 	andStep += (andStep % 2);
324 
325 	if (!xorMask || (xorMaskLength == 0))
326 		return FALSE;
327 	if (!andMask || (andMaskLength == 0))
328 		return FALSE;
329 
330 	xorStep = (nWidth + 7) / 8;
331 	xorStep += (xorStep % 2);
332 
333 	if (xorStep * nHeight > xorMaskLength)
334 		return FALSE;
335 
336 	if (andStep * nHeight > andMaskLength)
337 		return FALSE;
338 
339 	for (y = 0; y < nHeight; y++)
340 	{
341 		const BYTE* andBits;
342 		const BYTE* xorBits;
343 		BYTE* pDstPixel =
344 		    &pDstData[((nYDst + y) * nDstStep) + (nXDst * GetBytesPerPixel(DstFormat))];
345 		xorBit = andBit = 0x80;
346 
347 		if (!vFlip)
348 		{
349 			xorBits = &xorMask[xorStep * y];
350 			andBits = &andMask[andStep * y];
351 		}
352 		else
353 		{
354 			xorBits = &xorMask[xorStep * (nHeight - y - 1)];
355 			andBits = &andMask[andStep * (nHeight - y - 1)];
356 		}
357 
358 		for (x = 0; x < nWidth; x++)
359 		{
360 			UINT32 color = 0;
361 			xorPixel = (*xorBits & xorBit) ? 1 : 0;
362 
363 			if (!(xorBit >>= 1))
364 			{
365 				xorBits++;
366 				xorBit = 0x80;
367 			}
368 
369 			andPixel = (*andBits & andBit) ? 1 : 0;
370 
371 			if (!(andBit >>= 1))
372 			{
373 				andBits++;
374 				andBit = 0x80;
375 			}
376 
377 			if (!andPixel && !xorPixel)
378 				color = FreeRDPGetColor(DstFormat, 0, 0, 0, 0xFF); /* black */
379 			else if (!andPixel && xorPixel)
380 				color = FreeRDPGetColor(DstFormat, 0xFF, 0xFF, 0xFF, 0xFF); /* white */
381 			else if (andPixel && !xorPixel)
382 				color = FreeRDPGetColor(DstFormat, 0, 0, 0, 0); /* transparent */
383 			else if (andPixel && xorPixel)
384 				color = freerdp_image_inverted_pointer_color(x, y, DstFormat); /* inverted */
385 
386 			WriteColor(pDstPixel, DstFormat, color);
387 			pDstPixel += GetBytesPerPixel(DstFormat);
388 		}
389 	}
390 
391 	return TRUE;
392 }
393 
freerdp_image_copy_from_pointer_data_xbpp(BYTE * pDstData,UINT32 DstFormat,UINT32 nDstStep,UINT32 nXDst,UINT32 nYDst,UINT32 nWidth,UINT32 nHeight,const BYTE * xorMask,UINT32 xorMaskLength,const BYTE * andMask,UINT32 andMaskLength,UINT32 xorBpp,const gdiPalette * palette)394 static BOOL freerdp_image_copy_from_pointer_data_xbpp(BYTE* pDstData, UINT32 DstFormat,
395                                                       UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
396                                                       UINT32 nWidth, UINT32 nHeight,
397                                                       const BYTE* xorMask, UINT32 xorMaskLength,
398                                                       const BYTE* andMask, UINT32 andMaskLength,
399                                                       UINT32 xorBpp, const gdiPalette* palette)
400 {
401 	UINT32 x, y;
402 	BOOL vFlip;
403 	UINT32 xorStep;
404 	UINT32 andStep;
405 	UINT32 andBit;
406 	UINT32 xorPixel;
407 	UINT32 andPixel;
408 	UINT32 dstBitsPerPixel;
409 	UINT32 dstBytesPerPixel;
410 	UINT32 xorBytesPerPixel;
411 	dstBitsPerPixel = GetBitsPerPixel(DstFormat);
412 	dstBytesPerPixel = GetBytesPerPixel(DstFormat);
413 
414 	vFlip = (xorBpp == 1) ? FALSE : TRUE;
415 	andStep = (nWidth + 7) / 8;
416 	andStep += (andStep % 2);
417 
418 	if (!xorMask || (xorMaskLength == 0))
419 		return FALSE;
420 
421 	xorBytesPerPixel = xorBpp >> 3;
422 	xorStep = nWidth * xorBytesPerPixel;
423 	xorStep += (xorStep % 2);
424 
425 	if (xorBpp == 8 && !palette)
426 	{
427 		WLog_ERR(TAG, "null palette in conversion from %" PRIu32 " bpp to %" PRIu32 " bpp", xorBpp,
428 		         dstBitsPerPixel);
429 		return FALSE;
430 	}
431 
432 	if (xorStep * nHeight > xorMaskLength)
433 		return FALSE;
434 
435 	if (andMask)
436 	{
437 		if (andStep * nHeight > andMaskLength)
438 			return FALSE;
439 	}
440 
441 	for (y = 0; y < nHeight; y++)
442 	{
443 		const BYTE* xorBits;
444 		const BYTE* andBits = NULL;
445 		BYTE* pDstPixel =
446 		    &pDstData[((nYDst + y) * nDstStep) + (nXDst * GetBytesPerPixel(DstFormat))];
447 		andBit = 0x80;
448 
449 		if (!vFlip)
450 		{
451 			if (andMask)
452 				andBits = &andMask[andStep * y];
453 
454 			xorBits = &xorMask[xorStep * y];
455 		}
456 		else
457 		{
458 			if (andMask)
459 				andBits = &andMask[andStep * (nHeight - y - 1)];
460 
461 			xorBits = &xorMask[xorStep * (nHeight - y - 1)];
462 		}
463 
464 		for (x = 0; x < nWidth; x++)
465 		{
466 			UINT32 pixelFormat;
467 			UINT32 color;
468 
469 			if (xorBpp == 32)
470 			{
471 				pixelFormat = PIXEL_FORMAT_BGRA32;
472 				xorPixel = ReadColor(xorBits, pixelFormat);
473 			}
474 			else if (xorBpp == 16)
475 			{
476 				pixelFormat = PIXEL_FORMAT_RGB15;
477 				xorPixel = ReadColor(xorBits, pixelFormat);
478 			}
479 			else if (xorBpp == 8)
480 			{
481 				pixelFormat = palette->format;
482 				xorPixel = palette->palette[xorBits[0]];
483 			}
484 			else
485 			{
486 				pixelFormat = PIXEL_FORMAT_BGR24;
487 				xorPixel = ReadColor(xorBits, pixelFormat);
488 			}
489 
490 			xorPixel = FreeRDPConvertColor(xorPixel, pixelFormat, PIXEL_FORMAT_ARGB32, palette);
491 			xorBits += xorBytesPerPixel;
492 			andPixel = 0;
493 
494 			if (andMask)
495 			{
496 				andPixel = (*andBits & andBit) ? 1 : 0;
497 
498 				if (!(andBit >>= 1))
499 				{
500 					andBits++;
501 					andBit = 0x80;
502 				}
503 			}
504 
505 			if (andPixel)
506 			{
507 				if (xorPixel == 0xFF000000) /* black -> transparent */
508 					xorPixel = 0x00000000;
509 				else if (xorPixel == 0xFFFFFFFF) /* white -> inverted */
510 					xorPixel = freerdp_image_inverted_pointer_color(x, y, PIXEL_FORMAT_ARGB32);
511 			}
512 
513 			color = FreeRDPConvertColor(xorPixel, PIXEL_FORMAT_ARGB32, DstFormat, palette);
514 			WriteColor(pDstPixel, DstFormat, color);
515 			pDstPixel += GetBytesPerPixel(DstFormat);
516 		}
517 	}
518 
519 	return TRUE;
520 }
521 
522 /**
523  * Drawing Monochrome Pointers:
524  * http://msdn.microsoft.com/en-us/library/windows/hardware/ff556143/
525  *
526  * Drawing Color Pointers:
527  * http://msdn.microsoft.com/en-us/library/windows/hardware/ff556138/
528  */
529 
freerdp_image_copy_from_pointer_data(BYTE * pDstData,UINT32 DstFormat,UINT32 nDstStep,UINT32 nXDst,UINT32 nYDst,UINT32 nWidth,UINT32 nHeight,const BYTE * xorMask,UINT32 xorMaskLength,const BYTE * andMask,UINT32 andMaskLength,UINT32 xorBpp,const gdiPalette * palette)530 BOOL freerdp_image_copy_from_pointer_data(BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep,
531                                           UINT32 nXDst, UINT32 nYDst, UINT32 nWidth, UINT32 nHeight,
532                                           const BYTE* xorMask, UINT32 xorMaskLength,
533                                           const BYTE* andMask, UINT32 andMaskLength, UINT32 xorBpp,
534                                           const gdiPalette* palette)
535 {
536 	UINT32 y;
537 	UINT32 dstBitsPerPixel;
538 	UINT32 dstBytesPerPixel;
539 	dstBitsPerPixel = GetBitsPerPixel(DstFormat);
540 	dstBytesPerPixel = GetBytesPerPixel(DstFormat);
541 
542 	if (nDstStep <= 0)
543 		nDstStep = dstBytesPerPixel * nWidth;
544 
545 	for (y = nYDst; y < nHeight; y++)
546 	{
547 		BYTE* pDstLine = &pDstData[y * nDstStep + nXDst * dstBytesPerPixel];
548 		memset(pDstLine, 0, dstBytesPerPixel * (nWidth - nXDst) * 1ULL);
549 	}
550 
551 	switch (xorBpp)
552 	{
553 		case 1:
554 			return freerdp_image_copy_from_pointer_data_1bpp(
555 			    pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, xorMask,
556 			    xorMaskLength, andMask, andMaskLength, xorBpp);
557 
558 		case 8:
559 		case 16:
560 		case 24:
561 		case 32:
562 			return freerdp_image_copy_from_pointer_data_xbpp(
563 			    pDstData, DstFormat, nDstStep, nXDst, nYDst, nWidth, nHeight, xorMask,
564 			    xorMaskLength, andMask, andMaskLength, xorBpp, palette);
565 
566 		default:
567 			WLog_ERR(TAG, "failed to convert from %" PRIu32 " bpp to %" PRIu32 " bpp", xorBpp,
568 			         dstBitsPerPixel);
569 			return FALSE;
570 	}
571 }
572 
overlapping(const BYTE * pDstData,UINT32 nXDst,UINT32 nYDst,UINT32 nDstStep,UINT32 dstBytesPerPixel,const BYTE * pSrcData,UINT32 nXSrc,UINT32 nYSrc,UINT32 nSrcStep,UINT32 srcBytesPerPixel,UINT32 nWidth,UINT32 nHeight)573 static INLINE BOOL overlapping(const BYTE* pDstData, UINT32 nXDst, UINT32 nYDst, UINT32 nDstStep,
574                                UINT32 dstBytesPerPixel, const BYTE* pSrcData, UINT32 nXSrc,
575                                UINT32 nYSrc, UINT32 nSrcStep, UINT32 srcBytesPerPixel,
576                                UINT32 nWidth, UINT32 nHeight)
577 {
578 	const BYTE* pDstStart = &pDstData[nXDst * dstBytesPerPixel + nYDst * nDstStep];
579 	const BYTE* pDstEnd = pDstStart + nHeight * nDstStep;
580 	const BYTE* pSrcStart = &pSrcData[nXSrc * srcBytesPerPixel + nYSrc * nSrcStep];
581 	const BYTE* pSrcEnd = pSrcStart + nHeight * nSrcStep;
582 
583 	WINPR_UNUSED(nWidth);
584 
585 	if ((pDstStart >= pSrcStart) && (pDstStart <= pSrcEnd))
586 		return TRUE;
587 
588 	if ((pDstEnd >= pSrcStart) && (pDstEnd <= pSrcEnd))
589 		return TRUE;
590 
591 	return FALSE;
592 }
593 
freerdp_image_copy(BYTE * pDstData,DWORD DstFormat,UINT32 nDstStep,UINT32 nXDst,UINT32 nYDst,UINT32 nWidth,UINT32 nHeight,const BYTE * pSrcData,DWORD SrcFormat,UINT32 nSrcStep,UINT32 nXSrc,UINT32 nYSrc,const gdiPalette * palette,UINT32 flags)594 BOOL freerdp_image_copy(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst,
595                         UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, const BYTE* pSrcData,
596                         DWORD SrcFormat, UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc,
597                         const gdiPalette* palette, UINT32 flags)
598 {
599 	const UINT32 dstByte = GetBytesPerPixel(DstFormat);
600 	const UINT32 srcByte = GetBytesPerPixel(SrcFormat);
601 	const UINT32 copyDstWidth = nWidth * dstByte;
602 	const UINT32 xSrcOffset = nXSrc * srcByte;
603 	const UINT32 xDstOffset = nXDst * dstByte;
604 	const BOOL vSrcVFlip = flags & FREERDP_FLIP_VERTICAL;
605 	UINT32 srcVOffset = 0;
606 	INT32 srcVMultiplier = 1;
607 	UINT32 dstVOffset = 0;
608 	INT32 dstVMultiplier = 1;
609 
610 	if ((nHeight > INT32_MAX) || (nWidth > INT32_MAX))
611 		return FALSE;
612 
613 	if (!pDstData || !pSrcData)
614 		return FALSE;
615 
616 	if (nDstStep == 0)
617 		nDstStep = nWidth * GetBytesPerPixel(DstFormat);
618 
619 	if (nSrcStep == 0)
620 		nSrcStep = nWidth * GetBytesPerPixel(SrcFormat);
621 
622 	if (vSrcVFlip)
623 	{
624 		srcVOffset = (nHeight - 1) * nSrcStep;
625 		srcVMultiplier = -1;
626 	}
627 
628 	if (AreColorFormatsEqualNoAlpha(SrcFormat, DstFormat))
629 	{
630 		INT32 y;
631 
632 		if (overlapping(pDstData, nXDst, nYDst, nDstStep, dstByte, pSrcData, nXSrc, nYSrc, nSrcStep,
633 		                srcByte, nWidth, nHeight))
634 		{
635 			/* Copy down */
636 			if (nYDst < nYSrc)
637 			{
638 				for (y = 0; y < (INT32)nHeight; y++)
639 				{
640 					const BYTE* srcLine =
641 					    &pSrcData[(y + nYSrc) * nSrcStep * srcVMultiplier + srcVOffset];
642 					BYTE* dstLine = &pDstData[(y + nYDst) * nDstStep * dstVMultiplier + dstVOffset];
643 					memcpy(&dstLine[xDstOffset], &srcLine[xSrcOffset], copyDstWidth);
644 				}
645 			}
646 			/* Copy up */
647 			else if (nYDst > nYSrc)
648 			{
649 				for (y = nHeight - 1; y >= 0; y--)
650 				{
651 					const BYTE* srcLine =
652 					    &pSrcData[(y + nYSrc) * nSrcStep * srcVMultiplier + srcVOffset];
653 					BYTE* dstLine = &pDstData[(y + nYDst) * nDstStep * dstVMultiplier + dstVOffset];
654 					memcpy(&dstLine[xDstOffset], &srcLine[xSrcOffset], copyDstWidth);
655 				}
656 			}
657 			/* Copy left */
658 			else if (nXSrc > nXDst)
659 			{
660 				for (y = 0; y < (INT32)nHeight; y++)
661 				{
662 					const BYTE* srcLine =
663 					    &pSrcData[(y + nYSrc) * nSrcStep * srcVMultiplier + srcVOffset];
664 					BYTE* dstLine = &pDstData[(y + nYDst) * nDstStep * dstVMultiplier + dstVOffset];
665 					memmove(&dstLine[xDstOffset], &srcLine[xSrcOffset], copyDstWidth);
666 				}
667 			}
668 			/* Copy right */
669 			else if (nXSrc < nXDst)
670 			{
671 				for (y = (INT32)nHeight - 1; y >= 0; y--)
672 				{
673 					const BYTE* srcLine =
674 					    &pSrcData[(y + nYSrc) * nSrcStep * srcVMultiplier + srcVOffset];
675 					BYTE* dstLine = &pDstData[(y + nYDst) * nDstStep * dstVMultiplier + dstVOffset];
676 					memmove(&dstLine[xDstOffset], &srcLine[xSrcOffset], copyDstWidth);
677 				}
678 			}
679 			/* Source and destination are equal... */
680 			else
681 			{
682 			}
683 		}
684 		else
685 		{
686 			for (y = 0; y < (INT32)nHeight; y++)
687 			{
688 				const BYTE* srcLine =
689 				    &pSrcData[(y + nYSrc) * nSrcStep * srcVMultiplier + srcVOffset];
690 				BYTE* dstLine = &pDstData[(y + nYDst) * nDstStep * dstVMultiplier + dstVOffset];
691 				memcpy(&dstLine[xDstOffset], &srcLine[xSrcOffset], copyDstWidth);
692 			}
693 		}
694 	}
695 	else
696 	{
697 		UINT32 x, y;
698 
699 		for (y = 0; y < nHeight; y++)
700 		{
701 			const BYTE* srcLine = &pSrcData[(y + nYSrc) * nSrcStep * srcVMultiplier + srcVOffset];
702 			BYTE* dstLine = &pDstData[(y + nYDst) * nDstStep * dstVMultiplier + dstVOffset];
703 
704 			UINT32 color = ReadColor(&srcLine[nXSrc * srcByte], SrcFormat);
705 			UINT32 oldColor = color;
706 			UINT32 dstColor = FreeRDPConvertColor(color, SrcFormat, DstFormat, palette);
707 			WriteColor(&dstLine[nXDst * dstByte], DstFormat, dstColor);
708 			for (x = 1; x < nWidth; x++)
709 			{
710 				color = ReadColor(&srcLine[(x + nXSrc) * srcByte], SrcFormat);
711 				if (color == oldColor)
712 				{
713 					WriteColor(&dstLine[(x + nXDst) * dstByte], DstFormat, dstColor);
714 				}
715 				else
716 				{
717 					oldColor = color;
718 					dstColor = FreeRDPConvertColor(color, SrcFormat, DstFormat, palette);
719 					WriteColor(&dstLine[(x + nXDst) * dstByte], DstFormat, dstColor);
720 				}
721 			}
722 		}
723 	}
724 
725 	return TRUE;
726 }
727 
freerdp_image_fill(BYTE * pDstData,DWORD DstFormat,UINT32 nDstStep,UINT32 nXDst,UINT32 nYDst,UINT32 nWidth,UINT32 nHeight,UINT32 color)728 BOOL freerdp_image_fill(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst,
729                         UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, UINT32 color)
730 {
731 	UINT32 x, y;
732 	const UINT32 bpp = GetBytesPerPixel(DstFormat);
733 	BYTE* pFirstDstLine = &pDstData[nYDst * nDstStep];
734 	BYTE* pFirstDstLineXOffset = &pFirstDstLine[nXDst * bpp];
735 
736 	for (x = 0; x < nWidth; x++)
737 	{
738 		BYTE* pDst = &pFirstDstLine[(x + nXDst) * bpp];
739 		WriteColor(pDst, DstFormat, color);
740 	}
741 
742 	for (y = 1; y < nHeight; y++)
743 	{
744 		BYTE* pDstLine = &pDstData[(y + nYDst) * nDstStep + nXDst * bpp];
745 		memcpy(pDstLine, pFirstDstLineXOffset, nWidth * bpp * 1ULL);
746 	}
747 
748 	return TRUE;
749 }
750 
751 #if defined(SWSCALE_FOUND)
av_format_for_buffer(UINT32 format)752 static int av_format_for_buffer(UINT32 format)
753 {
754 	switch (format)
755 	{
756 		case PIXEL_FORMAT_ARGB32:
757 			return AV_PIX_FMT_BGRA;
758 
759 		case PIXEL_FORMAT_XRGB32:
760 			return AV_PIX_FMT_BGR0;
761 
762 		case PIXEL_FORMAT_BGRA32:
763 			return AV_PIX_FMT_RGBA;
764 
765 		case PIXEL_FORMAT_BGRX32:
766 			return AV_PIX_FMT_RGB0;
767 
768 		default:
769 			return AV_PIX_FMT_NONE;
770 	}
771 }
772 #endif
773 
freerdp_image_scale(BYTE * pDstData,DWORD DstFormat,UINT32 nDstStep,UINT32 nXDst,UINT32 nYDst,UINT32 nDstWidth,UINT32 nDstHeight,const BYTE * pSrcData,DWORD SrcFormat,UINT32 nSrcStep,UINT32 nXSrc,UINT32 nYSrc,UINT32 nSrcWidth,UINT32 nSrcHeight)774 BOOL freerdp_image_scale(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst,
775                          UINT32 nYDst, UINT32 nDstWidth, UINT32 nDstHeight, const BYTE* pSrcData,
776                          DWORD SrcFormat, UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc,
777                          UINT32 nSrcWidth, UINT32 nSrcHeight)
778 {
779 	BOOL rc = FALSE;
780 
781 	if (nDstStep == 0)
782 		nDstStep = nDstWidth * GetBytesPerPixel(DstFormat);
783 
784 	if (nSrcStep == 0)
785 		nSrcStep = nSrcWidth * GetBytesPerPixel(SrcFormat);
786 
787 #if defined(SWSCALE_FOUND) || defined(CAIRO_FOUND)
788 	const BYTE* src = &pSrcData[nXSrc * GetBytesPerPixel(SrcFormat) + nYSrc * nSrcStep];
789 	BYTE* dst = &pDstData[nXDst * GetBytesPerPixel(DstFormat) + nYDst * nDstStep];
790 #endif
791 
792 	/* direct copy is much faster than scaling, so check if we can simply copy... */
793 	if ((nDstWidth == nSrcWidth) && (nDstHeight == nSrcHeight))
794 	{
795 		return freerdp_image_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst, nDstWidth,
796 		                          nDstHeight, pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, NULL,
797 		                          FREERDP_FLIP_NONE);
798 	}
799 	else
800 #if defined(SWSCALE_FOUND)
801 	{
802 		int res;
803 		struct SwsContext* resize;
804 		int srcFormat = av_format_for_buffer(SrcFormat);
805 		int dstFormat = av_format_for_buffer(DstFormat);
806 		const int srcStep[1] = { (int)nSrcStep };
807 		const int dstStep[1] = { (int)nDstStep };
808 
809 		if ((srcFormat == AV_PIX_FMT_NONE) || (dstFormat == AV_PIX_FMT_NONE))
810 			return FALSE;
811 
812 		resize = sws_getContext((int)nSrcWidth, (int)nSrcHeight, srcFormat, (int)nDstWidth,
813 		                        (int)nDstHeight, dstFormat, SWS_BILINEAR, NULL, NULL, NULL);
814 
815 		if (!resize)
816 			goto fail;
817 
818 		res = sws_scale(resize, &src, srcStep, 0, (int)nSrcHeight, &dst, dstStep);
819 		rc = (res == ((int)nDstHeight));
820 	fail:
821 		sws_freeContext(resize);
822 	}
823 
824 #elif defined(CAIRO_FOUND)
825 	{
826 		const double sx = (double)nDstWidth / (double)nSrcWidth;
827 		const double sy = (double)nDstHeight / (double)nSrcHeight;
828 		cairo_t* cairo_context;
829 		cairo_surface_t *csrc, *cdst;
830 
831 		if ((nSrcWidth > INT_MAX) || (nSrcHeight > INT_MAX) || (nSrcStep > INT_MAX))
832 			return FALSE;
833 
834 		if ((nDstWidth > INT_MAX) || (nDstHeight > INT_MAX) || (nDstStep > INT_MAX))
835 			return FALSE;
836 
837 		csrc = cairo_image_surface_create_for_data((void*)src, CAIRO_FORMAT_ARGB32, (int)nSrcWidth,
838 		                                           (int)nSrcHeight, (int)nSrcStep);
839 		cdst = cairo_image_surface_create_for_data(dst, CAIRO_FORMAT_ARGB32, (int)nDstWidth,
840 		                                           (int)nDstHeight, (int)nDstStep);
841 
842 		if (!csrc || !cdst)
843 			goto fail;
844 
845 		cairo_context = cairo_create(cdst);
846 
847 		if (!cairo_context)
848 			goto fail2;
849 
850 		cairo_scale(cairo_context, sx, sy);
851 		cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE);
852 		cairo_set_source_surface(cairo_context, csrc, 0, 0);
853 		cairo_paint(cairo_context);
854 		rc = TRUE;
855 	fail2:
856 		cairo_destroy(cairo_context);
857 	fail:
858 		cairo_surface_destroy(csrc);
859 		cairo_surface_destroy(cdst);
860 	}
861 #else
862 	{
863 		WLog_WARN(TAG, "SmartScaling requested but compiled without libcairo support!");
864 	}
865 #endif
866 	return rc;
867 }
868