FreeRDP
Loading...
Searching...
No Matches
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 = WINPR_PACKED_ALIGN_CAST(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 = WINPR_PACKED_ALIGN_CAST(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 nullptr;
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 nullptr;
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 =
166 winpr_aligned_calloc(1ull * nWidth * FreeRDPGetBytesPerPixel(hBitmap->format), nHeight, 16);
167 hBitmap->free = winpr_aligned_free;
168
169 if (!hBitmap->data)
170 {
171 free(hBitmap);
172 return nullptr;
173 }
174
175 /* Initialize with 0xff */
176 memset(hBitmap->data, 0xff, size);
177 hBitmap->scanline = nWidth * FreeRDPGetBytesPerPixel(hBitmap->format);
178 return hBitmap;
179}
180
181static BOOL op_not(UINT32* stack, const UINT32* stackp)
182{
183 if (!stack || !stackp)
184 return FALSE;
185
186 if (*stackp < 1)
187 return FALSE;
188
189 stack[(*stackp) - 1] = ~stack[(*stackp) - 1];
190 return TRUE;
191}
192
193static BOOL op_and(UINT32* stack, UINT32* stackp)
194{
195 if (!stack || !stackp)
196 return FALSE;
197
198 if (*stackp < 2)
199 return FALSE;
200
201 (*stackp)--;
202 stack[(*stackp) - 1] &= stack[(*stackp)];
203 return TRUE;
204}
205
206static BOOL op_or(UINT32* stack, UINT32* stackp)
207{
208 if (!stack || !stackp)
209 return FALSE;
210
211 if (*stackp < 2)
212 return FALSE;
213
214 (*stackp)--;
215 stack[(*stackp) - 1] |= stack[(*stackp)];
216 return TRUE;
217}
218
219static BOOL op_xor(UINT32* stack, UINT32* stackp)
220{
221 if (!stack || !stackp)
222 return FALSE;
223
224 if (*stackp < 2)
225 return FALSE;
226
227 (*stackp)--;
228 stack[(*stackp) - 1] ^= stack[(*stackp)];
229 return TRUE;
230}
231
232static UINT32 process_rop(UINT32 src, UINT32 dst, UINT32 pat, const char* rop, UINT32 format)
233{
234 UINT32 stack[10] = WINPR_C_ARRAY_INIT;
235 UINT32 stackp = 0;
236
237 while (*rop != '\0')
238 {
239 char op = *rop++;
240
241 switch (op)
242 {
243 case '0':
244 stack[stackp++] = FreeRDPGetColor(format, 0, 0, 0, 0xFF);
245 break;
246
247 case '1':
248 stack[stackp++] = FreeRDPGetColor(format, 0xFF, 0xFF, 0xFF, 0xFF);
249 break;
250
251 case 'D':
252 stack[stackp++] = dst;
253 break;
254
255 case 'S':
256 stack[stackp++] = src;
257 break;
258
259 case 'P':
260 stack[stackp++] = pat;
261 break;
262
263 case 'x':
264 op_xor(stack, &stackp);
265 break;
266
267 case 'a':
268 op_and(stack, &stackp);
269 break;
270
271 case 'o':
272 op_or(stack, &stackp);
273 break;
274
275 case 'n':
276 op_not(stack, &stackp);
277 break;
278
279 default:
280 break;
281 }
282 }
283
284 return stack[0];
285}
286
287static inline BOOL BitBlt_write(HGDI_DC hdcDest, HGDI_DC hdcSrc, INT32 nXDest, INT32 nYDest,
288 INT32 nXSrc, INT32 nYSrc, INT32 x, INT32 y, BOOL useSrc,
289 BOOL usePat, UINT32 style, const char* rop,
290 const gdiPalette* palette)
291{
292 UINT32 dstColor = 0;
293 UINT32 colorA = 0;
294 UINT32 colorB = 0;
295 UINT32 colorC = 0;
296 const INT32 dstX = nXDest + x;
297 const INT32 dstY = nYDest + y;
298 BYTE* dstp = gdi_get_bitmap_pointer(hdcDest, dstX, dstY);
299
300 if (!dstp)
301 {
302 WLog_ERR(TAG, "dstp=%p", (const void*)dstp);
303 return FALSE;
304 }
305
306 colorA = FreeRDPReadColor(dstp, hdcDest->format);
307
308 if (useSrc)
309 {
310 const BYTE* srcp = gdi_get_bitmap_pointer(hdcSrc, nXSrc + x, nYSrc + y);
311
312 if (!srcp)
313 {
314 WLog_ERR(TAG, "srcp=%p", (const void*)srcp);
315 return FALSE;
316 }
317
318 colorC = FreeRDPReadColor(srcp, hdcSrc->format);
319 colorC = FreeRDPConvertColor(colorC, hdcSrc->format, hdcDest->format, palette);
320 }
321
322 if (usePat)
323 {
324 switch (style)
325 {
326 case GDI_BS_SOLID:
327 colorB = hdcDest->brush->color;
328 break;
329
330 case GDI_BS_HATCHED:
331 case GDI_BS_PATTERN:
332 {
333 const BYTE* patp =
334 gdi_get_brush_pointer(hdcDest, WINPR_ASSERTING_INT_CAST(uint32_t, nXDest + x),
335 WINPR_ASSERTING_INT_CAST(uint32_t, nYDest + y));
336
337 if (!patp)
338 {
339 WLog_ERR(TAG, "patp=%p", (const void*)patp);
340 return FALSE;
341 }
342
343 colorB = FreeRDPReadColor(patp, hdcDest->format);
344 }
345 break;
346
347 default:
348 break;
349 }
350 }
351
352 dstColor = process_rop(colorC, colorA, colorB, rop, hdcDest->format);
353 return FreeRDPWriteColor(dstp, hdcDest->format, dstColor);
354}
355
356static BOOL adjust_src_coordinates(HGDI_DC hdcSrc, INT32 nWidth, INT32 nHeight, INT32* px,
357 INT32* py)
358{
359 HGDI_BITMAP hSrcBmp = nullptr;
360 INT32 nXSrc = 0;
361 INT32 nYSrc = 0;
362
363 if (!hdcSrc || (nWidth < 0) || (nHeight < 0) || !px || !py)
364 return FALSE;
365
366 hSrcBmp = WINPR_PACKED_ALIGN_CAST(HGDI_BITMAP, hdcSrc->selectedObject);
367 nXSrc = *px;
368 nYSrc = *py;
369
370 if (!hSrcBmp)
371 return FALSE;
372
373 if (nYSrc < 0)
374 {
375 nYSrc = 0;
376 nHeight = nHeight + nYSrc;
377 }
378
379 if ((nXSrc) < 0)
380 {
381 nXSrc = 0;
382 nWidth = nWidth + nXSrc;
383 }
384
385 if (hSrcBmp->width < (nXSrc + nWidth))
386 nXSrc = hSrcBmp->width - nWidth;
387
388 if (hSrcBmp->height < (nYSrc + nHeight))
389 nYSrc = hSrcBmp->height - nHeight;
390
391 if ((nXSrc < 0) || (nYSrc < 0))
392 return FALSE;
393
394 *px = nXSrc;
395 *py = nYSrc;
396 return TRUE;
397}
398
399static BOOL adjust_src_dst_coordinates(HGDI_DC hdcDest, INT32* pnXSrc, INT32* pnYSrc, INT32* pnXDst,
400 INT32* pnYDst, INT32* pnWidth, INT32* pnHeight)
401{
402 HGDI_BITMAP hDstBmp = nullptr;
403 volatile INT32 diffX = 0;
404 volatile INT32 diffY = 0;
405 volatile INT32 nXSrc = 0;
406 volatile INT32 nYSrc = 0;
407 volatile INT32 nXDst = 0;
408 volatile INT32 nYDst = 0;
409 volatile INT32 nWidth = 0;
410 volatile INT32 nHeight = 0;
411
412 if (!hdcDest || !pnXSrc || !pnYSrc || !pnXDst || !pnYDst || !pnWidth || !pnHeight)
413 return FALSE;
414
415 hDstBmp = WINPR_PACKED_ALIGN_CAST(HGDI_BITMAP, hdcDest->selectedObject);
416 nXSrc = *pnXSrc;
417 nYSrc = *pnYSrc;
418 nXDst = *pnXDst;
419 nYDst = *pnYDst;
420 nWidth = *pnWidth;
421 nHeight = *pnHeight;
422
423 if (!hDstBmp)
424 return FALSE;
425
426 if (nXDst < 0)
427 {
428 nXSrc -= nXDst;
429 nWidth += nXDst;
430 nXDst = 0;
431 }
432
433 if (nYDst < 0)
434 {
435 nYSrc -= nYDst;
436 nHeight += nYDst;
437 nYDst = 0;
438 }
439
440 diffX = hDstBmp->width - nXDst - nWidth;
441
442 if (diffX < 0)
443 nWidth += diffX;
444
445 diffY = hDstBmp->height - nYDst - nHeight;
446
447 if (diffY < 0)
448 nHeight += diffY;
449
450 if ((nXDst < 0) || (nYDst < 0) || (nWidth < 0) || (nHeight < 0))
451 {
452 nXDst = 0;
453 nYDst = 0;
454 nWidth = 0;
455 nHeight = 0;
456 }
457
458 *pnXSrc = nXSrc;
459 *pnYSrc = nYSrc;
460 *pnXDst = nXDst;
461 *pnYDst = nYDst;
462 *pnWidth = nWidth;
463 *pnHeight = nHeight;
464 return TRUE;
465}
466
467static BOOL BitBlt_process(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight,
468 HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, const char* rop,
469 const gdiPalette* palette)
470{
471 UINT32 style = 0;
472 BOOL useSrc = FALSE;
473 BOOL usePat = FALSE;
474 const char* iter = rop;
475
476 while (*iter != '\0')
477 {
478 switch (*iter++)
479 {
480 case 'P':
481 usePat = TRUE;
482 break;
483
484 case 'S':
485 useSrc = TRUE;
486 break;
487
488 default:
489 break;
490 }
491 }
492
493 if (!hdcDest)
494 return FALSE;
495
496 if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth, &nHeight))
497 return FALSE;
498
499 if (useSrc && !hdcSrc)
500 return FALSE;
501
502 if (useSrc)
503 {
504 if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc))
505 return FALSE;
506 }
507
508 if (usePat)
509 {
510 style = gdi_GetBrushStyle(hdcDest);
511
512 switch (style)
513 {
514 case GDI_BS_SOLID:
515 case GDI_BS_HATCHED:
516 case GDI_BS_PATTERN:
517 break;
518
519 default:
520 WLog_ERR(TAG, "Invalid brush!!");
521 return FALSE;
522 }
523 }
524
525 if ((nXDest > nXSrc) && (nYDest > nYSrc))
526 {
527 for (INT32 y = nHeight - 1; y >= 0; y--)
528 {
529 for (INT32 x = nWidth - 1; x >= 0; x--)
530 {
531 if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
532 usePat, style, rop, palette))
533 return FALSE;
534 }
535 }
536 }
537 else if (nXDest > nXSrc)
538 {
539 for (INT32 y = 0; y < nHeight; y++)
540 {
541 for (INT32 x = nWidth - 1; x >= 0; x--)
542 {
543 if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
544 usePat, style, rop, palette))
545 return FALSE;
546 }
547 }
548 }
549 else if (nYDest > nYSrc)
550 {
551 for (INT32 y = nHeight - 1; y >= 0; y--)
552 {
553 for (INT32 x = 0; x < nWidth; x++)
554 {
555 if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
556 usePat, style, rop, palette))
557 return FALSE;
558 }
559 }
560 }
561 else
562 {
563 for (INT32 y = 0; y < nHeight; y++)
564 {
565 for (INT32 x = 0; x < nWidth; x++)
566 {
567 if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
568 usePat, style, rop, palette))
569 return FALSE;
570 }
571 }
572 }
573
574 return TRUE;
575}
576
592BOOL gdi_BitBlt(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight,
593 HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, DWORD rop, const gdiPalette* palette)
594{
595 HGDI_BITMAP hSrcBmp = nullptr;
596 HGDI_BITMAP hDstBmp = nullptr;
597
598 if (!hdcDest)
599 return FALSE;
600
601 if (!gdi_ClipCoords(hdcDest, &nXDest, &nYDest, &nWidth, &nHeight, &nXSrc, &nYSrc))
602 return TRUE;
603
604 /* Check which ROP should be performed.
605 * Some specific ROP are used heavily and are resource intensive,
606 * add optimized versions for these here.
607 *
608 * For all others fall back to the generic implementation.
609 */
610 switch (rop)
611 {
612 case GDI_SRCCOPY:
613 if (!hdcSrc)
614 return FALSE;
615
616 if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth,
617 &nHeight))
618 return FALSE;
619
620 if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc))
621 return FALSE;
622
623 hSrcBmp = WINPR_PACKED_ALIGN_CAST(HGDI_BITMAP, hdcSrc->selectedObject);
624 hDstBmp = WINPR_PACKED_ALIGN_CAST(HGDI_BITMAP, hdcDest->selectedObject);
625
626 if (!hSrcBmp || !hDstBmp)
627 return FALSE;
628
629 if (!freerdp_image_copy(
630 hDstBmp->data, hDstBmp->format, hDstBmp->scanline,
631 WINPR_ASSERTING_INT_CAST(UINT32, nXDest),
632 WINPR_ASSERTING_INT_CAST(UINT32, nYDest),
633 WINPR_ASSERTING_INT_CAST(UINT32, nWidth),
634 WINPR_ASSERTING_INT_CAST(UINT32, nHeight), hSrcBmp->data, hSrcBmp->format,
635 hSrcBmp->scanline, WINPR_ASSERTING_INT_CAST(UINT32, nXSrc),
636 WINPR_ASSERTING_INT_CAST(UINT32, nYSrc), palette, FREERDP_FLIP_NONE))
637 return FALSE;
638
639 break;
640
641 case GDI_DSTCOPY:
642 hSrcBmp = WINPR_PACKED_ALIGN_CAST(HGDI_BITMAP, hdcDest->selectedObject);
643 hDstBmp = WINPR_PACKED_ALIGN_CAST(HGDI_BITMAP, hdcDest->selectedObject);
644
645 if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth,
646 &nHeight))
647 return FALSE;
648
649 if (!adjust_src_coordinates(hdcDest, nWidth, nHeight, &nXSrc, &nYSrc))
650 return FALSE;
651
652 if (!hSrcBmp || !hDstBmp)
653 return FALSE;
654
655 if (!freerdp_image_copy(
656 hDstBmp->data, hDstBmp->format, hDstBmp->scanline,
657 WINPR_ASSERTING_INT_CAST(UINT32, nXDest),
658 WINPR_ASSERTING_INT_CAST(UINT32, nYDest),
659 WINPR_ASSERTING_INT_CAST(UINT32, nWidth),
660 WINPR_ASSERTING_INT_CAST(UINT32, nHeight), hSrcBmp->data, hSrcBmp->format,
661 hSrcBmp->scanline, WINPR_ASSERTING_INT_CAST(UINT32, nXSrc),
662 WINPR_ASSERTING_INT_CAST(UINT32, nYSrc), palette, FREERDP_FLIP_NONE))
663 return FALSE;
664
665 break;
666
667 default:
668 if (!BitBlt_process(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc,
669 gdi_rop_to_string(rop), palette))
670 return FALSE;
671
672 break;
673 }
674
675 return gdi_InvalidateRegion(hdcDest, nXDest, nYDest, nWidth, nHeight);
676}