FreeRDP
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 
52 UINT32 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 
60 BYTE* 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 
76 static 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 
83 UINT32 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 
99 HGDI_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 
115 HGDI_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 
149 HGDI_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 
180 static 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 
192 static 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 
205 static 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 
218 static 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 
231 static 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 
286 static 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 
355 static 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 
398 static 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 
466 static 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 
591 BOOL 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 }