summaryrefslogtreecommitdiffstats
path: root/libfreerdp/gdi/bitmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'libfreerdp/gdi/bitmap.c')
-rw-r--r--libfreerdp/gdi/bitmap.c660
1 files changed, 660 insertions, 0 deletions
diff --git a/libfreerdp/gdi/bitmap.c b/libfreerdp/gdi/bitmap.c
new file mode 100644
index 0000000..915eb40
--- /dev/null
+++ b/libfreerdp/gdi/bitmap.c
@@ -0,0 +1,660 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * GDI Bitmap Functions
+ *
+ * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ * Copyright 2016 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2016 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <freerdp/api.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/gdi/gdi.h>
+#include <freerdp/codec/color.h>
+
+#include <freerdp/gdi/region.h>
+#include <freerdp/gdi/bitmap.h>
+#include <freerdp/log.h>
+#include <freerdp/gdi/shape.h>
+
+#include "brush.h"
+#include "clipping.h"
+#include "../gdi/gdi.h"
+
+#define TAG FREERDP_TAG("gdi.bitmap")
+
+/**
+ * Get pixel at the given coordinates. msdn{dd144909}
+ * @param hdc device context
+ * @param nXPos pixel x position
+ * @param nYPos pixel y position
+ * @return pixel color
+ */
+
+UINT32 gdi_GetPixel(HGDI_DC hdc, UINT32 nXPos, UINT32 nYPos)
+{
+ HGDI_BITMAP hBmp = (HGDI_BITMAP)hdc->selectedObject;
+ BYTE* data =
+ &(hBmp->data[(nYPos * hBmp->scanline) + nXPos * FreeRDPGetBytesPerPixel(hBmp->format)]);
+ return FreeRDPReadColor(data, hBmp->format);
+}
+
+BYTE* gdi_GetPointer(HGDI_BITMAP hBmp, UINT32 X, UINT32 Y)
+{
+ UINT32 bpp = FreeRDPGetBytesPerPixel(hBmp->format);
+ return &hBmp->data[(Y * hBmp->width * bpp) + X * bpp];
+}
+
+/**
+ * Set pixel at the given coordinates. msdn{dd145078}
+ *
+ * @param hBmp device context
+ * @param X pixel x position
+ * @param Y pixel y position
+ * @param crColor new pixel color
+ * @return the color written
+ */
+
+static INLINE UINT32 gdi_SetPixelBmp(HGDI_BITMAP hBmp, UINT32 X, UINT32 Y, UINT32 crColor)
+{
+ BYTE* p = &hBmp->data[(Y * hBmp->scanline) + X * FreeRDPGetBytesPerPixel(hBmp->format)];
+ FreeRDPWriteColor(p, hBmp->format, crColor);
+ return crColor;
+}
+
+UINT32 gdi_SetPixel(HGDI_DC hdc, UINT32 X, UINT32 Y, UINT32 crColor)
+{
+ HGDI_BITMAP hBmp = (HGDI_BITMAP)hdc->selectedObject;
+ return gdi_SetPixelBmp(hBmp, X, Y, crColor);
+}
+
+/**
+ * Create a new bitmap with the given width, height, color format and pixel buffer. msdn{dd183485}
+ *
+ * @param nWidth width
+ * @param nHeight height
+ * @param format the color format used
+ * @param data pixel buffer
+ * @return new bitmap
+ */
+
+HGDI_BITMAP gdi_CreateBitmap(UINT32 nWidth, UINT32 nHeight, UINT32 format, BYTE* data)
+{
+ return gdi_CreateBitmapEx(nWidth, nHeight, format, 0, data, winpr_aligned_free);
+}
+
+/**
+ * Create a new bitmap with the given width, height, color format and pixel buffer. msdn{dd183485}
+ *
+ * @param nWidth width
+ * @param nHeight height
+ * @param format the color format used
+ * @param data pixel buffer
+ * @param fkt_free The function used for deallocation of the buffer, NULL for none.
+ * @return new bitmap
+ */
+
+HGDI_BITMAP gdi_CreateBitmapEx(UINT32 nWidth, UINT32 nHeight, UINT32 format, UINT32 stride,
+ BYTE* data, void (*fkt_free)(void*))
+{
+ HGDI_BITMAP hBitmap = (HGDI_BITMAP)calloc(1, sizeof(GDI_BITMAP));
+
+ if (!hBitmap)
+ return NULL;
+
+ hBitmap->objectType = GDIOBJECT_BITMAP;
+ hBitmap->format = format;
+
+ if (stride > 0)
+ hBitmap->scanline = stride;
+ else
+ hBitmap->scanline = nWidth * FreeRDPGetBytesPerPixel(hBitmap->format);
+
+ hBitmap->width = nWidth;
+ hBitmap->height = nHeight;
+ hBitmap->data = data;
+ hBitmap->free = fkt_free;
+ return hBitmap;
+}
+
+/**
+ * Create a new bitmap of the given width and height compatible with the current device context.
+ * msdn{dd183488}
+ *
+ * @param hdc device context
+ * @param nWidth width
+ * @param nHeight height
+ *
+ * @return new bitmap
+ */
+
+HGDI_BITMAP gdi_CreateCompatibleBitmap(HGDI_DC hdc, UINT32 nWidth, UINT32 nHeight)
+{
+ HGDI_BITMAP hBitmap = (HGDI_BITMAP)calloc(1, sizeof(GDI_BITMAP));
+
+ if (!hBitmap)
+ return NULL;
+
+ hBitmap->objectType = GDIOBJECT_BITMAP;
+ hBitmap->format = hdc->format;
+ hBitmap->width = nWidth;
+ hBitmap->height = nHeight;
+ hBitmap->data = winpr_aligned_malloc(
+ 1ull * nWidth * nHeight * FreeRDPGetBytesPerPixel(hBitmap->format), 16);
+ hBitmap->free = winpr_aligned_free;
+
+ if (!hBitmap->data)
+ {
+ free(hBitmap);
+ return NULL;
+ }
+
+ hBitmap->scanline = nWidth * FreeRDPGetBytesPerPixel(hBitmap->format);
+ return hBitmap;
+}
+
+static BOOL op_not(UINT32* stack, UINT32* stackp)
+{
+ if (!stack || !stackp)
+ return FALSE;
+
+ if (*stackp < 1)
+ return FALSE;
+
+ stack[(*stackp) - 1] = ~stack[(*stackp) - 1];
+ return TRUE;
+}
+
+static BOOL op_and(UINT32* stack, UINT32* stackp)
+{
+ if (!stack || !stackp)
+ return FALSE;
+
+ if (*stackp < 2)
+ return FALSE;
+
+ (*stackp)--;
+ stack[(*stackp) - 1] &= stack[(*stackp)];
+ return TRUE;
+}
+
+static BOOL op_or(UINT32* stack, UINT32* stackp)
+{
+ if (!stack || !stackp)
+ return FALSE;
+
+ if (*stackp < 2)
+ return FALSE;
+
+ (*stackp)--;
+ stack[(*stackp) - 1] |= stack[(*stackp)];
+ return TRUE;
+}
+
+static BOOL op_xor(UINT32* stack, UINT32* stackp)
+{
+ if (!stack || !stackp)
+ return FALSE;
+
+ if (*stackp < 2)
+ return FALSE;
+
+ (*stackp)--;
+ stack[(*stackp) - 1] ^= stack[(*stackp)];
+ return TRUE;
+}
+
+static UINT32 process_rop(UINT32 src, UINT32 dst, UINT32 pat, const char* rop, UINT32 format)
+{
+ UINT32 stack[10] = { 0 };
+ UINT32 stackp = 0;
+
+ while (*rop != '\0')
+ {
+ char op = *rop++;
+
+ switch (op)
+ {
+ case '0':
+ stack[stackp++] = FreeRDPGetColor(format, 0, 0, 0, 0xFF);
+ break;
+
+ case '1':
+ stack[stackp++] = FreeRDPGetColor(format, 0xFF, 0xFF, 0xFF, 0xFF);
+ break;
+
+ case 'D':
+ stack[stackp++] = dst;
+ break;
+
+ case 'S':
+ stack[stackp++] = src;
+ break;
+
+ case 'P':
+ stack[stackp++] = pat;
+ break;
+
+ case 'x':
+ op_xor(stack, &stackp);
+ break;
+
+ case 'a':
+ op_and(stack, &stackp);
+ break;
+
+ case 'o':
+ op_or(stack, &stackp);
+ break;
+
+ case 'n':
+ op_not(stack, &stackp);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return stack[0];
+}
+
+static INLINE BOOL BitBlt_write(HGDI_DC hdcDest, HGDI_DC hdcSrc, INT32 nXDest, INT32 nYDest,
+ INT32 nXSrc, INT32 nYSrc, INT32 x, INT32 y, BOOL useSrc,
+ BOOL usePat, UINT32 style, const char* rop,
+ const gdiPalette* palette)
+{
+ UINT32 dstColor = 0;
+ UINT32 colorA = 0;
+ UINT32 colorB = 0;
+ UINT32 colorC = 0;
+ const INT32 dstX = nXDest + x;
+ const INT32 dstY = nYDest + y;
+ BYTE* dstp = gdi_get_bitmap_pointer(hdcDest, dstX, dstY);
+
+ if (!dstp)
+ {
+ WLog_ERR(TAG, "dstp=%p", (const void*)dstp);
+ return FALSE;
+ }
+
+ colorA = FreeRDPReadColor(dstp, hdcDest->format);
+
+ if (useSrc)
+ {
+ const BYTE* srcp = gdi_get_bitmap_pointer(hdcSrc, nXSrc + x, nYSrc + y);
+
+ if (!srcp)
+ {
+ WLog_ERR(TAG, "srcp=%p", (const void*)srcp);
+ return FALSE;
+ }
+
+ colorC = FreeRDPReadColor(srcp, hdcSrc->format);
+ colorC = FreeRDPConvertColor(colorC, hdcSrc->format, hdcDest->format, palette);
+ }
+
+ if (usePat)
+ {
+ switch (style)
+ {
+ case GDI_BS_SOLID:
+ colorB = hdcDest->brush->color;
+ break;
+
+ case GDI_BS_HATCHED:
+ case GDI_BS_PATTERN:
+ {
+ const BYTE* patp = gdi_get_brush_pointer(hdcDest, nXDest + x, nYDest + y);
+
+ if (!patp)
+ {
+ WLog_ERR(TAG, "patp=%p", (const void*)patp);
+ return FALSE;
+ }
+
+ colorB = FreeRDPReadColor(patp, hdcDest->format);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ dstColor = process_rop(colorC, colorA, colorB, rop, hdcDest->format);
+ return FreeRDPWriteColor(dstp, hdcDest->format, dstColor);
+}
+
+static BOOL adjust_src_coordinates(HGDI_DC hdcSrc, INT32 nWidth, INT32 nHeight, INT32* px,
+ INT32* py)
+{
+ HGDI_BITMAP hSrcBmp = NULL;
+ INT32 nXSrc = 0;
+ INT32 nYSrc = 0;
+
+ if (!hdcSrc || (nWidth < 0) || (nHeight < 0) || !px || !py)
+ return FALSE;
+
+ hSrcBmp = (HGDI_BITMAP)hdcSrc->selectedObject;
+ nXSrc = *px;
+ nYSrc = *py;
+
+ if (!hSrcBmp)
+ return FALSE;
+
+ if (nYSrc < 0)
+ {
+ nYSrc = 0;
+ nHeight = nHeight + nYSrc;
+ }
+
+ if ((nXSrc) < 0)
+ {
+ nXSrc = 0;
+ nWidth = nWidth + nXSrc;
+ }
+
+ if (hSrcBmp->width < (nXSrc + nWidth))
+ nXSrc = hSrcBmp->width - nWidth;
+
+ if (hSrcBmp->height < (nYSrc + nHeight))
+ nYSrc = hSrcBmp->height - nHeight;
+
+ if ((nXSrc < 0) || (nYSrc < 0))
+ return FALSE;
+
+ *px = nXSrc;
+ *py = nYSrc;
+ return TRUE;
+}
+
+static BOOL adjust_src_dst_coordinates(HGDI_DC hdcDest, INT32* pnXSrc, INT32* pnYSrc, INT32* pnXDst,
+ INT32* pnYDst, INT32* pnWidth, INT32* pnHeight)
+{
+ HGDI_BITMAP hDstBmp = NULL;
+ volatile INT32 diffX = 0;
+ volatile INT32 diffY = 0;
+ volatile INT32 nXSrc = 0;
+ volatile INT32 nYSrc = 0;
+ volatile INT32 nXDst = 0;
+ volatile INT32 nYDst = 0;
+ volatile INT32 nWidth = 0;
+ volatile INT32 nHeight = 0;
+
+ if (!hdcDest || !pnXSrc || !pnYSrc || !pnXDst || !pnYDst || !pnWidth || !pnHeight)
+ return FALSE;
+
+ hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject;
+ nXSrc = *pnXSrc;
+ nYSrc = *pnYSrc;
+ nXDst = *pnXDst;
+ nYDst = *pnYDst;
+ nWidth = *pnWidth;
+ nHeight = *pnHeight;
+
+ if (!hDstBmp)
+ return FALSE;
+
+ if (nXDst < 0)
+ {
+ nXSrc -= nXDst;
+ nWidth += nXDst;
+ nXDst = 0;
+ }
+
+ if (nYDst < 0)
+ {
+ nYSrc -= nYDst;
+ nHeight += nYDst;
+ nYDst = 0;
+ }
+
+ diffX = hDstBmp->width - nXDst - nWidth;
+
+ if (diffX < 0)
+ nWidth += diffX;
+
+ diffY = hDstBmp->height - nYDst - nHeight;
+
+ if (diffY < 0)
+ nHeight += diffY;
+
+ if ((nXDst < 0) || (nYDst < 0) || (nWidth < 0) || (nHeight < 0))
+ {
+ nXDst = 0;
+ nYDst = 0;
+ nWidth = 0;
+ nHeight = 0;
+ }
+
+ *pnXSrc = nXSrc;
+ *pnYSrc = nYSrc;
+ *pnXDst = nXDst;
+ *pnYDst = nYDst;
+ *pnWidth = nWidth;
+ *pnHeight = nHeight;
+ return TRUE;
+}
+
+static BOOL BitBlt_process(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight,
+ HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, const char* rop,
+ const gdiPalette* palette)
+{
+ UINT32 style = 0;
+ BOOL useSrc = FALSE;
+ BOOL usePat = FALSE;
+ const char* iter = rop;
+
+ while (*iter != '\0')
+ {
+ switch (*iter++)
+ {
+ case 'P':
+ usePat = TRUE;
+ break;
+
+ case 'S':
+ useSrc = TRUE;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (!hdcDest)
+ return FALSE;
+
+ if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth, &nHeight))
+ return FALSE;
+
+ if (useSrc && !hdcSrc)
+ return FALSE;
+
+ if (useSrc)
+ {
+ if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc))
+ return FALSE;
+ }
+
+ if (usePat)
+ {
+ style = gdi_GetBrushStyle(hdcDest);
+
+ switch (style)
+ {
+ case GDI_BS_SOLID:
+ case GDI_BS_HATCHED:
+ case GDI_BS_PATTERN:
+ break;
+
+ default:
+ WLog_ERR(TAG, "Invalid brush!!");
+ return FALSE;
+ }
+ }
+
+ if ((nXDest > nXSrc) && (nYDest > nYSrc))
+ {
+ for (INT32 y = nHeight - 1; y >= 0; y--)
+ {
+ for (INT32 x = nWidth - 1; x >= 0; x--)
+ {
+ if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
+ usePat, style, rop, palette))
+ return FALSE;
+ }
+ }
+ }
+ else if (nXDest > nXSrc)
+ {
+ for (INT32 y = 0; y < nHeight; y++)
+ {
+ for (INT32 x = nWidth - 1; x >= 0; x--)
+ {
+ if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
+ usePat, style, rop, palette))
+ return FALSE;
+ }
+ }
+ }
+ else if (nYDest > nYSrc)
+ {
+ for (INT32 y = nHeight - 1; y >= 0; y--)
+ {
+ for (INT32 x = 0; x < nWidth; x++)
+ {
+ if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
+ usePat, style, rop, palette))
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ for (INT32 y = 0; y < nHeight; y++)
+ {
+ for (INT32 x = 0; x < nWidth; x++)
+ {
+ if (!BitBlt_write(hdcDest, hdcSrc, nXDest, nYDest, nXSrc, nYSrc, x, y, useSrc,
+ usePat, style, rop, palette))
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * Perform a bit blit operation on the given pixel buffers.
+ * msdn{dd183370}
+ *
+ * @param hdcDest destination device context
+ * @param nXDest destination x1
+ * @param nYDest destination y1
+ * @param nWidth width
+ * @param nHeight height
+ * @param hdcSrc source device context
+ * @param nXSrc source x1
+ * @param nYSrc source y1
+ * @param rop raster operation code
+ * @return 0 on failure, non-zero otherwise
+ */
+BOOL gdi_BitBlt(HGDI_DC hdcDest, INT32 nXDest, INT32 nYDest, INT32 nWidth, INT32 nHeight,
+ HGDI_DC hdcSrc, INT32 nXSrc, INT32 nYSrc, DWORD rop, const gdiPalette* palette)
+{
+ HGDI_BITMAP hSrcBmp = NULL;
+ HGDI_BITMAP hDstBmp = NULL;
+
+ if (!hdcDest)
+ return FALSE;
+
+ if (!gdi_ClipCoords(hdcDest, &nXDest, &nYDest, &nWidth, &nHeight, &nXSrc, &nYSrc))
+ return TRUE;
+
+ /* Check which ROP should be performed.
+ * Some specific ROP are used heavily and are resource intensive,
+ * add optimized versions for these here.
+ *
+ * For all others fall back to the generic implementation.
+ */
+ switch (rop)
+ {
+ case GDI_SRCCOPY:
+ if (!hdcSrc)
+ return FALSE;
+
+ if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth,
+ &nHeight))
+ return FALSE;
+
+ if (!adjust_src_coordinates(hdcSrc, nWidth, nHeight, &nXSrc, &nYSrc))
+ return FALSE;
+
+ hSrcBmp = (HGDI_BITMAP)hdcSrc->selectedObject;
+ hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject;
+
+ if (!hSrcBmp || !hDstBmp)
+ return FALSE;
+
+ if (!freerdp_image_copy(hDstBmp->data, hDstBmp->format, hDstBmp->scanline, nXDest,
+ nYDest, nWidth, nHeight, hSrcBmp->data, hSrcBmp->format,
+ hSrcBmp->scanline, nXSrc, nYSrc, palette, FREERDP_FLIP_NONE))
+ return FALSE;
+
+ break;
+
+ case GDI_DSTCOPY:
+ hSrcBmp = (HGDI_BITMAP)hdcDest->selectedObject;
+ hDstBmp = (HGDI_BITMAP)hdcDest->selectedObject;
+
+ if (!adjust_src_dst_coordinates(hdcDest, &nXSrc, &nYSrc, &nXDest, &nYDest, &nWidth,
+ &nHeight))
+ return FALSE;
+
+ if (!adjust_src_coordinates(hdcDest, nWidth, nHeight, &nXSrc, &nYSrc))
+ return FALSE;
+
+ if (!hSrcBmp || !hDstBmp)
+ return FALSE;
+
+ if (!freerdp_image_copy(hDstBmp->data, hDstBmp->format, hDstBmp->scanline, nXDest,
+ nYDest, nWidth, nHeight, hSrcBmp->data, hSrcBmp->format,
+ hSrcBmp->scanline, nXSrc, nYSrc, palette, FREERDP_FLIP_NONE))
+ return FALSE;
+
+ break;
+
+ default:
+ if (!BitBlt_process(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc,
+ gdi_rop_to_string(rop), palette))
+ return FALSE;
+
+ break;
+ }
+
+ if (!gdi_InvalidateRegion(hdcDest, nXDest, nYDest, nWidth, nHeight))
+ return FALSE;
+
+ return TRUE;
+}