FreeRDP
Loading...
Searching...
No Matches
gdi.c
1
22#include <freerdp/config.h>
23
24#include <stdio.h>
25#include <stdlib.h>
26
27#include <winpr/crt.h>
28#include <winpr/assert.h>
29#include <winpr/cast.h>
30
31#include <freerdp/api.h>
32#include <freerdp/log.h>
33#include <freerdp/freerdp.h>
34#include <freerdp/codecs.h>
35
36#include <freerdp/gdi/gdi.h>
37#include <freerdp/gdi/dc.h>
38#include <freerdp/gdi/pen.h>
39#include <freerdp/gdi/shape.h>
40#include <freerdp/gdi/region.h>
41#include <freerdp/gdi/bitmap.h>
42
43#include "drawing.h"
44#include "clipping.h"
45#include "brush.h"
46#include "line.h"
47#include "gdi.h"
48#include "../core/graphics.h"
49#include "../core/update.h"
50#include "../cache/cache.h"
51
52#define TAG FREERDP_TAG("gdi")
53
54/* Ternary Raster Operation Table */
55typedef struct
56{
57 DWORD code;
58 const char* name;
59} rop_table_entry;
60
61static const rop_table_entry rop3_code_table[] = { { GDI_BLACKNESS, "0" },
62 { GDI_DPSoon, "DPSoon" },
63 { GDI_DPSona, "DPSona" },
64 { GDI_PSon, "PSon" },
65 { GDI_SDPona, "SDPona" },
66 { GDI_DPon, "DPon" },
67 { GDI_PDSxnon, "PDSxnon" },
68 { GDI_PDSaon, "PDSaon" },
69 { GDI_SDPnaa, "SDPnaa" },
70 { GDI_PDSxon, "PDSxon" },
71 { GDI_DPna, "DPna" },
72 { GDI_PSDnaon, "PSDnaon" },
73 { GDI_SPna, "SPna" },
74 { GDI_PDSnaon, "PDSnaon" },
75 { GDI_PDSonon, "PDSonon" },
76 { GDI_Pn, "Pn" },
77 { GDI_PDSona, "PDSona" },
78 { GDI_NOTSRCERASE, "DSon" },
79 { GDI_SDPxnon, "SDPxnon" },
80 { GDI_SDPaon, "SDPaon" },
81 { GDI_DPSxnon, "DPSxnon" },
82 { GDI_DPSaon, "DPSaon" },
83 { GDI_PSDPSanaxx, "PSDPSanaxx" },
84 { GDI_SSPxDSxaxn, "SSPxDSxaxn" },
85 { GDI_SPxPDxa, "SPxPDxa" },
86 { GDI_SDPSanaxn, "SDPSanaxn" },
87 { GDI_PDSPaox, "PDSPaox" },
88 { GDI_SDPSxaxn, "SDPSxaxn" },
89 { GDI_PSDPaox, "PSDPaox" },
90 { GDI_DSPDxaxn, "DSPDxaxn" },
91 { GDI_PDSox, "PDSox" },
92 { GDI_PDSoan, "PDSoan" },
93 { GDI_DPSnaa, "DPSnaa" },
94 { GDI_SDPxon, "SDPxon" },
95 { GDI_DSna, "DSna" },
96 { GDI_SPDnaon, "SPDnaon" },
97 { GDI_SPxDSxa, "SPxDSxa" },
98 { GDI_PDSPanaxn, "PDSPanaxn" },
99 { GDI_SDPSaox, "SDPSaox" },
100 { GDI_SDPSxnox, "SDPSxnox" },
101 { GDI_DPSxa, "DPSxa" },
102 { GDI_PSDPSaoxxn, "PSDPSaoxxn" },
103 { GDI_DPSana, "DPSana" },
104 { GDI_SSPxPDxaxn, "SSPxPDxaxn" },
105 { GDI_SPDSoax, "SPDSoax" },
106 { GDI_PSDnox, "PSDnox" },
107 { GDI_PSDPxox, "PSDPxox" },
108 { GDI_PSDnoan, "PSDnoan" },
109 { GDI_PSna, "PSna" },
110 { GDI_SDPnaon, "SDPnaon" },
111 { GDI_SDPSoox, "SDPSoox" },
112 { GDI_NOTSRCCOPY, "Sn" },
113 { GDI_SPDSaox, "SPDSaox" },
114 { GDI_SPDSxnox, "SPDSxnox" },
115 { GDI_SDPox, "SDPox" },
116 { GDI_SDPoan, "SDPoan" },
117 { GDI_PSDPoax, "PSDPoax" },
118 { GDI_SPDnox, "SPDnox" },
119 { GDI_SPDSxox, "SPDSxox" },
120 { GDI_SPDnoan, "SPDnoan" },
121 { GDI_PSx, "PSx" },
122 { GDI_SPDSonox, "SPDSonox" },
123 { GDI_SPDSnaox, "SPDSnaox" },
124 { GDI_PSan, "PSan" },
125 { GDI_PSDnaa, "PSDnaa" },
126 { GDI_DPSxon, "DPSxon" },
127 { GDI_SDxPDxa, "SDxPDxa" },
128 { GDI_SPDSanaxn, "SPDSanaxn" },
129 { GDI_SRCERASE, "SDna" },
130 { GDI_DPSnaon, "DPSnaon" },
131 { GDI_DSPDaox, "DSPDaox" },
132 { GDI_PSDPxaxn, "PSDPxaxn" },
133 { GDI_SDPxa, "SDPxa" },
134 { GDI_PDSPDaoxxn, "PDSPDaoxxn" },
135 { GDI_DPSDoax, "DPSDoax" },
136 { GDI_PDSnox, "PDSnox" },
137 { GDI_SDPana, "SDPana" },
138 { GDI_SSPxDSxoxn, "SSPxDSxoxn" },
139 { GDI_PDSPxox, "PDSPxox" },
140 { GDI_PDSnoan, "PDSnoan" },
141 { GDI_PDna, "PDna" },
142 { GDI_DSPnaon, "DSPnaon" },
143 { GDI_DPSDaox, "DPSDaox" },
144 { GDI_SPDSxaxn, "SPDSxaxn" },
145 { GDI_DPSonon, "DPSonon" },
146 { GDI_DSTINVERT, "Dn" },
147 { GDI_DPSox, "DPSox" },
148 { GDI_DPSoan, "DPSoan" },
149 { GDI_PDSPoax, "PDSPoax" },
150 { GDI_DPSnox, "DPSnox" },
151 { GDI_PATINVERT, "DPx" },
152 { GDI_DPSDonox, "DPSDonox" },
153 { GDI_DPSDxox, "DPSDxox" },
154 { GDI_DPSnoan, "DPSnoan" },
155 { GDI_DPSDnaox, "DPSDnaox" },
156 { GDI_DPan, "DPan" },
157 { GDI_PDSxa, "PDSxa" },
158 { GDI_DSPDSaoxxn, "DSPDSaoxxn" },
159 { GDI_DSPDoax, "DSPDoax" },
160 { GDI_SDPnox, "SDPnox" },
161 { GDI_SDPSoax, "SDPSoax" },
162 { GDI_DSPnox, "DSPnox" },
163 { GDI_SRCINVERT, "DSx" },
164 { GDI_SDPSonox, "SDPSonox" },
165 { GDI_DSPDSonoxxn, "DSPDSonoxxn" },
166 { GDI_PDSxxn, "PDSxxn" },
167 { GDI_DPSax, "DPSax" },
168 { GDI_PSDPSoaxxn, "PSDPSoaxxn" },
169 { GDI_SDPax, "SDPax" },
170 { GDI_PDSPDoaxxn, "PDSPDoaxxn" },
171 { GDI_SDPSnoax, "SDPSnoax" },
172 { GDI_PDSxnan, "PDSxnan" },
173 { GDI_PDSana, "PDSana" },
174 { GDI_SSDxPDxaxn, "SSDxPDxaxn" },
175 { GDI_SDPSxox, "SDPSxox" },
176 { GDI_SDPnoan, "SDPnoan" },
177 { GDI_DSPDxox, "DSPDxox" },
178 { GDI_DSPnoan, "DSPnoan" },
179 { GDI_SDPSnaox, "SDPSnaox" },
180 { GDI_DSan, "DSan" },
181 { GDI_PDSax, "PDSax" },
182 { GDI_DSPDSoaxxn, "DSPDSoaxxn" },
183 { GDI_DPSDnoax, "DPSDnoax" },
184 { GDI_SDPxnan, "SDPxnan" },
185 { GDI_SPDSnoax, "SPDSnoax" },
186 { GDI_DPSxnan, "DPSxnan" },
187 { GDI_SPxDSxo, "SPxDSxo" },
188 { GDI_DPSaan, "DPSaan" },
189 { GDI_DPSaa, "DPSaa" },
190 { GDI_SPxDSxon, "SPxDSxon" },
191 { GDI_DPSxna, "DPSxna" },
192 { GDI_SPDSnoaxn, "SPDSnoaxn" },
193 { GDI_SDPxna, "SDPxna" },
194 { GDI_PDSPnoaxn, "PDSPnoaxn" },
195 { GDI_DSPDSoaxx, "DSPDSoaxx" },
196 { GDI_PDSaxn, "PDSaxn" },
197 { GDI_SRCAND, "DSa" },
198 { GDI_SDPSnaoxn, "SDPSnaoxn" },
199 { GDI_DSPnoa, "DSPnoa" },
200 { GDI_DSPDxoxn, "DSPDxoxn" },
201 { GDI_SDPnoa, "SDPnoa" },
202 { GDI_SDPSxoxn, "SDPSxoxn" },
203 { GDI_SSDxPDxax, "SSDxPDxax" },
204 { GDI_PDSanan, "PDSanan" },
205 { GDI_PDSxna, "PDSxna" },
206 { GDI_SDPSnoaxn, "SDPSnoaxn" },
207 { GDI_DPSDPoaxx, "DPSDPoaxx" },
208 { GDI_SPDaxn, "SPDaxn" },
209 { GDI_PSDPSoaxx, "PSDPSoaxx" },
210 { GDI_DPSaxn, "DPSaxn" },
211 { GDI_DPSxx, "DPSxx" },
212 { GDI_PSDPSonoxx, "PSDPSonoxx" },
213 { GDI_SDPSonoxn, "SDPSonoxn" },
214 { GDI_DSxn, "DSxn" },
215 { GDI_DPSnax, "DPSnax" },
216 { GDI_SDPSoaxn, "SDPSoaxn" },
217 { GDI_SPDnax, "SPDnax" },
218 { GDI_DSPDoaxn, "DSPDoaxn" },
219 { GDI_DSPDSaoxx, "DSPDSaoxx" },
220 { GDI_PDSxan, "PDSxan" },
221 { GDI_DPa, "DPa" },
222 { GDI_PDSPnaoxn, "PDSPnaoxn" },
223 { GDI_DPSnoa, "DPSnoa" },
224 { GDI_DPSDxoxn, "DPSDxoxn" },
225 { GDI_PDSPonoxn, "PDSPonoxn" },
226 { GDI_PDxn, "PDxn" },
227 { GDI_DSPnax, "DSPnax" },
228 { GDI_PDSPoaxn, "PDSPoaxn" },
229 { GDI_DPSoa, "DPSoa" },
230 { GDI_DPSoxn, "DPSoxn" },
231 { GDI_DSTCOPY, "D" },
232 { GDI_DPSono, "DPSono" },
233 { GDI_SPDSxax, "SPDSxax" },
234 { GDI_DPSDaoxn, "DPSDaoxn" },
235 { GDI_DSPnao, "DSPnao" },
236 { GDI_DPno, "DPno" },
237 { GDI_PDSnoa, "PDSnoa" },
238 { GDI_PDSPxoxn, "PDSPxoxn" },
239 { GDI_SSPxDSxox, "SSPxDSxox" },
240 { GDI_SDPanan, "SDPanan" },
241 { GDI_PSDnax, "PSDnax" },
242 { GDI_DPSDoaxn, "DPSDoaxn" },
243 { GDI_DPSDPaoxx, "DPSDPaoxx" },
244 { GDI_SDPxan, "SDPxan" },
245 { GDI_PSDPxax, "PSDPxax" },
246 { GDI_DSPDaoxn, "DSPDaoxn" },
247 { GDI_DPSnao, "DPSnao" },
248 { GDI_MERGEPAINT, "DSno" },
249 { GDI_SPDSanax, "SPDSanax" },
250 { GDI_SDxPDxan, "SDxPDxan" },
251 { GDI_DPSxo, "DPSxo" },
252 { GDI_DPSano, "DPSano" },
253 { GDI_MERGECOPY, "PSa" },
254 { GDI_SPDSnaoxn, "SPDSnaoxn" },
255 { GDI_SPDSonoxn, "SPDSonoxn" },
256 { GDI_PSxn, "PSxn" },
257 { GDI_SPDnoa, "SPDnoa" },
258 { GDI_SPDSxoxn, "SPDSxoxn" },
259 { GDI_SDPnax, "SDPnax" },
260 { GDI_PSDPoaxn, "PSDPoaxn" },
261 { GDI_SDPoa, "SDPoa" },
262 { GDI_SPDoxn, "SPDoxn" },
263 { GDI_DPSDxax, "DPSDxax" },
264 { GDI_SPDSaoxn, "SPDSaoxn" },
265 { GDI_SRCCOPY, "S" },
266 { GDI_SDPono, "SDPono" },
267 { GDI_SDPnao, "SDPnao" },
268 { GDI_SPno, "SPno" },
269 { GDI_PSDnoa, "PSDnoa" },
270 { GDI_PSDPxoxn, "PSDPxoxn" },
271 { GDI_PDSnax, "PDSnax" },
272 { GDI_SPDSoaxn, "SPDSoaxn" },
273 { GDI_SSPxPDxax, "SSPxPDxax" },
274 { GDI_DPSanan, "DPSanan" },
275 { GDI_PSDPSaoxx, "PSDPSaoxx" },
276 { GDI_DPSxan, "DPSxan" },
277 { GDI_PDSPxax, "PDSPxax" },
278 { GDI_SDPSaoxn, "SDPSaoxn" },
279 { GDI_DPSDanax, "DPSDanax" },
280 { GDI_SPxDSxan, "SPxDSxan" },
281 { GDI_SPDnao, "SPDnao" },
282 { GDI_SDno, "SDno" },
283 { GDI_SDPxo, "SDPxo" },
284 { GDI_SDPano, "SDPano" },
285 { GDI_PDSoa, "PDSoa" },
286 { GDI_PDSoxn, "PDSoxn" },
287 { GDI_DSPDxax, "DSPDxax" },
288 { GDI_PSDPaoxn, "PSDPaoxn" },
289 { GDI_SDPSxax, "SDPSxax" },
290 { GDI_PDSPaoxn, "PDSPaoxn" },
291 { GDI_SDPSanax, "SDPSanax" },
292 { GDI_SPxPDxan, "SPxPDxan" },
293 { GDI_SSPxDSxax, "SSPxDSxax" },
294 { GDI_DSPDSanaxxn, "DSPDSanaxxn" },
295 { GDI_DPSao, "DPSao" },
296 { GDI_DPSxno, "DPSxno" },
297 { GDI_SDPao, "SDPao" },
298 { GDI_SDPxno, "SDPxno" },
299 { GDI_SRCPAINT, "DSo" },
300 { GDI_SDPnoo, "SDPnoo" },
301 { GDI_PATCOPY, "P" },
302 { GDI_PDSono, "PDSono" },
303 { GDI_PDSnao, "PDSnao" },
304 { GDI_PSno, "PSno" },
305 { GDI_PSDnao, "PSDnao" },
306 { GDI_PDno, "PDno" },
307 { GDI_PDSxo, "PDSxo" },
308 { GDI_PDSano, "PDSano" },
309 { GDI_PDSao, "PDSao" },
310 { GDI_PDSxno, "PDSxno" },
311 { GDI_DPo, "DPo" },
312 { GDI_PATPAINT, "DPSnoo" },
313 { GDI_PSo, "PSo" },
314 { GDI_PSDnoo, "PSDnoo" },
315 { GDI_DPSoo, "DPSoo" },
316 { GDI_WHITENESS, "1" } };
317
318/* Hatch Patterns as monochrome data */
319static const BYTE GDI_BS_HATCHED_PATTERNS[] = {
320 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, /* HS_HORIZONTAL */
321 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, /* HS_VERTICAL */
322 0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F, /* HS_FDIAGONAL */
323 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE, /* HS_BDIAGONAL */
324 0xF7, 0xF7, 0xF7, 0x00, 0xF7, 0xF7, 0xF7, 0xF7, /* HS_CROSS */
325 0x7E, 0xBD, 0xDB, 0xE7, 0xE7, 0xDB, 0xBD, 0x7E /* HS_DIACROSS */
326};
327
328#define gdi_rop3_code_checked(code) gdi_rop3_code_checked_int((code), __FILE__, __func__, __LINE__)
329static inline DWORD gdi_rop3_code_checked_int(UINT32 code, WINPR_ATTR_UNUSED const char* file,
330 WINPR_ATTR_UNUSED const char* fkt,
331 WINPR_ATTR_UNUSED size_t line)
332{
333 WINPR_ASSERT_AT(code <= UINT8_MAX, file, fkt, line);
334 return gdi_rop3_code((UINT8)code);
335}
336
337BOOL gdi_decode_color(rdpGdi* gdi, UINT32 srcColor, UINT32* color, UINT32* format)
338{
339 UINT32 SrcFormat = 0;
340
341 if (!gdi || !color || !gdi->context || !gdi->context->settings)
342 return FALSE;
343
344 const UINT32 ColorDepth =
345 freerdp_settings_get_uint32(gdi->context->settings, FreeRDP_ColorDepth);
346
347 switch (ColorDepth)
348 {
349 case 32:
350 case 24:
351 SrcFormat = PIXEL_FORMAT_BGR24;
352 break;
353
354 case 16:
355 SrcFormat = PIXEL_FORMAT_RGB16;
356 break;
357
358 case 15:
359 SrcFormat = PIXEL_FORMAT_RGB15;
360 break;
361
362 case 8:
363 SrcFormat = PIXEL_FORMAT_RGB8;
364 break;
365
366 default:
367 return FALSE;
368 }
369
370 if (format)
371 *format = gdi->dstFormat;
372
373 *color = FreeRDPConvertColor(srcColor, SrcFormat, gdi->dstFormat, &gdi->palette);
374 return TRUE;
375}
376
377/* GDI Helper Functions */
378DWORD gdi_rop3_code(BYTE code)
379{
380 return rop3_code_table[code].code;
381}
382
383const char* gdi_rop3_code_string(BYTE code)
384{
385 return rop3_code_table[code].name;
386}
387
388const char* gdi_rop3_string(DWORD rop)
389{
390 const size_t count = sizeof(rop3_code_table) / sizeof(rop3_code_table[0]);
391
392 for (size_t x = 0; x < count; x++)
393 {
394 if (rop3_code_table[x].code == rop)
395 return rop3_code_table[x].name;
396 }
397
398 return "UNKNOWN";
399}
400
401UINT32 gdi_get_pixel_format(UINT32 bitsPerPixel)
402{
403 UINT32 format = 0;
404
405 switch (bitsPerPixel)
406 {
407 case 32:
408 format = PIXEL_FORMAT_BGRA32;
409 break;
410
411 case 24:
412 format = PIXEL_FORMAT_BGR24;
413 break;
414
415 case 16:
416 format = PIXEL_FORMAT_RGB16;
417 break;
418
419 case 15:
420 format = PIXEL_FORMAT_RGB15;
421 break;
422
423 case 8:
424 format = PIXEL_FORMAT_RGB8;
425 break;
426
427 default:
428 WLog_ERR(TAG, "Unsupported color depth %" PRIu32, bitsPerPixel);
429 format = 0;
430 break;
431 }
432
433 return format;
434}
435
436gdiBitmap* gdi_bitmap_new_ex(rdpGdi* gdi, int width, int height, int bpp, BYTE* data)
437{
438 gdiBitmap* bitmap = (gdiBitmap*)calloc(1, sizeof(gdiBitmap));
439
440 if (!bitmap)
441 goto fail_bitmap;
442
443 if (!(bitmap->hdc = gdi_CreateCompatibleDC(gdi->hdc)))
444 goto fail_hdc;
445
446 WLog_Print(gdi->log, WLOG_DEBUG, "gdi_bitmap_new: width:%d height:%d bpp:%d", width, height,
447 bpp);
448
449 if (!data)
450 bitmap->bitmap =
451 gdi_CreateCompatibleBitmap(gdi->hdc, WINPR_ASSERTING_INT_CAST(uint32_t, width),
452 WINPR_ASSERTING_INT_CAST(uint32_t, height));
453 else
454 bitmap->bitmap = gdi_create_bitmap(gdi, WINPR_ASSERTING_INT_CAST(uint32_t, width),
455 WINPR_ASSERTING_INT_CAST(uint32_t, height),
456 WINPR_ASSERTING_INT_CAST(uint32_t, bpp), data);
457
458 if (!bitmap->bitmap)
459 goto fail_bitmap_bitmap;
460
461 gdi_SelectObject(bitmap->hdc, (HGDIOBJECT)bitmap->bitmap);
462 bitmap->org_bitmap = nullptr;
463 return bitmap;
464fail_bitmap_bitmap:
465 gdi_DeleteDC(bitmap->hdc);
466fail_hdc:
467 free(bitmap);
468fail_bitmap:
469 return nullptr;
470}
471
472void gdi_bitmap_free_ex(gdiBitmap* bitmap)
473{
474 if (bitmap)
475 {
476 gdi_SelectObject(bitmap->hdc, (HGDIOBJECT)bitmap->org_bitmap);
477 gdi_DeleteObject((HGDIOBJECT)bitmap->bitmap);
478 gdi_DeleteDC(bitmap->hdc);
479 free(bitmap);
480 }
481}
482
483BOOL gdi_bitmap_update(rdpContext* context, const BITMAP_UPDATE* bitmapUpdate)
484{
485 if (!context || !bitmapUpdate || !context->gdi || !context->codecs)
486 {
487 WLog_ERR(TAG,
488 "Invalid arguments: context=%p, bitmapUpdate=%p, context->gdi=%p, "
489 "context->codecs=%p",
490 WINPR_CXX_COMPAT_CAST(const void*, context),
491 WINPR_CXX_COMPAT_CAST(const void*, bitmapUpdate),
492 WINPR_CXX_COMPAT_CAST(const void*, context ? context->gdi : nullptr),
493 WINPR_CXX_COMPAT_CAST(const void*, context ? context->codecs : nullptr));
494 return FALSE;
495 }
496
497 for (UINT32 index = 0; index < bitmapUpdate->number; index++)
498 {
499 BOOL rc = FALSE;
500 const BITMAP_DATA* bitmap = &(bitmapUpdate->rectangles[index]);
501 rdpBitmap* bmp = Bitmap_Alloc(context);
502
503 if (!bmp)
504 goto fail;
505
506 if (!Bitmap_SetDimensions(bmp, WINPR_ASSERTING_INT_CAST(UINT16, bitmap->width),
507 WINPR_ASSERTING_INT_CAST(UINT16, bitmap->height)))
508 goto fail;
509
510 if (!Bitmap_SetRectangle(bmp, WINPR_ASSERTING_INT_CAST(UINT16, bitmap->destLeft),
511 WINPR_ASSERTING_INT_CAST(UINT16, bitmap->destTop),
512 WINPR_ASSERTING_INT_CAST(UINT16, bitmap->destRight),
513 WINPR_ASSERTING_INT_CAST(UINT16, bitmap->destBottom)))
514 goto fail;
515
516 if (!bmp->Decompress(context, bmp, bitmap->bitmapDataStream, bitmap->width, bitmap->height,
517 bitmap->bitsPerPixel, bitmap->bitmapLength, bitmap->compressed,
518 RDP_CODEC_ID_NONE))
519 goto fail;
520
521 if (!bmp->New(context, bmp))
522 goto fail;
523
524 if (!bmp->Paint(context, bmp))
525 goto fail;
526
527 rc = TRUE;
528 fail:
529 Bitmap_Free(context, bmp);
530 if (!rc)
531 return FALSE;
532 }
533
534 return TRUE;
535}
536
537static BOOL gdi_palette_update(rdpContext* context, const PALETTE_UPDATE* palette)
538{
539 if (!context || !palette)
540 return FALSE;
541
542 rdpGdi* gdi = context->gdi;
543 if (!gdi)
544 return FALSE;
545
546 gdi->palette.format = gdi->dstFormat;
547
548 for (UINT32 index = 0; index < palette->number; index++)
549 {
550 const PALETTE_ENTRY* pe = &(palette->entries[index]);
551 gdi->palette.palette[index] =
552 FreeRDPGetColor(gdi->dstFormat, pe->red, pe->green, pe->blue, 0xFF);
553 }
554
555 return TRUE;
556}
557
558static BOOL gdi_set_bounds(rdpContext* context, const rdpBounds* bounds)
559{
560 if (!context)
561 return FALSE;
562
563 rdpGdi* gdi = context->gdi;
564 if (!gdi || !gdi->drawing)
565 return FALSE;
566
567 if (bounds)
568 return gdi_SetClipRgn(gdi->drawing->hdc, bounds->left, bounds->top,
569 bounds->right - bounds->left + 1, bounds->bottom - bounds->top + 1);
570
571 return gdi_SetNullClipRgn(gdi->drawing->hdc);
572}
573
574static BOOL gdi_dstblt(rdpContext* context, const DSTBLT_ORDER* dstblt)
575{
576 if (!context || !dstblt)
577 return FALSE;
578
579 rdpGdi* gdi = context->gdi;
580 if (!gdi || !gdi->drawing)
581 return FALSE;
582 return gdi_BitBlt(gdi->drawing->hdc, dstblt->nLeftRect, dstblt->nTopRect, dstblt->nWidth,
583 dstblt->nHeight, nullptr, 0, 0, gdi_rop3_code_checked(dstblt->bRop),
584 &gdi->palette);
585}
586
587static BOOL gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt)
588{
589 WINPR_ASSERT(context);
590 WINPR_ASSERT(patblt);
591
592 const rdpBrush* brush = &patblt->brush;
593 UINT32 foreColor = 0;
594 UINT32 backColor = 0;
595 UINT32 originalColor = 0;
596 HGDI_BRUSH originalBrush = nullptr;
597 HGDI_BRUSH hbrush = nullptr;
598 rdpGdi* gdi = context->gdi;
599 BOOL ret = FALSE;
600 const DWORD rop = gdi_rop3_code_checked(patblt->bRop);
601 INT32 nXSrc = 0;
602 INT32 nYSrc = 0;
603 BYTE data[8 * 8 * 4];
604 HGDI_BITMAP hBmp = nullptr;
605
606 if (!gdi || !gdi->drawing || !gdi->drawing->hdc)
607 return FALSE;
608
609 if (!gdi_decode_color(gdi, patblt->foreColor, &foreColor, nullptr))
610 return FALSE;
611
612 if (!gdi_decode_color(gdi, patblt->backColor, &backColor, nullptr))
613 return FALSE;
614
615 originalColor = gdi_SetTextColor(gdi->drawing->hdc, foreColor);
616 originalBrush = gdi->drawing->hdc->brush;
617
618 switch (brush->style)
619 {
620 case GDI_BS_SOLID:
621 hbrush = gdi_CreateSolidBrush(foreColor);
622 break;
623
624 case GDI_BS_HATCHED:
625 {
626 if (brush->hatch >= ARRAYSIZE(GDI_BS_HATCHED_PATTERNS) / 8)
627 {
628 WLog_ERR(TAG, "invalid brush hatch:%" PRIu32 "", brush->hatch);
629 goto out_error;
630 }
631
632 const BYTE* hatched = &GDI_BS_HATCHED_PATTERNS[8ULL * brush->hatch];
633
634 if (!freerdp_image_copy_from_monochrome(data, gdi->drawing->hdc->format, 0, 0, 0, 8, 8,
635 hatched, backColor, foreColor, &gdi->palette))
636 goto out_error;
637
638 hBmp = gdi_CreateBitmapEx(8, 8, gdi->drawing->hdc->format, 0, data, nullptr);
639
640 if (!hBmp)
641 goto out_error;
642
643 hbrush = gdi_CreateHatchBrush(hBmp);
644 }
645 break;
646
647 case GDI_BS_PATTERN:
648 {
649 UINT32 brushFormat = 0;
650
651 if (brush->bpp > 1)
652 {
653 UINT32 bpp = brush->bpp;
654
655 if ((bpp == 16) &&
656 (freerdp_settings_get_uint32(context->settings, FreeRDP_ColorDepth) == 15))
657 bpp = 15;
658
659 brushFormat = gdi_get_pixel_format(bpp);
660
661 if (!freerdp_image_copy_no_overlap(data, gdi->drawing->hdc->format, 0, 0, 0, 8, 8,
662 brush->data, brushFormat, 0, 0, 0, &gdi->palette,
663 FREERDP_FLIP_NONE))
664 goto out_error;
665 }
666 else
667 {
668 if (!freerdp_image_copy_from_monochrome(data, gdi->drawing->hdc->format, 0, 0, 0, 8,
669 8, brush->data, backColor, foreColor,
670 &gdi->palette))
671 goto out_error;
672 }
673
674 hBmp = gdi_CreateBitmapEx(8, 8, gdi->drawing->hdc->format, 0, data, nullptr);
675
676 if (!hBmp)
677 goto out_error;
678
679 hbrush = gdi_CreatePatternBrush(hBmp);
680 }
681 break;
682
683 default:
684 WLog_ERR(TAG, "unimplemented brush style:%" PRIu32 "", brush->style);
685 break;
686 }
687
688 if (hbrush)
689 {
690 hbrush->nXOrg = WINPR_ASSERTING_INT_CAST(int32_t, brush->x);
691 hbrush->nYOrg = WINPR_ASSERTING_INT_CAST(int32_t, brush->y);
692 gdi->drawing->hdc->brush = hbrush;
693 ret = gdi_BitBlt(gdi->drawing->hdc, patblt->nLeftRect, patblt->nTopRect, patblt->nWidth,
694 patblt->nHeight, gdi->primary->hdc, nXSrc, nYSrc, rop, &gdi->palette);
695 }
696
697out_error:
698 gdi_DeleteObject((HGDIOBJECT)hBmp);
699 gdi_DeleteObject((HGDIOBJECT)hbrush);
700 gdi->drawing->hdc->brush = originalBrush;
701 gdi_SetTextColor(gdi->drawing->hdc, originalColor);
702 return ret;
703}
704
705static BOOL gdi_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt)
706{
707 if (!context || !context->gdi)
708 return FALSE;
709
710 rdpGdi* gdi = context->gdi;
711 if (!gdi->drawing || !gdi->primary)
712 return FALSE;
713
714 return gdi_BitBlt(gdi->drawing->hdc, scrblt->nLeftRect, scrblt->nTopRect, scrblt->nWidth,
715 scrblt->nHeight, gdi->primary->hdc, scrblt->nXSrc, scrblt->nYSrc,
716 gdi_rop3_code_checked(scrblt->bRop), &gdi->palette);
717}
718
719static BOOL gdi_opaque_rect(rdpContext* context, const OPAQUE_RECT_ORDER* opaque_rect)
720{
721 WINPR_ASSERT(context);
722 WINPR_ASSERT(opaque_rect);
723
724 GDI_RECT rect;
725 HGDI_BRUSH hBrush = nullptr;
726 UINT32 brush_color = 0;
727 rdpGdi* gdi = context->gdi;
728 BOOL ret = 0;
729 INT32 x = opaque_rect->nLeftRect;
730 INT32 y = opaque_rect->nTopRect;
731 INT32 w = opaque_rect->nWidth;
732 INT32 h = opaque_rect->nHeight;
733 if (!gdi || !gdi->drawing)
734 return FALSE;
735
736 /* older windows servers tend to send empty rectangles.
737 * ignore these
738 */
739 if (!gdi_CRgnToRect(x, y, w, h, &rect))
740 return TRUE;
741
742 if (!gdi_decode_color(gdi, opaque_rect->color, &brush_color, nullptr))
743 return FALSE;
744
745 if (!(hBrush = gdi_CreateSolidBrush(brush_color)))
746 return FALSE;
747
748 ret = gdi_FillRect(gdi->drawing->hdc, &rect, hBrush);
749 gdi_DeleteObject((HGDIOBJECT)hBrush);
750 return ret;
751}
752
753static BOOL gdi_multi_opaque_rect(rdpContext* context,
754 const MULTI_OPAQUE_RECT_ORDER* multi_opaque_rect)
755{
756 WINPR_ASSERT(context);
757 WINPR_ASSERT(multi_opaque_rect);
758
759 GDI_RECT rect = WINPR_C_ARRAY_INIT;
760 UINT32 brush_color = 0;
761 rdpGdi* gdi = context->gdi;
762 BOOL ret = TRUE;
763
764 if (!gdi || !gdi->drawing)
765 return FALSE;
766
767 if (!gdi_decode_color(gdi, multi_opaque_rect->color, &brush_color, nullptr))
768 return FALSE;
769
770 HGDI_BRUSH hBrush = gdi_CreateSolidBrush(brush_color);
771
772 if (!hBrush)
773 return FALSE;
774
775 for (UINT32 i = 0; i < multi_opaque_rect->numRectangles; i++)
776 {
777 const DELTA_RECT* rectangle = &multi_opaque_rect->rectangles[i];
778 const INT32 x = rectangle->left;
779 const INT32 y = rectangle->top;
780 const INT32 w = rectangle->width;
781 const INT32 h = rectangle->height;
782
783 /* older windows servers tend to send empty rectangles.
784 * ignore these
785 */
786 if (!gdi_CRgnToRect(x, y, w, h, &rect))
787 continue;
788
789 ret = gdi_FillRect(gdi->drawing->hdc, &rect, hBrush);
790
791 if (!ret)
792 break;
793 }
794
795 gdi_DeleteObject((HGDIOBJECT)hBrush);
796 return ret;
797}
798
799static BOOL gdi_line_to(rdpContext* context, const LINE_TO_ORDER* lineTo)
800{
801 BOOL rc = FALSE;
802 UINT32 color = 0;
803 WINPR_ASSERT(context);
804 WINPR_ASSERT(lineTo);
805
806 rdpGdi* gdi = context->gdi;
807 if (!gdi || !gdi->drawing || !gdi->drawing->hdc)
808 return FALSE;
809
810 if (!gdi_decode_color(gdi, lineTo->penColor, &color, nullptr))
811 return FALSE;
812
813 HGDI_PEN hPen = gdi_CreatePen(lineTo->penStyle, lineTo->penWidth, color,
814 gdi->drawing->hdc->format, &gdi->palette);
815 if (!hPen)
816 return FALSE;
817
818 WINPR_ASSERT(gdi->drawing);
819
820 gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)hPen);
821 gdi_SetROP2(gdi->drawing->hdc, WINPR_ASSERTING_INT_CAST(int32_t, lineTo->bRop2));
822 if (!gdi_MoveToEx(gdi->drawing->hdc, lineTo->nXStart, lineTo->nYStart, nullptr))
823 goto fail;
824 if (!gdi_LineTo(gdi->drawing->hdc, lineTo->nXEnd, lineTo->nYEnd))
825 goto fail;
826
827 rc = TRUE;
828fail:
829 gdi_DeleteObject((HGDIOBJECT)hPen);
830 return rc;
831}
832
833static BOOL gdi_polyline(rdpContext* context, const POLYLINE_ORDER* polyline)
834{
835 BOOL rc = FALSE;
836 WINPR_ASSERT(context);
837 WINPR_ASSERT(polyline);
838
839 rdpGdi* gdi = context->gdi;
840 if (!gdi || !gdi->drawing || !gdi->drawing->hdc)
841 return FALSE;
842
843 UINT32 color = 0;
844 if (!gdi_decode_color(gdi, polyline->penColor, &color, nullptr))
845 return FALSE;
846
847 WINPR_ASSERT(gdi->drawing);
848 WINPR_ASSERT(gdi->drawing->hdc);
849
850 HGDI_PEN hPen = gdi_CreatePen(GDI_PS_SOLID, 1, color, gdi->drawing->hdc->format, &gdi->palette);
851 if (!hPen)
852 return FALSE;
853
854 gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)hPen);
855 gdi_SetROP2(gdi->drawing->hdc, WINPR_ASSERTING_INT_CAST(int32_t, polyline->bRop2));
856 INT32 x = polyline->xStart;
857 INT32 y = polyline->yStart;
858 if (!gdi_MoveToEx(gdi->drawing->hdc, x, y, nullptr))
859 goto fail;
860 DELTA_POINT* points = polyline->points;
861
862 for (UINT32 i = 0; i < polyline->numDeltaEntries; i++)
863 {
864 x += points[i].x;
865 y += points[i].y;
866 if (!gdi_LineTo(gdi->drawing->hdc, x, y))
867 goto fail;
868 if (!gdi_MoveToEx(gdi->drawing->hdc, x, y, nullptr))
869 goto fail;
870 }
871
872 rc = TRUE;
873fail:
874 gdi_DeleteObject((HGDIOBJECT)hPen);
875 return rc;
876}
877
878static BOOL gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt)
879{
880 if (!context || !memblt || !memblt->bitmap)
881 return FALSE;
882
883 gdiBitmap* bitmap = (gdiBitmap*)memblt->bitmap;
884 rdpGdi* gdi = context->gdi;
885 if (!gdi || !gdi->drawing)
886 return FALSE;
887 return gdi_BitBlt(gdi->drawing->hdc, memblt->nLeftRect, memblt->nTopRect, memblt->nWidth,
888 memblt->nHeight, bitmap->hdc, memblt->nXSrc, memblt->nYSrc,
889 gdi_rop3_code_checked(memblt->bRop), &gdi->palette);
890}
891
892static BOOL gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt)
893{
894 WINPR_ASSERT(context);
895 WINPR_ASSERT(mem3blt);
896
897 HGDI_BRUSH originalBrush = nullptr;
898 rdpGdi* gdi = context->gdi;
899 if (!gdi || !gdi->drawing)
900 return FALSE;
901
902 BOOL ret = TRUE;
903 const rdpBrush* brush = &mem3blt->brush;
904 gdiBitmap* bitmap = (gdiBitmap*)mem3blt->bitmap;
905 UINT32 foreColor = 0;
906 UINT32 backColor = 0;
907 UINT32 originalColor = 0;
908
909 if (!gdi_decode_color(gdi, mem3blt->foreColor, &foreColor, nullptr))
910 return FALSE;
911
912 if (!gdi_decode_color(gdi, mem3blt->backColor, &backColor, nullptr))
913 return FALSE;
914
915 originalColor = gdi_SetTextColor(gdi->drawing->hdc, foreColor);
916
917 switch (brush->style)
918 {
919 case GDI_BS_SOLID:
920 originalBrush = gdi->drawing->hdc->brush;
921 gdi->drawing->hdc->brush = gdi_CreateSolidBrush(foreColor);
922
923 if (!gdi->drawing->hdc->brush)
924 {
925 ret = FALSE;
926 goto out_fail;
927 }
928
929 ret = gdi_BitBlt(gdi->drawing->hdc, mem3blt->nLeftRect, mem3blt->nTopRect,
930 mem3blt->nWidth, mem3blt->nHeight, bitmap->hdc, mem3blt->nXSrc,
931 mem3blt->nYSrc, gdi_rop3_code_checked(mem3blt->bRop), &gdi->palette);
932 gdi_DeleteObject((HGDIOBJECT)gdi->drawing->hdc->brush);
933 gdi->drawing->hdc->brush = originalBrush;
934 break;
935
936 case GDI_BS_PATTERN:
937 {
938 HGDI_BITMAP hBmp = nullptr;
939 UINT32 brushFormat = 0;
940 BYTE* data = (BYTE*)winpr_aligned_malloc(
941 8ULL * 8ULL * FreeRDPGetBytesPerPixel(gdi->drawing->hdc->format), 16);
942
943 if (!data)
944 {
945 ret = FALSE;
946 goto out_fail;
947 }
948
949 if (brush->bpp > 1)
950 {
951 UINT32 bpp = brush->bpp;
952
953 const UINT32 ColorDepth =
954 freerdp_settings_get_uint32(gdi->context->settings, FreeRDP_ColorDepth);
955 if ((bpp == 16) && (ColorDepth == 15))
956 bpp = 15;
957
958 brushFormat = gdi_get_pixel_format(bpp);
959
960 if (!freerdp_image_copy_no_overlap(data, gdi->drawing->hdc->format, 0, 0, 0, 8, 8,
961 brush->data, brushFormat, 0, 0, 0, &gdi->palette,
962 FREERDP_FLIP_NONE))
963 {
964 ret = FALSE;
965 winpr_aligned_free(data);
966 goto out_fail;
967 }
968 }
969 else
970 {
971 if (!freerdp_image_copy_from_monochrome(data, gdi->drawing->hdc->format, 0, 0, 0, 8,
972 8, brush->data, backColor, foreColor,
973 &gdi->palette))
974 {
975 ret = FALSE;
976 winpr_aligned_free(data);
977 goto out_fail;
978 }
979 }
980
981 hBmp = gdi_CreateBitmap(8, 8, gdi->drawing->hdc->format, data);
982
983 if (!hBmp)
984 {
985 ret = FALSE;
986 winpr_aligned_free(data);
987 goto out_fail;
988 }
989
990 originalBrush = gdi->drawing->hdc->brush;
991 gdi->drawing->hdc->brush = gdi_CreatePatternBrush(hBmp);
992
993 if (!gdi->drawing->hdc->brush)
994 {
995 gdi_DeleteObject((HGDIOBJECT)hBmp);
996 goto out_fail;
997 }
998
999 gdi->drawing->hdc->brush->nXOrg = WINPR_ASSERTING_INT_CAST(int32_t, brush->x);
1000 gdi->drawing->hdc->brush->nYOrg = WINPR_ASSERTING_INT_CAST(int32_t, brush->y);
1001 ret = gdi_BitBlt(gdi->drawing->hdc, mem3blt->nLeftRect, mem3blt->nTopRect,
1002 mem3blt->nWidth, mem3blt->nHeight, bitmap->hdc, mem3blt->nXSrc,
1003 mem3blt->nYSrc, gdi_rop3_code_checked(mem3blt->bRop), &gdi->palette);
1004 gdi_DeleteObject((HGDIOBJECT)gdi->drawing->hdc->brush);
1005 gdi_DeleteObject((HGDIOBJECT)hBmp);
1006 gdi->drawing->hdc->brush = originalBrush;
1007 }
1008 break;
1009
1010 default:
1011 WLog_ERR(TAG, "Mem3Blt unimplemented brush style:%" PRIu32 "", brush->style);
1012 break;
1013 }
1014
1015out_fail:
1016 gdi_SetTextColor(gdi->drawing->hdc, originalColor);
1017 return ret;
1018}
1019
1020static BOOL gdi_polygon_sc(WINPR_ATTR_UNUSED rdpContext* context,
1021 WINPR_ATTR_UNUSED const POLYGON_SC_ORDER* polygon_sc)
1022{
1023 WLog_WARN(TAG, "not implemented");
1024 return FALSE;
1025}
1026
1027static BOOL gdi_polygon_cb(WINPR_ATTR_UNUSED rdpContext* context,
1028 WINPR_ATTR_UNUSED POLYGON_CB_ORDER* polygon_cb)
1029{
1030 WLog_WARN(TAG, "not implemented");
1031 return FALSE;
1032}
1033
1034static BOOL gdi_ellipse_sc(WINPR_ATTR_UNUSED rdpContext* context,
1035 WINPR_ATTR_UNUSED const ELLIPSE_SC_ORDER* ellipse_sc)
1036{
1037 WLog_WARN(TAG, "not implemented");
1038 return FALSE;
1039}
1040
1041static BOOL gdi_ellipse_cb(WINPR_ATTR_UNUSED rdpContext* context,
1042 WINPR_ATTR_UNUSED const ELLIPSE_CB_ORDER* ellipse_cb)
1043{
1044 WLog_WARN(TAG, "not implemented");
1045 return FALSE;
1046}
1047
1048static BOOL gdi_frame_marker(WINPR_ATTR_UNUSED rdpContext* context,
1049 WINPR_ATTR_UNUSED const FRAME_MARKER_ORDER* frameMarker)
1050{
1051 return TRUE;
1052}
1053
1054static BOOL gdi_surface_frame_marker(rdpContext* context,
1055 const SURFACE_FRAME_MARKER* surfaceFrameMarker)
1056{
1057 WINPR_ASSERT(context);
1058 WINPR_ASSERT(context->gdi);
1059 WINPR_ASSERT(context->update);
1060 WINPR_ASSERT(surfaceFrameMarker);
1061
1062 WLog_Print(context->gdi->log, WLOG_DEBUG, "frameId %" PRIu32 " frameAction %" PRIu32 "",
1063 surfaceFrameMarker->frameId, surfaceFrameMarker->frameAction);
1064
1065 switch (surfaceFrameMarker->frameAction)
1066 {
1067 case SURFACECMD_FRAMEACTION_BEGIN:
1068 break;
1069
1070 case SURFACECMD_FRAMEACTION_END:
1071 if (freerdp_settings_get_uint32(context->settings, FreeRDP_FrameAcknowledge) > 0)
1072 {
1073 if (!IFCALLRESULT(TRUE, context->update->SurfaceFrameAcknowledge, context,
1074 surfaceFrameMarker->frameId))
1075 return FALSE;
1076 }
1077
1078 break;
1079 default:
1080 break;
1081 }
1082
1083 return TRUE;
1084}
1085
1086static BOOL intersect_rect(const rdpGdi* gdi, const SURFACE_BITS_COMMAND* cmd, RECTANGLE_16* prect)
1087{
1088 WINPR_ASSERT(gdi);
1089 WINPR_ASSERT(cmd);
1090 WINPR_ASSERT(prect);
1091
1092 const UINT32 w = (const UINT32)gdi->width;
1093 const UINT32 h = (const UINT32)gdi->height;
1094
1095 if (cmd->destLeft > w)
1096 return FALSE;
1097 if (cmd->destRight > w)
1098 return FALSE;
1099 if (cmd->destLeft > cmd->destRight)
1100 return FALSE;
1101 if (cmd->destRight > UINT16_MAX)
1102 return FALSE;
1103
1104 if (cmd->destTop > h)
1105 return FALSE;
1106 if (cmd->destBottom > h)
1107 return FALSE;
1108 if (cmd->destTop > cmd->destBottom)
1109 return FALSE;
1110 if (cmd->destBottom > UINT16_MAX)
1111 return FALSE;
1112
1113 prect->left = (const UINT16)cmd->destLeft;
1114 prect->top = (const UINT16)cmd->destTop;
1115 prect->right = MIN((UINT16)cmd->destRight, prect->left + cmd->bmp.width);
1116 prect->bottom = MIN((UINT16)cmd->destBottom, prect->top + cmd->bmp.height);
1117 return TRUE;
1118}
1119
1120static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cmd)
1121{
1122 BOOL result = FALSE;
1123 DWORD format = 0;
1124 size_t size = 0;
1125 REGION16 region;
1126 RECTANGLE_16 cmdRect = WINPR_C_ARRAY_INIT;
1127
1128 if (!context || !cmd)
1129 return FALSE;
1130
1131 rdpGdi* gdi = context->gdi;
1132 if (!gdi)
1133 return FALSE;
1134
1135 WLog_Print(gdi->log, WLOG_DEBUG,
1136 "destLeft %" PRIu32 " destTop %" PRIu32 " destRight %" PRIu32 " destBottom %" PRIu32
1137 " "
1138 "bpp %" PRIu8 " flags %" PRIx8 " codecID %s [0x%04" PRIu16 "] width %" PRIu16
1139 " height %" PRIu16 " length %" PRIu32 "",
1140 cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom, cmd->bmp.bpp,
1141 cmd->bmp.flags, freerdp_codec_id_to_str(cmd->bmp.codecID), cmd->bmp.codecID,
1142 cmd->bmp.width, cmd->bmp.height, cmd->bmp.bitmapDataLength);
1143 region16_init(&region);
1144
1145 if (!intersect_rect(gdi, cmd, &cmdRect))
1146 goto out;
1147
1148 switch (cmd->bmp.codecID)
1149 {
1150 case RDP_CODEC_ID_REMOTEFX:
1151 case RDP_CODEC_ID_IMAGE_REMOTEFX:
1152 if (!rfx_process_message(context->codecs->rfx, cmd->bmp.bitmapData,
1153 cmd->bmp.bitmapDataLength, cmdRect.left, cmdRect.top,
1154 gdi->primary_buffer, gdi->dstFormat, gdi->stride,
1155 WINPR_ASSERTING_INT_CAST(uint32_t, gdi->height), &region))
1156 {
1157 WLog_ERR(TAG, "Failed to process RemoteFX message");
1158 goto out;
1159 }
1160
1161 break;
1162
1163 case RDP_CODEC_ID_NSCODEC:
1164 format = gdi->dstFormat;
1165
1166 if (!nsc_process_message(
1167 context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width, cmd->bmp.height,
1168 cmd->bmp.bitmapData, cmd->bmp.bitmapDataLength, gdi->primary_buffer, format,
1169 gdi->stride, cmdRect.left, cmdRect.top,
1170 WINPR_ASSERTING_INT_CAST(UINT32, gdi->width),
1171 WINPR_ASSERTING_INT_CAST(UINT32, gdi->height), FREERDP_FLIP_VERTICAL))
1172 {
1173 WLog_ERR(TAG, "Failed to process NSCodec message");
1174 goto out;
1175 }
1176
1177 if (!region16_union_rect(&region, &region, &cmdRect))
1178 goto out;
1179 break;
1180
1181 case RDP_CODEC_ID_NONE:
1182 format = gdi_get_pixel_format(cmd->bmp.bpp);
1183 size = 1ull * cmd->bmp.width * cmd->bmp.height * FreeRDPGetBytesPerPixel(format);
1184 if (size > cmd->bmp.bitmapDataLength)
1185 {
1186 WLog_ERR(TAG, "Short nocodec message: got %" PRIu32 " bytes, require %" PRIuz,
1187 cmd->bmp.bitmapDataLength, size);
1188 goto out;
1189 }
1190
1191 if (!freerdp_image_copy_no_overlap(
1192 gdi->primary_buffer, gdi->dstFormat, gdi->stride, cmdRect.left, cmdRect.top,
1193 cmdRect.right - cmdRect.left, cmdRect.bottom - cmdRect.top, cmd->bmp.bitmapData,
1194 format, 0, 0, 0, &gdi->palette, FREERDP_FLIP_VERTICAL))
1195 {
1196 WLog_ERR(TAG, "Failed to process nocodec message");
1197 goto out;
1198 }
1199
1200 if (!region16_union_rect(&region, &region, &cmdRect))
1201 goto out;
1202 break;
1203
1204 default:
1205 WLog_ERR(TAG, "Unsupported codecID %" PRIu32 "", cmd->bmp.codecID);
1206 break;
1207 }
1208
1209 {
1210 UINT32 nbRects = 0;
1211 const RECTANGLE_16* rects = region16_rects(&region, &nbRects);
1212 if (!rects && (nbRects > 0))
1213 goto out;
1214
1215 if (nbRects == 0)
1216 {
1217 const int32_t w = cmdRect.right - cmdRect.left;
1218 const int32_t h = cmdRect.bottom - cmdRect.top;
1219 if (!gdi_InvalidateRegion(gdi->primary->hdc, cmdRect.left, cmdRect.top, w, h))
1220 goto out;
1221 }
1222 for (UINT32 i = 0; i < nbRects; i++)
1223 {
1224 const RECTANGLE_16* rect = &rects[i];
1225
1226 UINT32 left = rect->left;
1227 UINT32 top = rect->top;
1228 UINT32 width = rect->right - rect->left;
1229 UINT32 height = rect->bottom - rect->top;
1230
1231 if (!gdi_InvalidateRegion(gdi->primary->hdc, WINPR_ASSERTING_INT_CAST(int32_t, left),
1232 WINPR_ASSERTING_INT_CAST(int32_t, top),
1233 WINPR_ASSERTING_INT_CAST(int32_t, width),
1234 WINPR_ASSERTING_INT_CAST(int32_t, height)))
1235 {
1236 WLog_ERR(TAG, "Failed to update invalid region");
1237 goto out;
1238 }
1239 }
1240 }
1241 result = TRUE;
1242out:
1243 region16_uninit(&region);
1244 return result;
1245}
1246
1252static void gdi_register_update_callbacks(rdpUpdate* update)
1253{
1254 WINPR_ASSERT(update);
1255 WINPR_ASSERT(update->context);
1256
1257 const rdpSettings* settings = update->context->settings;
1258 WINPR_ASSERT(settings);
1259
1260 rdpPrimaryUpdate* primary = update->primary;
1261 WINPR_ASSERT(primary);
1262
1263 if (freerdp_settings_get_bool(settings, FreeRDP_DeactivateClientDecoding))
1264 return;
1265 update->Palette = gdi_palette_update;
1266 update->SetBounds = gdi_set_bounds;
1267 primary->DstBlt = gdi_dstblt;
1268 primary->PatBlt = gdi_patblt;
1269 primary->ScrBlt = gdi_scrblt;
1270 primary->OpaqueRect = gdi_opaque_rect;
1271 primary->DrawNineGrid = nullptr;
1272 primary->MultiDstBlt = nullptr;
1273 primary->MultiPatBlt = nullptr;
1274 primary->MultiScrBlt = nullptr;
1275 primary->MultiOpaqueRect = gdi_multi_opaque_rect;
1276 primary->MultiDrawNineGrid = nullptr;
1277 primary->LineTo = gdi_line_to;
1278 primary->Polyline = gdi_polyline;
1279 primary->MemBlt = gdi_memblt;
1280 primary->Mem3Blt = gdi_mem3blt;
1281 primary->SaveBitmap = nullptr;
1282 primary->GlyphIndex = nullptr;
1283 primary->FastIndex = nullptr;
1284 primary->FastGlyph = nullptr;
1285 primary->PolygonSC = gdi_polygon_sc;
1286 primary->PolygonCB = gdi_polygon_cb;
1287 primary->EllipseSC = gdi_ellipse_sc;
1288 primary->EllipseCB = gdi_ellipse_cb;
1289 update->SurfaceBits = gdi_surface_bits;
1290 update->SurfaceFrameMarker = gdi_surface_frame_marker;
1291 update->altsec->FrameMarker = gdi_frame_marker;
1292}
1293
1294static BOOL gdi_init_primary(rdpGdi* gdi, UINT32 stride, UINT32 format, BYTE* buffer,
1295 void (*pfree)(void*), BOOL isLocked)
1296{
1297 WINPR_ASSERT(gdi);
1298 WINPR_ASSERT(gdi->context);
1299 WINPR_ASSERT(gdi->context->update);
1300 if (!isLocked)
1301 rdp_update_lock(gdi->context->update);
1302
1303 gdi->primary = (gdiBitmap*)calloc(1, sizeof(gdiBitmap));
1304
1305 if (format > 0)
1306 gdi->dstFormat = format;
1307
1308 if (stride > 0)
1309 gdi->stride = stride;
1310 else
1311 gdi->stride = WINPR_ASSERTING_INT_CAST(uint32_t, gdi->width) *
1312 FreeRDPGetBytesPerPixel(gdi->dstFormat);
1313
1314 if (!gdi->primary)
1315 goto fail_primary;
1316
1317 if (!(gdi->primary->hdc = gdi_CreateCompatibleDC(gdi->hdc)))
1318 goto fail_hdc;
1319
1320 if (!buffer)
1321 {
1322 gdi->primary->bitmap =
1323 gdi_CreateCompatibleBitmap(gdi->hdc, WINPR_ASSERTING_INT_CAST(uint32_t, gdi->width),
1324 WINPR_ASSERTING_INT_CAST(uint32_t, gdi->height));
1325 }
1326 else
1327 {
1328 gdi->primary->bitmap = gdi_CreateBitmapEx(WINPR_ASSERTING_INT_CAST(uint32_t, gdi->width),
1329 WINPR_ASSERTING_INT_CAST(uint32_t, gdi->height),
1330 gdi->dstFormat, gdi->stride, buffer, pfree);
1331 }
1332
1333 if (!gdi->primary->bitmap)
1334 goto fail_bitmap;
1335
1336 gdi->stride = gdi->primary->bitmap->scanline;
1337 gdi_SelectObject(gdi->primary->hdc, (HGDIOBJECT)gdi->primary->bitmap);
1338 gdi->primary->org_bitmap = nullptr;
1339 gdi->primary_buffer = gdi->primary->bitmap->data;
1340
1341 if (!(gdi->primary->hdc->hwnd = (HGDI_WND)calloc(1, sizeof(GDI_WND))))
1342 goto fail_hwnd;
1343
1344 if (!(gdi->primary->hdc->hwnd->invalid = gdi_CreateRectRgn(0, 0, 0, 0)))
1345 goto fail_hwnd;
1346
1347 gdi->primary->hdc->hwnd->invalid->null = TRUE;
1348 gdi->primary->hdc->hwnd->count = 32;
1349
1350 if (!(gdi->primary->hdc->hwnd->cinvalid =
1351 (GDI_RGN*)calloc(gdi->primary->hdc->hwnd->count, sizeof(GDI_RGN))))
1352 goto fail_hwnd;
1353
1354 gdi->primary->hdc->hwnd->ninvalid = 0;
1355
1356 if (!gdi->drawing)
1357 gdi->drawing = gdi->primary;
1358
1359 rdp_update_unlock(gdi->context->update);
1360 return TRUE;
1361fail_hwnd:
1362 gdi_DeleteObject((HGDIOBJECT)gdi->primary->bitmap);
1363fail_bitmap:
1364 gdi_DeleteDC(gdi->primary->hdc);
1365fail_hdc:
1366 free(gdi->primary);
1367 gdi->primary = nullptr;
1368fail_primary:
1369 rdp_update_unlock(gdi->context->update);
1370 return FALSE;
1371}
1372
1373BOOL gdi_resize(rdpGdi* gdi, UINT32 width, UINT32 height)
1374{
1375 return gdi_resize_ex(gdi, width, height, 0, 0, nullptr, nullptr);
1376}
1377
1378BOOL gdi_resize_ex(rdpGdi* gdi, UINT32 width, UINT32 height, UINT32 stride, UINT32 format,
1379 BYTE* buffer, void (*pfree)(void*))
1380{
1381 if (!gdi || !gdi->primary)
1382 return FALSE;
1383
1384 if ((width > INT32_MAX) || (height > INT32_MAX))
1385 return FALSE;
1386
1387 if ((gdi->width == (INT32)width) && (gdi->height == (INT32)height) &&
1388 (!buffer || (gdi->primary_buffer == buffer)))
1389 return TRUE;
1390
1391 WINPR_ASSERT(gdi->context);
1392 WINPR_ASSERT(gdi->context->update);
1393
1394 /* EndPaint might not have been called, ensure the update lock is released */
1395 if (!update_end_paint(gdi->context->update))
1396 return FALSE;
1397 rdp_update_lock(gdi->context->update);
1398
1399 if (gdi->drawing == gdi->primary)
1400 gdi->drawing = nullptr;
1401
1402 gdi->width = (INT32)width;
1403 gdi->height = (INT32)height;
1404 gdi_bitmap_free_ex(gdi->primary);
1405 gdi->primary = nullptr;
1406 gdi->primary_buffer = nullptr;
1407 return gdi_init_primary(gdi, stride, format, buffer, pfree, TRUE);
1408}
1409
1417BOOL gdi_init(freerdp* instance, UINT32 format)
1418{
1419 return gdi_init_ex(instance, format, 0, nullptr, winpr_aligned_free);
1420}
1421
1433BOOL gdi_init_ex(freerdp* instance, UINT32 format, UINT32 stride, BYTE* buffer,
1434 void (*pfree)(void*))
1435{
1436 rdpContext* context = nullptr;
1437 UINT32 SrcFormat = 0;
1438 rdpGdi* gdi = nullptr;
1439
1440 WINPR_ASSERT(instance);
1441
1442 context = instance->context;
1443 WINPR_ASSERT(context);
1444 WINPR_ASSERT(context->settings);
1445
1446 const UINT32 ColorDepth = freerdp_settings_get_uint32(context->settings, FreeRDP_ColorDepth);
1447 SrcFormat = gdi_get_pixel_format(ColorDepth);
1448 gdi = (rdpGdi*)calloc(1, sizeof(rdpGdi));
1449
1450 if (!gdi)
1451 goto fail;
1452
1453 context->gdi = gdi;
1454 gdi->log = WLog_Get(TAG);
1455
1456 if (!gdi->log)
1457 goto fail;
1458
1459 gdi->context = context;
1460 gdi->width = WINPR_ASSERTING_INT_CAST(
1461 int32_t, freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth));
1462 gdi->height = WINPR_ASSERTING_INT_CAST(
1463 int32_t, freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopHeight));
1464 gdi->dstFormat = format;
1465 /* default internal buffer format */
1466 WLog_Print(gdi->log, WLOG_INFO, "Local framebuffer format %s",
1467 FreeRDPGetColorFormatName(gdi->dstFormat));
1468 WLog_Print(gdi->log, WLOG_INFO, "Remote framebuffer format %s",
1469 FreeRDPGetColorFormatName(SrcFormat));
1470
1471 if (!(gdi->hdc = gdi_GetDC()))
1472 goto fail;
1473
1474 gdi->hdc->format = gdi->dstFormat;
1475
1476 if (!gdi_init_primary(gdi, stride, gdi->dstFormat, buffer, pfree, FALSE))
1477 goto fail;
1478
1479 if (!(context->cache = cache_new(context)))
1480 goto fail;
1481
1482 gdi_register_update_callbacks(context->update);
1483 brush_cache_register_callbacks(context->update);
1484 glyph_cache_register_callbacks(context->update);
1485 bitmap_cache_register_callbacks(context->update);
1486 offscreen_cache_register_callbacks(context->update);
1487 palette_cache_register_callbacks(context->update);
1488
1489 if (!gdi_register_graphics(context->graphics))
1490 goto fail;
1491
1492 return TRUE;
1493fail:
1494 gdi_free(instance);
1495 WLog_ERR(TAG, "failed to initialize gdi");
1496 return FALSE;
1497}
1498
1499void gdi_free(freerdp* instance)
1500{
1501 rdpGdi* gdi = nullptr;
1502 rdpContext* context = nullptr;
1503
1504 if (!instance || !instance->context)
1505 return;
1506
1507 gdi = instance->context->gdi;
1508
1509 if (gdi)
1510 {
1511 gdi_bitmap_free_ex(gdi->primary);
1512 gdi_DeleteDC(gdi->hdc);
1513 free(gdi);
1514 }
1515
1516 context = instance->context;
1517 cache_free(context->cache);
1518 context->cache = nullptr;
1519 instance->context->gdi = (rdpGdi*)nullptr;
1520}
1521
1522BOOL gdi_send_suppress_output(rdpGdi* gdi, BOOL suppress)
1523{
1524 if (!gdi || !gdi->context)
1525 return FALSE;
1526
1527 if (gdi->suppressOutput == suppress)
1528 return TRUE;
1529
1530 gdi->suppressOutput = suppress;
1531
1532 rdpContext* context = gdi->context;
1533 rdpSettings* settings = context->settings;
1534 WINPR_ASSERT(settings);
1535
1536 rdpUpdate* update = context->update;
1537 WINPR_ASSERT(update);
1538
1539 const UINT32 w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
1540 const UINT32 h = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
1541
1542 const RECTANGLE_16 rect = { .left = 0,
1543 .top = 0,
1544 .right = WINPR_ASSERTING_INT_CAST(UINT16, w),
1545 .bottom = WINPR_ASSERTING_INT_CAST(UINT16, h) };
1546
1547 WINPR_ASSERT(update->SuppressOutput);
1548 return update->SuppressOutput(context, !suppress, &rect);
1549}
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.
Definition types.h:82