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 * 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 = nWidth;
132  hBitmap->height = 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 = gdi_get_brush_pointer(hdcDest, nXDest + x, nYDest + y);
333 
334  if (!patp)
335  {
336  WLog_ERR(TAG, "patp=%p", (const void*)patp);
337  return FALSE;
338  }
339 
340  colorB = FreeRDPReadColor(patp, hdcDest->format);
341  }
342  break;
343 
344  default:
345  break;
346  }
347  }
348 
349  dstColor = process_rop(colorC, colorA, colorB, rop, hdcDest->format);
350  return FreeRDPWriteColor(dstp, hdcDest->format, dstColor);
351 }
352 
353 static BOOL adjust_src_coordinates(HGDI_DC hdcSrc, INT32 nWidth, INT32 nHeight, INT32* px,
354  INT32* py)
355 {
356  HGDI_BITMAP hSrcBmp = NULL;
357  INT32 nXSrc = 0;
358  INT32 nYSrc = 0;
359 
360  if (!hdcSrc || (nWidth < 0) || (nHeight < 0) || !px || !py)
361  return FALSE;
362 
363  hSrcBmp = (HGDI_BITMAP)hdcSrc->selectedObject;
364  nXSrc = *px;
365  nYSrc = *py;
366 
367  if (!hSrcBmp)
368  return FALSE;
369 
370  if (nYSrc < 0)
371  {
372  nYSrc = 0;
373  nHeight = nHeight + nYSrc;
374  }
375 
376  if ((nXSrc) < 0)
377  {
378  nXSrc = 0;
379  nWidth = nWidth + nXSrc;
380  }
381 
382  if (hSrcBmp->width < (nXSrc + nWidth))
383  nXSrc = hSrcBmp->width - nWidth;
384 
385  if (hSrcBmp->height < (nYSrc + nHeight))
386  nYSrc = hSrcBmp->height - nHeight;
387 
388  if ((nXSrc < 0) || (nYSrc < 0))
389  return FALSE;
390 
391  *px = nXSrc;
392  *py = nYSrc;
393  return TRUE;
394 }
395 
396 static BOOL adjust_src_dst_coordinates(HGDI_DC hdcDest, INT32* pnXSrc, INT32* pnYSrc, INT32* pnXDst,
397  INT32* pnYDst, INT32* pnWidth, INT32* pnHeight)
398 {
399  HGDI_BITMAP hDstBmp = NULL;
400  volatile INT32 diffX = 0;
401  volatile INT32 diffY = 0;
402  volatile INT32 nXSrc = 0;
403  volatile INT32 nYSrc = 0;
404  volatile INT32 nXDst = 0;
405  volatile INT32 nYDst = 0;
406  volatile INT32 nWidth = 0;
407  volatile INT32 nHeight = 0;
408 
409  if (!hdcDest || !pnXSrc || !pnYSrc || !pnXDst || !pnYDst || !pnWidth || !pnHeight)
410  return FALSE;
411 
412  hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject;
413  nXSrc = *pnXSrc;
414  nYSrc = *pnYSrc;
415  nXDst = *pnXDst;
416  nYDst = *pnYDst;
417  nWidth = *pnWidth;
418  nHeight = *pnHeight;
419 
420  if (!hDstBmp)
421  return FALSE;
422 
423  if (nXDst < 0)
424  {
425  nXSrc -= nXDst;
426  nWidth += nXDst;
427  nXDst = 0;
428  }
429 
430  if (nYDst < 0)
431  {
432  nYSrc -= nYDst;
433  nHeight += nYDst;
434  nYDst = 0;
435  }
436 
437  diffX = hDstBmp->width - nXDst - nWidth;
438 
439  if (diffX < 0)
440  nWidth += diffX;
441 
442  diffY = hDstBmp->height - nYDst - nHeight;
443 
444  if (diffY < 0)
445  nHeight += diffY;
446 
447  if ((nXDst < 0) || (nYDst < 0) || (nWidth < 0) || (nHeight < 0))
448  {
449  nXDst = 0;
450  nYDst = 0;
451  nWidth = 0;
452  nHeight = 0;
453  }
454 
455  *pnXSrc = nXSrc;
456  *pnYSrc = nYSrc;
457  *pnXDst = nXDst;
458  *pnYDst = nYDst;
459  *pnWidth = nWidth;
460  *pnHeight = nHeight;
461  return TRUE;
462 }
463 
464 static BOOL BitBlt_process(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight,
465  HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, const char* rop,
466  const gdiPalette* palette)
467 {
468  UINT32 style = 0;
469  BOOL useSrc = FALSE;
470  BOOL usePat = FALSE;
471  const char* iter = rop;
472 
473  while (*iter != '\0')
474  {
475  switch (*iter++)
476  {
477  case 'P':
478  usePat = TRUE;
479  break;
480 
481  case 'S':
482  useSrc = TRUE;
483  break;
484 
485  default:
486  break;
487  }
488  }
489 
490  if (!hdcDest)
491  return FALSE;
492 
493  if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth, &nHeight))
494  return FALSE;
495 
496  if (useSrc && !hdcSrc)
497  return FALSE;
498 
499  if (useSrc)
500  {
501  if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc))
502  return FALSE;
503  }
504 
505  if (usePat)
506  {
507  style = gdi_GetBrushStyle(hdcDest);
508 
509  switch (style)
510  {
511  case GDI_BS_SOLID:
512  case GDI_BS_HATCHED:
513  case GDI_BS_PATTERN:
514  break;
515 
516  default:
517  WLog_ERR(TAG, "Invalid brush!!");
518  return FALSE;
519  }
520  }
521 
522  if ((nXDest > nXSrc) && (nYDest > nYSrc))
523  {
524  for (INT32 y = nHeight - 1; y >= 0; y--)
525  {
526  for (INT32 x = nWidth - 1; x >= 0; x--)
527  {
528  if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
529  usePat, style, rop, palette))
530  return FALSE;
531  }
532  }
533  }
534  else if (nXDest > nXSrc)
535  {
536  for (INT32 y = 0; y < nHeight; y++)
537  {
538  for (INT32 x = nWidth - 1; x >= 0; x--)
539  {
540  if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
541  usePat, style, rop, palette))
542  return FALSE;
543  }
544  }
545  }
546  else if (nYDest > nYSrc)
547  {
548  for (INT32 y = nHeight - 1; y >= 0; y--)
549  {
550  for (INT32 x = 0; x < nWidth; x++)
551  {
552  if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
553  usePat, style, rop, palette))
554  return FALSE;
555  }
556  }
557  }
558  else
559  {
560  for (INT32 y = 0; y < nHeight; y++)
561  {
562  for (INT32 x = 0; x < nWidth; x++)
563  {
564  if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
565  usePat, style, rop, palette))
566  return FALSE;
567  }
568  }
569  }
570 
571  return TRUE;
572 }
573 
589 BOOL gdi_BitBlt(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight,
590  HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, DWORD rop, const gdiPalette* palette)
591 {
592  HGDI_BITMAP hSrcBmp = NULL;
593  HGDI_BITMAP hDstBmp = NULL;
594 
595  if (!hdcDest)
596  return FALSE;
597 
598  if (!gdi_ClipCoords(hdcDest, &nXDest, &nYDest, &nWidth, &nHeight, &nXSrc, &nYSrc))
599  return TRUE;
600 
601  /* Check which ROP should be performed.
602  * Some specific ROP are used heavily and are resource intensive,
603  * add optimized versions for these here.
604  *
605  * For all others fall back to the generic implementation.
606  */
607  switch (rop)
608  {
609  case GDI_SRCCOPY:
610  if (!hdcSrc)
611  return FALSE;
612 
613  if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth,
614  &nHeight))
615  return FALSE;
616 
617  if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc))
618  return FALSE;
619 
620  hSrcBmp = (HGDI_BITMAP)hdcSrc->selectedObject;
621  hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject;
622 
623  if (!hSrcBmp || !hDstBmp)
624  return FALSE;
625 
626  if (!freerdp_image_copy(hDstBmp->data, hDstBmp->format, hDstBmp->scanline, nXDest,
627  nYDest, nWidth, nHeight, hSrcBmp->data, hSrcBmp->format,
628  hSrcBmp->scanline, nXSrc, nYSrc, palette, FREERDP_FLIP_NONE))
629  return FALSE;
630 
631  break;
632 
633  case GDI_DSTCOPY:
634  hSrcBmp = (HGDI_BITMAP)hdcDest->selectedObject;
635  hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject;
636 
637  if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth,
638  &nHeight))
639  return FALSE;
640 
641  if (!adjust_src_coordinates(hdcDest, nWidth, nHeight, &nXSrc, &nYSrc))
642  return FALSE;
643 
644  if (!hSrcBmp || !hDstBmp)
645  return FALSE;
646 
647  if (!freerdp_image_copy(hDstBmp->data, hDstBmp->format, hDstBmp->scanline, nXDest,
648  nYDest, nWidth, nHeight, hSrcBmp->data, hSrcBmp->format,
649  hSrcBmp->scanline, nXSrc, nYSrc, palette, FREERDP_FLIP_NONE))
650  return FALSE;
651 
652  break;
653 
654  default:
655  if (!BitBlt_process(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc,
656  gdi_rop_to_string(rop), palette))
657  return FALSE;
658 
659  break;
660  }
661 
662  if (!gdi_InvalidateRegion(hdcDest, nXDest, nYDest, nWidth, nHeight))
663  return FALSE;
664 
665  return TRUE;
666 }