22#include <freerdp/config.h>
29#include <freerdp/log.h>
30#include <freerdp/gdi/gdi.h>
31#include <freerdp/constants.h>
32#include <freerdp/codec/color.h>
33#include <freerdp/codec/bitmap.h>
34#include <freerdp/codec/rfx.h>
35#include <freerdp/codec/nsc.h>
36#include <freerdp/gdi/gdi.h>
39#include "wf_graphics.h"
42#define TAG CLIENT_TAG("windows.gdi")
44static const BYTE wf_rop2_table[] = {
63static BOOL wf_decode_color(wfContext* wfc,
const UINT32 srcColor, COLORREF* color, UINT32* format)
66 rdpSettings* settings;
67 UINT32 SrcFormat, DstFormat;
72 gdi = wfc->common.context.gdi;
73 settings = wfc->common.context.settings;
75 if (!gdi || !settings)
83 switch (FreeRDPGetBitsPerPixel(gdi->dstFormat))
86 DstFormat = PIXEL_FORMAT_ABGR32;
90 DstFormat = PIXEL_FORMAT_BGR24;
94 DstFormat = PIXEL_FORMAT_RGB16;
101 *color = FreeRDPConvertColor(srcColor, SrcFormat, DstFormat, &gdi->palette);
105static BOOL wf_set_rop2(HDC hdc,
int rop2)
107 if ((rop2 < 0x01) || (rop2 > 0x10))
109 WLog_ERR(TAG,
"Unsupported ROP2: %d", rop2);
113 SetROP2(hdc, wf_rop2_table[rop2 - 1]);
120 glyph_bmp = wf_image_new(wfc, glyph->cx, glyph->cy, PIXEL_FORMAT_MONO, glyph->aj);
122 WLog_ERR(TAG,
"wf_image_new failed for glyph");
126static void wf_glyph_free(
wfBitmap* glyph)
128 wf_image_free(glyph);
131static BYTE* wf_glyph_convert(wfContext* wfc,
int width,
int height,
const BYTE* data)
133 const int src_bytes_per_row = (width + 7) / 8;
134 const int dst_bytes_per_row = src_bytes_per_row + (src_bytes_per_row % 2);
135 BYTE* cdata = (BYTE*)malloc(dst_bytes_per_row * height);
138 WLog_ERR(TAG,
"malloc failed for cdata buffer");
141 const BYTE* src = data;
143 for (
int indexy = 0; indexy < height; indexy++)
145 BYTE* dst = &cdata[1ull * indexy * dst_bytes_per_row];
147 for (
int indexx = 0; indexx < dst_bytes_per_row; indexx++)
149 if (indexx < src_bytes_per_row)
159static HBRUSH wf_create_brush(wfContext* wfc, rdpBrush* brush, UINT32 color, UINT32 bpp)
165 HBITMAP pattern = NULL;
166 lbr.lbStyle = brush->style;
168 if (lbr.lbStyle == BS_DIBPATTERN || lbr.lbStyle == BS_DIBPATTERN8X8 ||
169 lbr.lbStyle == BS_DIBPATTERNPT)
170 lbr.lbColor = DIB_RGB_COLORS;
174 if (lbr.lbStyle == BS_PATTERN || lbr.lbStyle == BS_PATTERN8X8)
178 UINT32 format = gdi_get_pixel_format(bpp);
179 pattern = wf_create_dib(wfc, 8, 8, format, brush->data, NULL);
180 lbr.lbHatch = (ULONG_PTR)pattern;
184 for (UINT32 i = 0; i != 8; i++)
185 ipattern[7 - i] = brush->data[i];
187 cdata = wf_glyph_convert(wfc, 8, 8, ipattern);
188 pattern = CreateBitmap(8, 8, 1, 1, cdata);
189 lbr.lbHatch = (ULONG_PTR)pattern;
193 else if (lbr.lbStyle == BS_HATCHED)
195 lbr.lbHatch = brush->hatch;
202 br = CreateBrushIndirect(&lbr);
203 SetBrushOrgEx(wfc->drawing->hdc, brush->x, brush->y, NULL);
206 DeleteObject(pattern);
211BOOL wf_scale_rect(wfContext* wfc, RECT* source)
213 UINT32 ww, wh, dw, dh;
214 rdpSettings* settings;
216 if (!wfc || !source || !wfc->common.context.settings)
219 settings = wfc->common.context.settings;
227 if (!wfc->client_width)
228 wfc->client_width = dw;
230 if (!wfc->client_height)
231 wfc->client_height = dh;
233 ww = wfc->client_width;
234 wh = wfc->client_height;
243 (ww != dw || wh != dh))
245 source->bottom = source->bottom * wh / dh + 20;
246 source->top = source->top * wh / dh - 20;
247 source->left = source->left * ww / dw - 20;
248 source->right = source->right * ww / dw + 20;
251 source->bottom -= wfc->yCurrentScroll;
252 source->top -= wfc->yCurrentScroll;
253 source->left -= wfc->xCurrentScroll;
254 source->right -= wfc->xCurrentScroll;
258void wf_invalidate_region(wfContext* wfc, UINT32 x, UINT32 y, UINT32 width, UINT32 height)
261 rdpGdi* gdi = wfc->common.context.gdi;
262 wfc->update_rect.left = x + wfc->offset_x;
263 wfc->update_rect.top = y + wfc->offset_y;
264 wfc->update_rect.right = wfc->update_rect.left + width;
265 wfc->update_rect.bottom = wfc->update_rect.top + height;
266 wf_scale_rect(wfc, &(wfc->update_rect));
267 InvalidateRect(wfc->hwnd, &(wfc->update_rect), FALSE);
271 rect.bottom = height;
272 wf_scale_rect(wfc, &rect);
273 gdi_InvalidateRegion(gdi->primary->hdc, rect.left, rect.top, rect.right, rect.bottom);
276void wf_update_offset(wfContext* wfc)
278 rdpSettings* settings;
279 settings = wfc->common.context.settings;
285 int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
286 int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
287 int w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
288 int h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
291 if (wfc->offset_x < x)
296 if (wfc->offset_y < y)
301 wfc->offset_x = (GetSystemMetrics(SM_CXSCREEN) -
305 if (wfc->offset_x < 0)
308 wfc->offset_y = (GetSystemMetrics(SM_CYSCREEN) -
312 if (wfc->offset_y < 0)
323void wf_resize_window(wfContext* wfc)
325 rdpSettings* settings;
326 settings = wfc->common.context.settings;
332 int x = GetSystemMetrics(SM_XVIRTUALSCREEN);
333 int y = GetSystemMetrics(SM_YVIRTUALSCREEN);
334 int w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
335 int h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
336 SetWindowLongPtr(wfc->hwnd, GWL_STYLE, WS_POPUP);
337 SetWindowPos(wfc->hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
341 SetWindowLongPtr(wfc->hwnd, GWL_STYLE, WS_POPUP);
342 SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0, GetSystemMetrics(SM_CXSCREEN),
343 GetSystemMetrics(SM_CYSCREEN), SWP_FRAMECHANGED);
348 SetWindowLongPtr(wfc->hwnd, GWL_STYLE, WS_CHILD);
352 if (!wfc->client_height)
355 if (!wfc->client_width)
358 wf_update_canvas_diff(wfc);
360 SetWindowPos(wfc->hwnd, HWND_TOP, wfc->client_x, wfc->client_y,
361 wfc->client_width + wfc->diff.x, wfc->client_height + wfc->diff.y,
367 SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0,
371 wf_update_canvas_diff(wfc);
372 SetWindowPos(wfc->hwnd, HWND_TOP, -1, -1,
375 SWP_NOMOVE | SWP_FRAMECHANGED);
380 SetWindowLongPtr(wfc->hwnd, GWL_STYLE,
381 WS_CAPTION | WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX |
384 if (!wfc->client_height)
387 if (!wfc->client_width)
396 wf_update_canvas_diff(wfc);
408 width = wfc->client_width + wfc->diff.x;
409 height = wfc->client_height + wfc->diff.y;
421 xpos = wfc->client_x;
422 ypos = wfc->client_y;
424 SetWindowPos(wfc->hwnd, HWND_TOP, xpos, ypos, width, height, 0 );
428 wf_update_offset(wfc);
431void wf_toggle_fullscreen(wfContext* wfc)
433 ShowWindow(wfc->hwnd, SW_HIDE);
434 wfc->fullscreen = !wfc->fullscreen;
438 wfc->disablewindowtracking = TRUE;
441 wf_floatbar_toggle_fullscreen(wfc->floatbar, wfc->fullscreen);
442 SetParent(wfc->hwnd, wfc->fullscreen ? NULL : wfc->hWndParent);
443 wf_resize_window(wfc);
444 ShowWindow(wfc->hwnd, SW_SHOW);
445 SetForegroundWindow(wfc->hwnd);
447 if (!wfc->fullscreen)
451 wfc->disablewindowtracking = FALSE;
455static BOOL wf_gdi_palette_update(rdpContext* context,
const PALETTE_UPDATE* palette)
460void wf_set_null_clip_rgn(wfContext* wfc)
462 SelectClipRgn(wfc->drawing->hdc, NULL);
465void wf_set_clip_rgn(wfContext* wfc,
int x,
int y,
int width,
int height)
468 clip = CreateRectRgn(x, y, x + width, y + height);
469 SelectClipRgn(wfc->drawing->hdc, clip);
473static BOOL wf_gdi_set_bounds(rdpContext* context,
const rdpBounds* bounds)
476 wfContext* wfc = (wfContext*)context;
478 if (!context || !bounds)
483 hrgn = CreateRectRgn(bounds->left, bounds->top, bounds->right + 1, bounds->bottom + 1);
484 SelectClipRgn(wfc->drawing->hdc, hrgn);
488 SelectClipRgn(wfc->drawing->hdc, NULL);
493static BOOL wf_gdi_dstblt(rdpContext* context,
const DSTBLT_ORDER* dstblt)
495 wfContext* wfc = (wfContext*)context;
497 if (!context || !dstblt)
500 if (!BitBlt(wfc->drawing->hdc, dstblt->nLeftRect, dstblt->nTopRect, dstblt->nWidth,
501 dstblt->nHeight, NULL, 0, 0, gdi_rop3_code(dstblt->bRop)))
504 wf_invalidate_region(wfc, dstblt->nLeftRect, dstblt->nTopRect, dstblt->nWidth, dstblt->nHeight);
508static BOOL wf_gdi_patblt(rdpContext* context,
PATBLT_ORDER* patblt)
515 COLORREF org_bkcolor;
516 COLORREF org_textcolor;
518 wfContext* wfc = (wfContext*)context;
520 if (!context || !patblt)
523 if (!wf_decode_color(wfc, patblt->foreColor, &fgcolor, NULL))
526 if (!wf_decode_color(wfc, patblt->backColor, &bgcolor, NULL))
529 brush = wf_create_brush(wfc, &patblt->brush, fgcolor,
531 org_bkmode = SetBkMode(wfc->drawing->hdc, OPAQUE);
532 org_bkcolor = SetBkColor(wfc->drawing->hdc, bgcolor);
533 org_textcolor = SetTextColor(wfc->drawing->hdc, fgcolor);
534 org_brush = (HBRUSH)SelectObject(wfc->drawing->hdc, brush);
535 rc = PatBlt(wfc->drawing->hdc, patblt->nLeftRect, patblt->nTopRect, patblt->nWidth,
536 patblt->nHeight, gdi_rop3_code(patblt->bRop));
537 SelectObject(wfc->drawing->hdc, org_brush);
539 SetBkMode(wfc->drawing->hdc, org_bkmode);
540 SetBkColor(wfc->drawing->hdc, org_bkcolor);
541 SetTextColor(wfc->drawing->hdc, org_textcolor);
543 if (wfc->drawing == wfc->primary)
544 wf_invalidate_region(wfc, patblt->nLeftRect, patblt->nTopRect, patblt->nWidth,
550static BOOL wf_gdi_scrblt(rdpContext* context,
const SCRBLT_ORDER* scrblt)
552 wfContext* wfc = (wfContext*)context;
554 if (!context || !scrblt || !wfc->drawing)
557 if (!BitBlt(wfc->drawing->hdc, scrblt->nLeftRect, scrblt->nTopRect, scrblt->nWidth,
558 scrblt->nHeight, wfc->primary->hdc, scrblt->nXSrc, scrblt->nYSrc,
559 gdi_rop3_code(scrblt->bRop)))
562 wf_invalidate_region(wfc, scrblt->nLeftRect, scrblt->nTopRect, scrblt->nWidth, scrblt->nHeight);
566static BOOL wf_gdi_opaque_rect(rdpContext* context,
const OPAQUE_RECT_ORDER* opaque_rect)
570 COLORREF brush_color;
571 wfContext* wfc = (wfContext*)context;
573 if (!context || !opaque_rect)
576 if (!wf_decode_color(wfc, opaque_rect->color, &brush_color, NULL))
579 rect.left = opaque_rect->nLeftRect;
580 rect.top = opaque_rect->nTopRect;
581 rect.right = opaque_rect->nLeftRect + opaque_rect->nWidth;
582 rect.bottom = opaque_rect->nTopRect + opaque_rect->nHeight;
583 brush = CreateSolidBrush(brush_color);
584 FillRect(wfc->drawing->hdc, &rect, brush);
587 if (wfc->drawing == wfc->primary)
588 wf_invalidate_region(wfc, rect.left, rect.top, rect.right - rect.left + 1,
589 rect.bottom - rect.top + 1);
594static BOOL wf_gdi_multi_opaque_rect(rdpContext* context,
599 COLORREF brush_color;
600 wfContext* wfc = (wfContext*)context;
602 if (!context || !multi_opaque_rect)
605 if (!wf_decode_color(wfc, multi_opaque_rect->color, &brush_color, NULL))
608 for (UINT32 i = 0; i < multi_opaque_rect->numRectangles; i++)
610 const DELTA_RECT* rectangle = &multi_opaque_rect->rectangles[i];
611 rect.left = rectangle->left;
612 rect.top = rectangle->top;
613 rect.right = rectangle->left + rectangle->width;
614 rect.bottom = rectangle->top + rectangle->height;
615 brush = CreateSolidBrush(brush_color);
616 FillRect(wfc->drawing->hdc, &rect, brush);
618 if (wfc->drawing == wfc->primary)
619 wf_invalidate_region(wfc, rect.left, rect.top, rect.right - rect.left + 1,
620 rect.bottom - rect.top + 1);
628static BOOL wf_gdi_line_to(rdpContext* context,
const LINE_TO_ORDER* line_to)
634 wfContext* wfc = (wfContext*)context;
636 if (!context || !line_to)
639 if (!wf_decode_color(wfc, line_to->penColor, &pen_color, NULL))
642 pen = CreatePen(line_to->penStyle, line_to->penWidth, pen_color);
643 wf_set_rop2(wfc->drawing->hdc, line_to->bRop2);
644 org_pen = (HPEN)SelectObject(wfc->drawing->hdc, pen);
645 MoveToEx(wfc->drawing->hdc, line_to->nXStart, line_to->nYStart, NULL);
646 LineTo(wfc->drawing->hdc, line_to->nXEnd, line_to->nYEnd);
647 x = (line_to->nXStart < line_to->nXEnd) ? line_to->nXStart : line_to->nXEnd;
648 y = (line_to->nYStart < line_to->nYEnd) ? line_to->nYStart : line_to->nYEnd;
649 w = (line_to->nXStart < line_to->nXEnd) ? (line_to->nXEnd - line_to->nXStart)
650 : (line_to->nXStart - line_to->nXEnd);
651 h = (line_to->nYStart < line_to->nYEnd) ? (line_to->nYEnd - line_to->nYStart)
652 : (line_to->nYStart - line_to->nYEnd);
654 if (wfc->drawing == wfc->primary)
655 wf_invalidate_region(wfc, x, y, w, h);
657 SelectObject(wfc->drawing->hdc, org_pen);
662static BOOL wf_gdi_polyline(rdpContext* context,
const POLYLINE_ORDER* polyline)
669 wfContext* wfc = (wfContext*)context;
671 if (!context || !polyline)
674 if (!wf_decode_color(wfc, polyline->penColor, &pen_color, NULL))
677 hpen = CreatePen(0, 1, pen_color);
678 org_rop2 = wf_set_rop2(wfc->drawing->hdc, polyline->bRop2);
679 org_hpen = (HPEN)SelectObject(wfc->drawing->hdc, hpen);
681 if (polyline->numDeltaEntries > 0)
686 numPoints = polyline->numDeltaEntries + 1;
687 pts = (POINT*)malloc(
sizeof(POINT) * numPoints);
690 WLog_ERR(TAG,
"malloc failed for polyline points");
693 pts[0].x = temp.x = polyline->xStart;
694 pts[0].y = temp.y = polyline->yStart;
696 for (UINT32 i = 0; i < polyline->numDeltaEntries; i++)
698 temp.x += polyline->points[i].x;
699 temp.y += polyline->points[i].y;
700 pts[i + 1].x = temp.x;
701 pts[i + 1].y = temp.y;
704 if (wfc->drawing == wfc->primary)
705 wf_invalidate_region(wfc, wfc->client_x, wfc->client_y, wfc->client_width,
708 Polyline(wfc->drawing->hdc, pts, numPoints);
715 SelectObject(wfc->drawing->hdc, org_hpen);
716 wf_set_rop2(wfc->drawing->hdc, org_rop2);
721static BOOL wf_gdi_memblt(rdpContext* context,
MEMBLT_ORDER* memblt)
724 wfContext* wfc = (wfContext*)context;
726 if (!context || !memblt)
731 if (!bitmap || !wfc->drawing || !wfc->drawing->hdc)
734 if (!BitBlt(wfc->drawing->hdc, memblt->nLeftRect, memblt->nTopRect, memblt->nWidth,
735 memblt->nHeight, bitmap->hdc, memblt->nXSrc, memblt->nYSrc,
736 gdi_rop3_code(memblt->bRop)))
739 if (wfc->drawing == wfc->primary)
740 wf_invalidate_region(wfc, memblt->nLeftRect, memblt->nTopRect, memblt->nWidth,
746static BOOL wf_gdi_mem3blt(rdpContext* context,
MEM3BLT_ORDER* mem3blt)
751 wfContext* wfc = (wfContext*)context;
752 COLORREF fgcolor, bgcolor, orgColor;
753 HBRUSH orgBrush = NULL, brush = NULL;
755 if (!context || !mem3blt)
758 bitmap = (
wfBitmap*)mem3blt->bitmap;
760 if (!bitmap || !wfc->drawing || !wfc->drawing->hdc)
763 hdc = wfc->drawing->hdc;
765 if (!wf_decode_color(wfc, mem3blt->foreColor, &fgcolor, NULL))
768 if (!wf_decode_color(wfc, mem3blt->backColor, &bgcolor, NULL))
771 orgColor = SetTextColor(hdc, fgcolor);
773 switch (mem3blt->brush.style)
776 brush = CreateSolidBrush(fgcolor);
782 HBITMAP bmp = CreateBitmap(8, 8, 1, mem3blt->brush.bpp, mem3blt->brush.data);
783 brush = CreatePatternBrush(bmp);
791 orgBrush = SelectObject(hdc, brush);
793 if (!BitBlt(hdc, mem3blt->nLeftRect, mem3blt->nTopRect, mem3blt->nWidth, mem3blt->nHeight,
794 bitmap->hdc, mem3blt->nXSrc, mem3blt->nYSrc, gdi_rop3_code(mem3blt->bRop)))
797 if (wfc->drawing == wfc->primary)
798 wf_invalidate_region(wfc, mem3blt->nLeftRect, mem3blt->nTopRect, mem3blt->nWidth,
805 SelectObject(hdc, orgBrush);
807 SetTextColor(hdc, orgColor);
811static BOOL wf_gdi_surface_frame_marker(rdpContext* context,
814 rdpSettings* settings;
816 if (!context || !surface_frame_marker || !context->instance)
819 settings = context->settings;
824 if (surface_frame_marker->frameAction == SURFACECMD_FRAMEACTION_END &&
827 IFCALL(context->update->SurfaceFrameAcknowledge, context, surface_frame_marker->frameId);
833void wf_gdi_register_update_callbacks(rdpUpdate* update)
835 rdpPrimaryUpdate* primary = update->primary;
836 update->Palette = wf_gdi_palette_update;
837 update->SetBounds = wf_gdi_set_bounds;
838 primary->DstBlt = wf_gdi_dstblt;
839 primary->PatBlt = wf_gdi_patblt;
840 primary->ScrBlt = wf_gdi_scrblt;
841 primary->OpaqueRect = wf_gdi_opaque_rect;
842 primary->MultiOpaqueRect = wf_gdi_multi_opaque_rect;
843 primary->LineTo = wf_gdi_line_to;
844 primary->Polyline = wf_gdi_polyline;
845 primary->MemBlt = wf_gdi_memblt;
846 primary->Mem3Blt = wf_gdi_mem3blt;
847 update->SurfaceFrameMarker = wf_gdi_surface_frame_marker;
850void wf_update_canvas_diff(wfContext* wfc)
852 RECT rc_client, rc_wnd;
854 GetClientRect(wfc->hwnd, &rc_client);
855 GetWindowRect(wfc->hwnd, &rc_wnd);
856 dx = (rc_wnd.right - rc_wnd.left) - rc_client.right;
857 dy = (rc_wnd.bottom - rc_wnd.top) - rc_client.bottom;
859 if (!wfc->disablewindowtracking)
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.