FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
gdi/bitmap.c
1
22#include <freerdp/config.h>
23
24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27
28#include <freerdp/api.h>
29#include <freerdp/freerdp.h>
30#include <freerdp/gdi/gdi.h>
31#include <freerdp/codec/color.h>
32
33#include <freerdp/gdi/region.h>
34#include <freerdp/gdi/bitmap.h>
35#include <freerdp/log.h>
36#include <freerdp/gdi/shape.h>
37
38#include "brush.h"
39#include "clipping.h"
40#include "../gdi/gdi.h"
41
42#define TAG FREERDP_TAG("gdi.bitmap")
43
52UINT32 gdi_GetPixel(HGDI_DC hdc, UINT32 nXPos, UINT32 nYPos)
53{
54 HGDI_BITMAP hBmp = (HGDI_BITMAP)hdc->selectedObject;
55 BYTE* data =
56 &(hBmp->data[(nYPos * hBmp->scanline) + nXPos * FreeRDPGetBytesPerPixel(hBmp->format)]);
57 return FreeRDPReadColor(data, hBmp->format);
58}
59
60BYTE* gdi_GetPointer(HGDI_BITMAP hBmp, UINT32 X, UINT32 Y)
61{
62 UINT32 bpp = FreeRDPGetBytesPerPixel(hBmp->format);
63 return &hBmp->data[(Y * WINPR_ASSERTING_INT_CAST(uint32_t, hBmp->width) * bpp) + X * bpp];
64}
65
76static INLINE UINT32 gdi_SetPixelBmp(HGDI_BITMAP hBmp, UINT32 X, UINT32 Y, UINT32 crColor)
77{
78 BYTE* p = &hBmp->data[(Y * hBmp->scanline) + X * FreeRDPGetBytesPerPixel(hBmp->format)];
79 FreeRDPWriteColor(p, hBmp->format, crColor);
80 return crColor;
81}
82
83UINT32 gdi_SetPixel(HGDI_DC hdc, UINT32 X, UINT32 Y, UINT32 crColor)
84{
85 HGDI_BITMAP hBmp = (HGDI_BITMAP)hdc->selectedObject;
86 return gdi_SetPixelBmp(hBmp, X, Y, crColor);
87}
88
99HGDI_BITMAP gdi_CreateBitmap(UINT32 nWidth, UINT32 nHeight, UINT32 format, BYTE* data)
100{
101 return gdi_CreateBitmapEx(nWidth, nHeight, format, 0, data, winpr_aligned_free);
102}
103
115HGDI_BITMAP gdi_CreateBitmapEx(UINT32 nWidth, UINT32 nHeight, UINT32 format, UINT32 stride,
116 BYTE* data, void (*fkt_free)(void*))
117{
118 HGDI_BITMAP hBitmap = (HGDI_BITMAP)calloc(1, sizeof(GDI_BITMAP));
119
120 if (!hBitmap)
121 return NULL;
122
123 hBitmap->objectType = GDIOBJECT_BITMAP;
124 hBitmap->format = format;
125
126 if (stride > 0)
127 hBitmap->scanline = stride;
128 else
129 hBitmap->scanline = nWidth * FreeRDPGetBytesPerPixel(hBitmap->format);
130
131 hBitmap->width = WINPR_ASSERTING_INT_CAST(int, nWidth);
132 hBitmap->height = WINPR_ASSERTING_INT_CAST(int, nHeight);
133 hBitmap->data = data;
134 hBitmap->free = fkt_free;
135 return hBitmap;
136}
137
149HGDI_BITMAP gdi_CreateCompatibleBitmap(HGDI_DC hdc, UINT32 nWidth, UINT32 nHeight)
150{
151 HGDI_BITMAP hBitmap = (HGDI_BITMAP)calloc(1, sizeof(GDI_BITMAP));
152
153 if (!hBitmap)
154 return NULL;
155
156 hBitmap->objectType = GDIOBJECT_BITMAP;
157 hBitmap->format = hdc->format;
158 WINPR_ASSERT(nWidth <= INT32_MAX);
159 hBitmap->width = (INT32)nWidth;
160
161 WINPR_ASSERT(nHeight <= INT32_MAX);
162 hBitmap->height = (INT32)nHeight;
163
164 size_t size = 1ull * nWidth * nHeight * FreeRDPGetBytesPerPixel(hBitmap->format);
165 hBitmap->data = winpr_aligned_malloc(size, 16);
166 hBitmap->free = winpr_aligned_free;
167
168 if (!hBitmap->data)
169 {
170 free(hBitmap);
171 return NULL;
172 }
173
174 /* Initialize with 0xff */
175 memset(hBitmap->data, 0xff, size);
176 hBitmap->scanline = nWidth * FreeRDPGetBytesPerPixel(hBitmap->format);
177 return hBitmap;
178}
179
180static BOOL op_not(UINT32* stack, const UINT32* stackp)
181{
182 if (!stack || !stackp)
183 return FALSE;
184
185 if (*stackp < 1)
186 return FALSE;
187
188 stack[(*stackp) - 1] = ~stack[(*stackp) - 1];
189 return TRUE;
190}
191
192static BOOL op_and(UINT32* stack, UINT32* stackp)
193{
194 if (!stack || !stackp)
195 return FALSE;
196
197 if (*stackp < 2)
198 return FALSE;
199
200 (*stackp)--;
201 stack[(*stackp) - 1] &= stack[(*stackp)];
202 return TRUE;
203}
204
205static BOOL op_or(UINT32* stack, UINT32* stackp)
206{
207 if (!stack || !stackp)
208 return FALSE;
209
210 if (*stackp < 2)
211 return FALSE;
212
213 (*stackp)--;
214 stack[(*stackp) - 1] |= stack[(*stackp)];
215 return TRUE;
216}
217
218static BOOL op_xor(UINT32* stack, UINT32* stackp)
219{
220 if (!stack || !stackp)
221 return FALSE;
222
223 if (*stackp < 2)
224 return FALSE;
225
226 (*stackp)--;
227 stack[(*stackp) - 1] ^= stack[(*stackp)];
228 return TRUE;
229}
230
231static UINT32 process_rop(UINT32 src, UINT32 dst, UINT32 pat, const char* rop, UINT32 format)
232{
233 UINT32 stack[10] = { 0 };
234 UINT32 stackp = 0;
235
236 while (*rop != '\0')
237 {
238 char op = *rop++;
239
240 switch (op)
241 {
242 case '0':
243 stack[stackp++] = FreeRDPGetColor(format, 0, 0, 0, 0xFF);
244 break;
245
246 case '1':
247 stack[stackp++] = FreeRDPGetColor(format, 0xFF, 0xFF, 0xFF, 0xFF);
248 break;
249
250 case 'D':
251 stack[stackp++] = dst;
252 break;
253
254 case 'S':
255 stack[stackp++] = src;
256 break;
257
258 case 'P':
259 stack[stackp++] = pat;
260 break;
261
262 case 'x':
263 op_xor(stack, &stackp);
264 break;
265
266 case 'a':
267 op_and(stack, &stackp);
268 break;
269
270 case 'o':
271 op_or(stack, &stackp);
272 break;
273
274 case 'n':
275 op_not(stack, &stackp);
276 break;
277
278 default:
279 break;
280 }
281 }
282
283 return stack[0];
284}
285
286static INLINE BOOL BitBlt_write(HGDI_DC hdcDest, HGDI_DC hdcSrc, INT32 nXDest, INT32 nYDest,
287 INT32 nXSrc, INT32 nYSrc, INT32 x, INT32 y, BOOL useSrc,
288 BOOL usePat, UINT32 style, const char* rop,
289 const gdiPalette* palette)
290{
291 UINT32 dstColor = 0;
292 UINT32 colorA = 0;
293 UINT32 colorB = 0;
294 UINT32 colorC = 0;
295 const INT32 dstX = nXDest + x;
296 const INT32 dstY = nYDest + y;
297 BYTE* dstp = gdi_get_bitmap_pointer(hdcDest, dstX, dstY);
298
299 if (!dstp)
300 {
301 WLog_ERR(TAG, "dstp=%p", (const void*)dstp);
302 return FALSE;
303 }
304
305 colorA = FreeRDPReadColor(dstp, hdcDest->format);
306
307 if (useSrc)
308 {
309 const BYTE* srcp = gdi_get_bitmap_pointer(hdcSrc, nXSrc + x, nYSrc + y);
310
311 if (!srcp)
312 {
313 WLog_ERR(TAG, "srcp=%p", (const void*)srcp);
314 return FALSE;
315 }
316
317 colorC = FreeRDPReadColor(srcp, hdcSrc->format);
318 colorC = FreeRDPConvertColor(colorC, hdcSrc->format, hdcDest->format, palette);
319 }
320
321 if (usePat)
322 {
323 switch (style)
324 {
325 case GDI_BS_SOLID:
326 colorB = hdcDest->brush->color;
327 break;
328
329 case GDI_BS_HATCHED:
330 case GDI_BS_PATTERN:
331 {
332 const BYTE* patp =
333 gdi_get_brush_pointer(hdcDest, WINPR_ASSERTING_INT_CAST(uint32_t, nXDest + x),
334 WINPR_ASSERTING_INT_CAST(uint32_t, nYDest + y));
335
336 if (!patp)
337 {
338 WLog_ERR(TAG, "patp=%p", (const void*)patp);
339 return FALSE;
340 }
341
342 colorB = FreeRDPReadColor(patp, hdcDest->format);
343 }
344 break;
345
346 default:
347 break;
348 }
349 }
350
351 dstColor = process_rop(colorC, colorA, colorB, rop, hdcDest->format);
352 return FreeRDPWriteColor(dstp, hdcDest->format, dstColor);
353}
354
355static BOOL adjust_src_coordinates(HGDI_DC hdcSrc, INT32 nWidth, INT32 nHeight, INT32* px,
356 INT32* py)
357{
358 HGDI_BITMAP hSrcBmp = NULL;
359 INT32 nXSrc = 0;
360 INT32 nYSrc = 0;
361
362 if (!hdcSrc || (nWidth < 0) || (nHeight < 0) || !px || !py)
363 return FALSE;
364
365 hSrcBmp = (HGDI_BITMAP)hdcSrc->selectedObject;
366 nXSrc = *px;
367 nYSrc = *py;
368
369 if (!hSrcBmp)
370 return FALSE;
371
372 if (nYSrc < 0)
373 {
374 nYSrc = 0;
375 nHeight = nHeight + nYSrc;
376 }
377
378 if ((nXSrc) < 0)
379 {
380 nXSrc = 0;
381 nWidth = nWidth + nXSrc;
382 }
383
384 if (hSrcBmp->width < (nXSrc + nWidth))
385 nXSrc = hSrcBmp->width - nWidth;
386
387 if (hSrcBmp->height < (nYSrc + nHeight))
388 nYSrc = hSrcBmp->height - nHeight;
389
390 if ((nXSrc < 0) || (nYSrc < 0))
391 return FALSE;
392
393 *px = nXSrc;
394 *py = nYSrc;
395 return TRUE;
396}
397
398static BOOL adjust_src_dst_coordinates(HGDI_DC hdcDest, INT32* pnXSrc, INT32* pnYSrc, INT32* pnXDst,
399 INT32* pnYDst, INT32* pnWidth, INT32* pnHeight)
400{
401 HGDI_BITMAP hDstBmp = NULL;
402 volatile INT32 diffX = 0;
403 volatile INT32 diffY = 0;
404 volatile INT32 nXSrc = 0;
405 volatile INT32 nYSrc = 0;
406 volatile INT32 nXDst = 0;
407 volatile INT32 nYDst = 0;
408 volatile INT32 nWidth = 0;
409 volatile INT32 nHeight = 0;
410
411 if (!hdcDest || !pnXSrc || !pnYSrc || !pnXDst || !pnYDst || !pnWidth || !pnHeight)
412 return FALSE;
413
414 hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject;
415 nXSrc = *pnXSrc;
416 nYSrc = *pnYSrc;
417 nXDst = *pnXDst;
418 nYDst = *pnYDst;
419 nWidth = *pnWidth;
420 nHeight = *pnHeight;
421
422 if (!hDstBmp)
423 return FALSE;
424
425 if (nXDst < 0)
426 {
427 nXSrc -= nXDst;
428 nWidth += nXDst;
429 nXDst = 0;
430 }
431
432 if (nYDst < 0)
433 {
434 nYSrc -= nYDst;
435 nHeight += nYDst;
436 nYDst = 0;
437 }
438
439 diffX = hDstBmp->width - nXDst - nWidth;
440
441 if (diffX < 0)
442 nWidth += diffX;
443
444 diffY = hDstBmp->height - nYDst - nHeight;
445
446 if (diffY < 0)
447 nHeight += diffY;
448
449 if ((nXDst < 0) || (nYDst < 0) || (nWidth < 0) || (nHeight < 0))
450 {
451 nXDst = 0;
452 nYDst = 0;
453 nWidth = 0;
454 nHeight = 0;
455 }
456
457 *pnXSrc = nXSrc;
458 *pnYSrc = nYSrc;
459 *pnXDst = nXDst;
460 *pnYDst = nYDst;
461 *pnWidth = nWidth;
462 *pnHeight = nHeight;
463 return TRUE;
464}
465
466static BOOL BitBlt_process(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight,
467 HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, const char* rop,
468 const gdiPalette* palette)
469{
470 UINT32 style = 0;
471 BOOL useSrc = FALSE;
472 BOOL usePat = FALSE;
473 const char* iter = rop;
474
475 while (*iter != '\0')
476 {
477 switch (*iter++)
478 {
479 case 'P':
480 usePat = TRUE;
481 break;
482
483 case 'S':
484 useSrc = TRUE;
485 break;
486
487 default:
488 break;
489 }
490 }
491
492 if (!hdcDest)
493 return FALSE;
494
495 if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth, &nHeight))
496 return FALSE;
497
498 if (useSrc && !hdcSrc)
499 return FALSE;
500
501 if (useSrc)
502 {
503 if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc))
504 return FALSE;
505 }
506
507 if (usePat)
508 {
509 style = gdi_GetBrushStyle(hdcDest);
510
511 switch (style)
512 {
513 case GDI_BS_SOLID:
514 case GDI_BS_HATCHED:
515 case GDI_BS_PATTERN:
516 break;
517
518 default:
519 WLog_ERR(TAG, "Invalid brush!!");
520 return FALSE;
521 }
522 }
523
524 if ((nXDest > nXSrc) && (nYDest > nYSrc))
525 {
526 for (INT32 y = nHeight - 1; y >= 0; y--)
527 {
528 for (INT32 x = nWidth - 1; x >= 0; x--)
529 {
530 if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
531 usePat, style, rop, palette))
532 return FALSE;
533 }
534 }
535 }
536 else if (nXDest > nXSrc)
537 {
538 for (INT32 y = 0; y < nHeight; y++)
539 {
540 for (INT32 x = nWidth - 1; x >= 0; x--)
541 {
542 if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
543 usePat, style, rop, palette))
544 return FALSE;
545 }
546 }
547 }
548 else if (nYDest > nYSrc)
549 {
550 for (INT32 y = nHeight - 1; y >= 0; y--)
551 {
552 for (INT32 x = 0; x < nWidth; x++)
553 {
554 if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
555 usePat, style, rop, palette))
556 return FALSE;
557 }
558 }
559 }
560 else
561 {
562 for (INT32 y = 0; y < nHeight; y++)
563 {
564 for (INT32 x = 0; x < nWidth; x++)
565 {
566 if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
567 usePat, style, rop, palette))
568 return FALSE;
569 }
570 }
571 }
572
573 return TRUE;
574}
575
591BOOL gdi_BitBlt(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight,
592 HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, DWORD rop, const gdiPalette* palette)
593{
594 HGDI_BITMAP hSrcBmp = NULL;
595 HGDI_BITMAP hDstBmp = NULL;
596
597 if (!hdcDest)
598 return FALSE;
599
600 if (!gdi_ClipCoords(hdcDest, &nXDest, &nYDest, &nWidth, &nHeight, &nXSrc, &nYSrc))
601 return TRUE;
602
603 /* Check which ROP should be performed.
604 * Some specific ROP are used heavily and are resource intensive,
605 * add optimized versions for these here.
606 *
607 * For all others fall back to the generic implementation.
608 */
609 switch (rop)
610 {
611 case GDI_SRCCOPY:
612 if (!hdcSrc)
613 return FALSE;
614
615 if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth,
616 &nHeight))
617 return FALSE;
618
619 if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc))
620 return FALSE;
621
622 hSrcBmp = (HGDI_BITMAP)hdcSrc->selectedObject;
623 hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject;
624
625 if (!hSrcBmp || !hDstBmp)
626 return FALSE;
627
628 if (!freerdp_image_copy(
629 hDstBmp->data, hDstBmp->format, hDstBmp->scanline,
630 WINPR_ASSERTING_INT_CAST(UINT32, nXDest),
631 WINPR_ASSERTING_INT_CAST(UINT32, nYDest),
632 WINPR_ASSERTING_INT_CAST(UINT32, nWidth),
633 WINPR_ASSERTING_INT_CAST(UINT32, nHeight), hSrcBmp->data, hSrcBmp->format,
634 hSrcBmp->scanline, WINPR_ASSERTING_INT_CAST(UINT32, nXSrc),
635 WINPR_ASSERTING_INT_CAST(UINT32, nYSrc), palette, FREERDP_FLIP_NONE))
636 return FALSE;
637
638 break;
639
640 case GDI_DSTCOPY:
641 hSrcBmp = (HGDI_BITMAP)hdcDest->selectedObject;
642 hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject;
643
644 if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth,
645 &nHeight))
646 return FALSE;
647
648 if (!adjust_src_coordinates(hdcDest, nWidth, nHeight, &nXSrc, &nYSrc))
649 return FALSE;
650
651 if (!hSrcBmp || !hDstBmp)
652 return FALSE;
653
654 if (!freerdp_image_copy(
655 hDstBmp->data, hDstBmp->format, hDstBmp->scanline,
656 WINPR_ASSERTING_INT_CAST(UINT32, nXDest),
657 WINPR_ASSERTING_INT_CAST(UINT32, nYDest),
658 WINPR_ASSERTING_INT_CAST(UINT32, nWidth),
659 WINPR_ASSERTING_INT_CAST(UINT32, nHeight), hSrcBmp->data, hSrcBmp->format,
660 hSrcBmp->scanline, WINPR_ASSERTING_INT_CAST(UINT32, nXSrc),
661 WINPR_ASSERTING_INT_CAST(UINT32, nYSrc), palette, FREERDP_FLIP_NONE))
662 return FALSE;
663
664 break;
665
666 default:
667 if (!BitBlt_process(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc,
668 gdi_rop_to_string(rop), palette))
669 return FALSE;
670
671 break;
672 }
673
674 if (!gdi_InvalidateRegion(hdcDest, nXDest, nYDest, nWidth, nHeight))
675 return FALSE;
676
677 return TRUE;
678}