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