summaryrefslogtreecommitdiffstats
path: root/libfreerdp/gdi/graphics.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libfreerdp/gdi/graphics.c463
1 files changed, 463 insertions, 0 deletions
diff --git a/libfreerdp/gdi/graphics.c b/libfreerdp/gdi/graphics.c
new file mode 100644
index 0000000..28860cf
--- /dev/null
+++ b/libfreerdp/gdi/graphics.c
@@ -0,0 +1,463 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Graphical Objects
+ *
+ * Copyright 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 <winpr/crt.h>
+
+#include <freerdp/log.h>
+#include <freerdp/freerdp.h>
+#include <freerdp/gdi/dc.h>
+#include <freerdp/gdi/shape.h>
+#include <freerdp/gdi/region.h>
+#include <freerdp/gdi/bitmap.h>
+
+#include "clipping.h"
+#include "drawing.h"
+#include "brush.h"
+#include "graphics.h"
+
+#define TAG FREERDP_TAG("gdi")
+/* Bitmap Class */
+
+HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, UINT32 nWidth, UINT32 nHeight, UINT32 SrcFormat,
+ BYTE* data)
+{
+ UINT32 nSrcStep = 0;
+ UINT32 nDstStep = 0;
+ BYTE* pSrcData = NULL;
+ BYTE* pDstData = NULL;
+ HGDI_BITMAP bitmap = NULL;
+
+ if (!gdi)
+ return NULL;
+
+ nDstStep = nWidth * FreeRDPGetBytesPerPixel(gdi->dstFormat);
+ pDstData = winpr_aligned_malloc(1ull * nHeight * nDstStep, 16);
+
+ if (!pDstData)
+ return NULL;
+
+ pSrcData = data;
+ nSrcStep = nWidth * FreeRDPGetBytesPerPixel(SrcFormat);
+
+ if (!freerdp_image_copy(pDstData, gdi->dstFormat, nDstStep, 0, 0, nWidth, nHeight, pSrcData,
+ SrcFormat, nSrcStep, 0, 0, &gdi->palette, FREERDP_FLIP_NONE))
+ {
+ winpr_aligned_free(pDstData);
+ return NULL;
+ }
+
+ bitmap = gdi_CreateBitmap(nWidth, nHeight, gdi->dstFormat, pDstData);
+ return bitmap;
+}
+
+static BOOL gdi_Bitmap_New(rdpContext* context, rdpBitmap* bitmap)
+{
+ gdiBitmap* gdi_bitmap = NULL;
+ rdpGdi* gdi = context->gdi;
+ gdi_bitmap = (gdiBitmap*)bitmap;
+ gdi_bitmap->hdc = gdi_CreateCompatibleDC(gdi->hdc);
+
+ if (!gdi_bitmap->hdc)
+ return FALSE;
+
+ if (!bitmap->data)
+ gdi_bitmap->bitmap = gdi_CreateCompatibleBitmap(gdi->hdc, bitmap->width, bitmap->height);
+ else
+ {
+ UINT32 format = bitmap->format;
+ gdi_bitmap->bitmap =
+ gdi_create_bitmap(gdi, bitmap->width, bitmap->height, format, bitmap->data);
+ }
+
+ if (!gdi_bitmap->bitmap)
+ {
+ gdi_DeleteDC(gdi_bitmap->hdc);
+ gdi_bitmap->hdc = NULL;
+ return FALSE;
+ }
+
+ gdi_bitmap->hdc->format = gdi_bitmap->bitmap->format;
+ gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->bitmap);
+ gdi_bitmap->org_bitmap = NULL;
+ return TRUE;
+}
+
+static void gdi_Bitmap_Free(rdpContext* context, rdpBitmap* bitmap)
+{
+ gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
+
+ if (gdi_bitmap)
+ {
+ if (gdi_bitmap->hdc)
+ gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->org_bitmap);
+
+ gdi_DeleteObject((HGDIOBJECT)gdi_bitmap->bitmap);
+ gdi_DeleteDC(gdi_bitmap->hdc);
+ winpr_aligned_free(bitmap->data);
+ }
+
+ free(bitmap);
+}
+
+static BOOL gdi_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap)
+{
+ gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
+ UINT32 width = bitmap->right - bitmap->left + 1;
+ UINT32 height = bitmap->bottom - bitmap->top + 1;
+ return gdi_BitBlt(context->gdi->primary->hdc, bitmap->left, bitmap->top, width, height,
+ gdi_bitmap->hdc, 0, 0, GDI_SRCCOPY, &context->gdi->palette);
+}
+
+static BOOL gdi_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, const BYTE* pSrcData,
+ UINT32 DstWidth, UINT32 DstHeight, UINT32 bpp, UINT32 length,
+ BOOL compressed, UINT32 codecId)
+{
+ int status = 0;
+ UINT32 SrcSize = length;
+ rdpGdi* gdi = context->gdi;
+ UINT32 size = DstWidth * DstHeight;
+ bitmap->compressed = FALSE;
+ bitmap->format = gdi->dstFormat;
+
+ if ((FreeRDPGetBytesPerPixel(bitmap->format) == 0) || (DstWidth == 0) || (DstHeight == 0) ||
+ (DstWidth > UINT32_MAX / DstHeight) ||
+ (size > (UINT32_MAX / FreeRDPGetBytesPerPixel(bitmap->format))))
+ {
+ WLog_ERR(TAG, "invalid input data");
+ return FALSE;
+ }
+
+ size *= FreeRDPGetBytesPerPixel(bitmap->format);
+ bitmap->length = size;
+ bitmap->data = (BYTE*)winpr_aligned_malloc(bitmap->length, 16);
+
+ if (!bitmap->data)
+ return FALSE;
+
+ if (compressed)
+ {
+ if ((codecId == RDP_CODEC_ID_REMOTEFX) || (codecId == RDP_CODEC_ID_IMAGE_REMOTEFX))
+ {
+ REGION16 invalidRegion;
+ region16_init(&invalidRegion);
+
+ if (!rfx_process_message(context->codecs->rfx, pSrcData, SrcSize, bitmap->left,
+ bitmap->top, bitmap->data, bitmap->format, gdi->stride,
+ gdi->height, &invalidRegion))
+ {
+ WLog_ERR(TAG, "rfx_process_message failed");
+ return FALSE;
+ }
+
+ status = 1;
+ }
+ else if (codecId == RDP_CODEC_ID_NSCODEC)
+ {
+ status = nsc_process_message(context->codecs->nsc, 32, DstWidth, DstHeight, pSrcData,
+ SrcSize, bitmap->data, bitmap->format, 0, 0, 0, DstWidth,
+ DstHeight, FREERDP_FLIP_VERTICAL);
+
+ if (status < 1)
+ {
+ WLog_ERR(TAG, "nsc_process_message failed");
+ return FALSE;
+ }
+
+ return freerdp_image_copy(bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight,
+ pSrcData, PIXEL_FORMAT_XRGB32, 0, 0, 0, &gdi->palette,
+ FREERDP_FLIP_VERTICAL);
+ }
+ else if (bpp < 32)
+ {
+ if (!interleaved_decompress(context->codecs->interleaved, pSrcData, SrcSize, DstWidth,
+ DstHeight, bpp, bitmap->data, bitmap->format, 0, 0, 0,
+ DstWidth, DstHeight, &gdi->palette))
+ {
+ WLog_ERR(TAG, "interleaved_decompress failed");
+ return FALSE;
+ }
+ }
+ else
+ {
+ const BOOL fidelity =
+ freerdp_settings_get_bool(context->settings, FreeRDP_DrawAllowDynamicColorFidelity);
+ freerdp_planar_switch_bgr(context->codecs->planar, fidelity);
+ if (!planar_decompress(context->codecs->planar, pSrcData, SrcSize, DstWidth, DstHeight,
+ bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight,
+ TRUE))
+ {
+ WLog_ERR(TAG, "planar_decompress failed");
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ const UINT32 SrcFormat = gdi_get_pixel_format(bpp);
+ const size_t sbpp = FreeRDPGetBytesPerPixel(SrcFormat);
+ const size_t dbpp = FreeRDPGetBytesPerPixel(bitmap->format);
+
+ if ((sbpp == 0) || (dbpp == 0))
+ return FALSE;
+ else
+ {
+ const size_t dstSize = SrcSize * dbpp / sbpp;
+
+ if (dstSize < bitmap->length)
+ {
+ WLog_ERR(TAG, "dstSize %" PRIuz " < bitmap->length %" PRIu32, dstSize,
+ bitmap->length);
+ return FALSE;
+ }
+ }
+
+ if (!freerdp_image_copy(bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight,
+ pSrcData, SrcFormat, 0, 0, 0, &gdi->palette, FREERDP_FLIP_VERTICAL))
+ {
+ WLog_ERR(TAG, "freerdp_image_copy failed");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL gdi_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary)
+{
+ rdpGdi* gdi = NULL;
+
+ if (!context)
+ return FALSE;
+
+ gdi = context->gdi;
+
+ if (!gdi)
+ return FALSE;
+
+ if (primary)
+ gdi->drawing = gdi->primary;
+ else
+ gdi->drawing = (gdiBitmap*)bitmap;
+
+ return TRUE;
+}
+
+/* Glyph Class */
+static BOOL gdi_Glyph_New(rdpContext* context, rdpGlyph* glyph)
+{
+ BYTE* data = NULL;
+ gdiGlyph* gdi_glyph = NULL;
+
+ if (!context || !glyph)
+ return FALSE;
+
+ gdi_glyph = (gdiGlyph*)glyph;
+ gdi_glyph->hdc = gdi_GetDC();
+
+ if (!gdi_glyph->hdc)
+ return FALSE;
+
+ gdi_glyph->hdc->format = PIXEL_FORMAT_MONO;
+ data = freerdp_glyph_convert(glyph->cx, glyph->cy, glyph->aj);
+
+ if (!data)
+ {
+ gdi_DeleteDC(gdi_glyph->hdc);
+ return FALSE;
+ }
+
+ gdi_glyph->bitmap = gdi_CreateBitmap(glyph->cx, glyph->cy, PIXEL_FORMAT_MONO, data);
+
+ if (!gdi_glyph->bitmap)
+ {
+ gdi_DeleteDC(gdi_glyph->hdc);
+ winpr_aligned_free(data);
+ return FALSE;
+ }
+
+ gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->bitmap);
+ gdi_glyph->org_bitmap = NULL;
+ return TRUE;
+}
+
+static void gdi_Glyph_Free(rdpContext* context, rdpGlyph* glyph)
+{
+ gdiGlyph* gdi_glyph = NULL;
+ gdi_glyph = (gdiGlyph*)glyph;
+
+ if (gdi_glyph)
+ {
+ gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->org_bitmap);
+ gdi_DeleteObject((HGDIOBJECT)gdi_glyph->bitmap);
+ gdi_DeleteDC(gdi_glyph->hdc);
+ free(glyph->aj);
+ free(glyph);
+ }
+}
+
+static BOOL gdi_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, INT32 x, INT32 y, INT32 w,
+ INT32 h, INT32 sx, INT32 sy, BOOL fOpRedundant)
+{
+ const gdiGlyph* gdi_glyph = NULL;
+ rdpGdi* gdi = NULL;
+ HGDI_BRUSH brush = NULL;
+ BOOL rc = FALSE;
+
+ if (!context || !glyph)
+ return FALSE;
+
+ gdi = context->gdi;
+ gdi_glyph = (const gdiGlyph*)glyph;
+
+ if (!fOpRedundant)
+ {
+ GDI_RECT rect = { 0 };
+
+ if (x > 0)
+ rect.left = x;
+
+ if (y > 0)
+ rect.top = y;
+
+ if (x + w > 0)
+ rect.right = x + w - 1;
+
+ if (y + h > 0)
+ rect.bottom = y + h - 1;
+
+ if ((rect.left < rect.right) && (rect.top < rect.bottom))
+ {
+ brush = gdi_CreateSolidBrush(gdi->drawing->hdc->bkColor);
+
+ if (!brush)
+ return FALSE;
+
+ gdi_FillRect(gdi->drawing->hdc, &rect, brush);
+ gdi_DeleteObject((HGDIOBJECT)brush);
+ }
+ }
+
+ brush = gdi_CreateSolidBrush(gdi->drawing->hdc->textColor);
+
+ if (!brush)
+ return FALSE;
+
+ gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)brush);
+ rc = gdi_BitBlt(gdi->drawing->hdc, x, y, w, h, gdi_glyph->hdc, sx, sy, GDI_GLYPH_ORDER,
+ &context->gdi->palette);
+ gdi_DeleteObject((HGDIOBJECT)brush);
+ return rc;
+}
+
+static BOOL gdi_Glyph_BeginDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height,
+ UINT32 bgcolor, UINT32 fgcolor, BOOL fOpRedundant)
+{
+ rdpGdi* gdi = NULL;
+
+ if (!context || !context->gdi)
+ return FALSE;
+
+ gdi = context->gdi;
+
+ if (!gdi->drawing || !gdi->drawing->hdc)
+ return FALSE;
+
+ if (!fOpRedundant)
+ {
+ if (!gdi_decode_color(gdi, bgcolor, &bgcolor, NULL))
+ return FALSE;
+
+ if (!gdi_decode_color(gdi, fgcolor, &fgcolor, NULL))
+ return FALSE;
+
+ gdi_SetClipRgn(gdi->drawing->hdc, x, y, width, height);
+ gdi_SetTextColor(gdi->drawing->hdc, bgcolor);
+ gdi_SetBkColor(gdi->drawing->hdc, fgcolor);
+
+ if (1)
+ {
+ GDI_RECT rect = { 0 };
+ HGDI_BRUSH brush = gdi_CreateSolidBrush(fgcolor);
+
+ if (!brush)
+ return FALSE;
+
+ if (x > 0)
+ rect.left = x;
+
+ if (y > 0)
+ rect.top = y;
+
+ rect.right = x + width - 1;
+ rect.bottom = y + height - 1;
+
+ if ((x + width > rect.left) && (y + height > rect.top))
+ gdi_FillRect(gdi->drawing->hdc, &rect, brush);
+
+ gdi_DeleteObject((HGDIOBJECT)brush);
+ }
+
+ return gdi_SetNullClipRgn(gdi->drawing->hdc);
+ }
+
+ return TRUE;
+}
+
+static BOOL gdi_Glyph_EndDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height,
+ UINT32 bgcolor, UINT32 fgcolor)
+{
+ rdpGdi* gdi = NULL;
+
+ if (!context || !context->gdi)
+ return FALSE;
+
+ gdi = context->gdi;
+
+ if (!gdi->drawing || !gdi->drawing->hdc)
+ return FALSE;
+
+ gdi_SetNullClipRgn(gdi->drawing->hdc);
+ return TRUE;
+}
+
+/* Graphics Module */
+BOOL gdi_register_graphics(rdpGraphics* graphics)
+{
+ rdpBitmap bitmap = { 0 };
+ rdpGlyph glyph = { 0 };
+ bitmap.size = sizeof(gdiBitmap);
+ bitmap.New = gdi_Bitmap_New;
+ bitmap.Free = gdi_Bitmap_Free;
+ bitmap.Paint = gdi_Bitmap_Paint;
+ bitmap.Decompress = gdi_Bitmap_Decompress;
+ bitmap.SetSurface = gdi_Bitmap_SetSurface;
+ graphics_register_bitmap(graphics, &bitmap);
+ glyph.size = sizeof(gdiGlyph);
+ glyph.New = gdi_Glyph_New;
+ glyph.Free = gdi_Glyph_Free;
+ glyph.Draw = gdi_Glyph_Draw;
+ glyph.BeginDraw = gdi_Glyph_BeginDraw;
+ glyph.EndDraw = gdi_Glyph_EndDraw;
+ graphics_register_glyph(graphics, &glyph);
+ return TRUE;
+}