FreeRDP
Loading...
Searching...
No Matches
gdi/graphics.c
1
22#include <freerdp/config.h>
23
24#include <winpr/crt.h>
25
26#include <freerdp/log.h>
27#include <freerdp/freerdp.h>
28#include <freerdp/gdi/dc.h>
29#include <freerdp/gdi/shape.h>
30#include <freerdp/gdi/region.h>
31#include <freerdp/gdi/bitmap.h>
32
33#include "clipping.h"
34#include "drawing.h"
35#include "brush.h"
36#include "graphics.h"
37
38#define TAG FREERDP_TAG("gdi")
39/* Bitmap Class */
40
41HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, UINT32 nWidth, UINT32 nHeight, UINT32 SrcFormat,
42 BYTE* data)
43{
44 UINT32 nSrcStep = 0;
45 UINT32 nDstStep = 0;
46 BYTE* pSrcData = nullptr;
47 BYTE* pDstData = nullptr;
48 HGDI_BITMAP bitmap = nullptr;
49
50 if (!gdi)
51 return nullptr;
52
53 nDstStep = nWidth * FreeRDPGetBytesPerPixel(gdi->dstFormat);
54 pDstData = winpr_aligned_malloc(1ull * nHeight * nDstStep, 16);
55
56 if (!pDstData)
57 return nullptr;
58
59 pSrcData = data;
60 nSrcStep = nWidth * FreeRDPGetBytesPerPixel(SrcFormat);
61
62 if (!freerdp_image_copy_no_overlap(pDstData, gdi->dstFormat, nDstStep, 0, 0, nWidth, nHeight,
63 pSrcData, SrcFormat, nSrcStep, 0, 0, &gdi->palette,
64 FREERDP_FLIP_NONE))
65 {
66 winpr_aligned_free(pDstData);
67 return nullptr;
68 }
69
70 bitmap = gdi_CreateBitmap(nWidth, nHeight, gdi->dstFormat, pDstData);
71 return bitmap;
72}
73
74static BOOL gdi_Bitmap_New(rdpContext* context, rdpBitmap* bitmap)
75{
76 gdiBitmap* gdi_bitmap = nullptr;
77 rdpGdi* gdi = context->gdi;
78 gdi_bitmap = (gdiBitmap*)bitmap;
79 gdi_bitmap->hdc = gdi_CreateCompatibleDC(gdi->hdc);
80
81 if (!gdi_bitmap->hdc)
82 return FALSE;
83
84 if (!bitmap->data)
85 gdi_bitmap->bitmap = gdi_CreateCompatibleBitmap(gdi->hdc, bitmap->width, bitmap->height);
86 else
87 {
88 UINT32 format = bitmap->format;
89 gdi_bitmap->bitmap =
90 gdi_create_bitmap(gdi, bitmap->width, bitmap->height, format, bitmap->data);
91 }
92
93 if (!gdi_bitmap->bitmap)
94 {
95 gdi_DeleteDC(gdi_bitmap->hdc);
96 gdi_bitmap->hdc = nullptr;
97 return FALSE;
98 }
99
100 gdi_bitmap->hdc->format = gdi_bitmap->bitmap->format;
101 gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->bitmap);
102 gdi_bitmap->org_bitmap = nullptr;
103 return TRUE;
104}
105
106static void gdi_Bitmap_Free(WINPR_ATTR_UNUSED rdpContext* context, rdpBitmap* bitmap)
107{
108 gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
109
110 if (gdi_bitmap)
111 {
112 if (gdi_bitmap->hdc)
113 gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->org_bitmap);
114
115 gdi_DeleteObject((HGDIOBJECT)gdi_bitmap->bitmap);
116 gdi_DeleteDC(gdi_bitmap->hdc);
117 winpr_aligned_free(bitmap->data);
118 }
119
120 free(bitmap);
121}
122
123static BOOL gdi_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap)
124{
125 gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
126 UINT32 width = bitmap->right - bitmap->left + 1;
127 UINT32 height = bitmap->bottom - bitmap->top + 1;
128 return gdi_BitBlt(context->gdi->primary->hdc, WINPR_ASSERTING_INT_CAST(int, bitmap->left),
129 WINPR_ASSERTING_INT_CAST(int, bitmap->top),
130 WINPR_ASSERTING_INT_CAST(int, width), WINPR_ASSERTING_INT_CAST(int, height),
131 gdi_bitmap->hdc, 0, 0, GDI_SRCCOPY, &context->gdi->palette);
132}
133
134static BOOL gdi_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, const BYTE* pSrcData,
135 UINT32 DstWidth, UINT32 DstHeight, UINT32 bpp, UINT32 length,
136 BOOL compressed, UINT32 codecId)
137{
138 UINT32 SrcSize = length;
139 rdpGdi* gdi = context->gdi;
140 UINT32 size = DstWidth * DstHeight;
141 bitmap->compressed = FALSE;
142 bitmap->format = gdi->dstFormat;
143
144 if ((FreeRDPGetBytesPerPixel(bitmap->format) == 0) || (DstWidth == 0) || (DstHeight == 0) ||
145 (DstWidth > UINT32_MAX / DstHeight) ||
146 (size > (UINT32_MAX / FreeRDPGetBytesPerPixel(bitmap->format))))
147 {
148 WLog_ERR(TAG, "invalid input data");
149 return FALSE;
150 }
151
152 size *= FreeRDPGetBytesPerPixel(bitmap->format);
153 bitmap->length = size;
154 bitmap->data = (BYTE*)winpr_aligned_malloc(bitmap->length, 16);
155
156 if (!bitmap->data)
157 return FALSE;
158
159 if (compressed)
160 {
161 if ((codecId == RDP_CODEC_ID_REMOTEFX) || (codecId == RDP_CODEC_ID_IMAGE_REMOTEFX))
162 {
163 REGION16 invalidRegion = WINPR_C_ARRAY_INIT;
164 region16_init(&invalidRegion);
165
166 const BOOL rc =
167 rfx_process_message(context->codecs->rfx, pSrcData, SrcSize, bitmap->left,
168 bitmap->top, bitmap->data, bitmap->format, gdi->stride,
169 WINPR_ASSERTING_INT_CAST(UINT32, gdi->height), &invalidRegion);
170 region16_uninit(&invalidRegion);
171
172 if (!rc)
173 {
174 WLog_ERR(TAG, "rfx_process_message failed");
175 return FALSE;
176 }
177 }
178 else if (codecId == RDP_CODEC_ID_NSCODEC)
179 {
180 const int status = nsc_process_message(
181 context->codecs->nsc, 32, DstWidth, DstHeight, pSrcData, SrcSize, bitmap->data,
182 bitmap->format, 0, 0, 0, DstWidth, DstHeight, FREERDP_FLIP_VERTICAL);
183
184 if (status < 1)
185 {
186 WLog_ERR(TAG, "nsc_process_message failed");
187 return FALSE;
188 }
189
190 return freerdp_image_copy_no_overlap(bitmap->data, bitmap->format, 0, 0, 0, DstWidth,
191 DstHeight, pSrcData, PIXEL_FORMAT_XRGB32, 0, 0, 0,
192 &gdi->palette, FREERDP_FLIP_VERTICAL);
193 }
194 else if (bpp < 32)
195 {
196 if (!interleaved_decompress(context->codecs->interleaved, pSrcData, SrcSize, DstWidth,
197 DstHeight, bpp, bitmap->data, bitmap->format, 0, 0, 0,
198 DstWidth, DstHeight, &gdi->palette))
199 {
200 WLog_ERR(TAG, "interleaved_decompress failed");
201 return FALSE;
202 }
203 }
204 else
205 {
206 const BOOL fidelity =
207 freerdp_settings_get_bool(context->settings, FreeRDP_DrawAllowDynamicColorFidelity);
208 freerdp_planar_switch_bgr(context->codecs->planar, fidelity);
209 if (!freerdp_bitmap_decompress_planar(context->codecs->planar, pSrcData, SrcSize,
210 DstWidth, DstHeight, bitmap->data, bitmap->format,
211 0, 0, 0, DstWidth, DstHeight, TRUE))
212 {
213 WLog_ERR(TAG, "freerdp_bitmap_decompress_planar failed");
214 return FALSE;
215 }
216 }
217 }
218 else
219 {
220 const UINT32 SrcFormat = gdi_get_pixel_format(bpp);
221 const size_t sbpp = FreeRDPGetBytesPerPixel(SrcFormat);
222 const size_t dbpp = FreeRDPGetBytesPerPixel(bitmap->format);
223
224 if ((sbpp == 0) || (dbpp == 0))
225 return FALSE;
226 else
227 {
228 const size_t dstSize = SrcSize * dbpp / sbpp;
229
230 if (dstSize < bitmap->length)
231 {
232 WLog_ERR(TAG, "dstSize %" PRIuz " < bitmap->length %" PRIu32, dstSize,
233 bitmap->length);
234 return FALSE;
235 }
236 }
237
238 if (!freerdp_image_copy_no_overlap(bitmap->data, bitmap->format, 0, 0, 0, DstWidth,
239 DstHeight, pSrcData, SrcFormat, 0, 0, 0, &gdi->palette,
240 FREERDP_FLIP_VERTICAL))
241 {
242 WLog_ERR(TAG, "freerdp_image_copy failed");
243 return FALSE;
244 }
245 }
246
247 return TRUE;
248}
249
250static BOOL gdi_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary)
251{
252 rdpGdi* gdi = nullptr;
253
254 if (!context)
255 return FALSE;
256
257 gdi = context->gdi;
258
259 if (!gdi)
260 return FALSE;
261
262 if (primary)
263 gdi->drawing = gdi->primary;
264 else
265 gdi->drawing = (gdiBitmap*)bitmap;
266
267 return TRUE;
268}
269
270/* Glyph Class */
271static BOOL gdi_Glyph_New(rdpContext* context, rdpGlyph* glyph)
272{
273 if (!context || !glyph)
274 return FALSE;
275
276 gdiGlyph* gdi_glyph = (gdiGlyph*)glyph;
277 gdi_glyph->hdc = gdi_GetDC();
278
279 if (!gdi_glyph->hdc)
280 return FALSE;
281
282 gdi_glyph->hdc->format = PIXEL_FORMAT_MONO;
283 BYTE* data = freerdp_glyph_convert_ex(glyph->cx, glyph->cy, glyph->aj, glyph->cb);
284
285 if (!data)
286 {
287 gdi_DeleteDC(gdi_glyph->hdc);
288 return FALSE;
289 }
290
291 gdi_glyph->bitmap = gdi_CreateBitmap(glyph->cx, glyph->cy, PIXEL_FORMAT_MONO, data);
292
293 if (!gdi_glyph->bitmap)
294 {
295 gdi_DeleteDC(gdi_glyph->hdc);
296 winpr_aligned_free(data);
297 return FALSE;
298 }
299
300 gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->bitmap);
301 gdi_glyph->org_bitmap = nullptr;
302 return TRUE;
303}
304
305static void gdi_Glyph_Free(WINPR_ATTR_UNUSED rdpContext* context, rdpGlyph* glyph)
306{
307 gdiGlyph* gdi_glyph = nullptr;
308 gdi_glyph = (gdiGlyph*)glyph;
309
310 if (gdi_glyph)
311 {
312 gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->org_bitmap);
313 gdi_DeleteObject((HGDIOBJECT)gdi_glyph->bitmap);
314 gdi_DeleteDC(gdi_glyph->hdc);
315 free(glyph->aj);
316 free(glyph);
317 }
318}
319
320static BOOL gdi_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, INT32 x, INT32 y, INT32 w,
321 INT32 h, INT32 sx, INT32 sy, BOOL fOpRedundant)
322{
323 const gdiGlyph* gdi_glyph = nullptr;
324 rdpGdi* gdi = nullptr;
325 HGDI_BRUSH brush = nullptr;
326 BOOL rc = FALSE;
327
328 if (!context || !glyph)
329 return FALSE;
330
331 gdi = context->gdi;
332 gdi_glyph = (const gdiGlyph*)glyph;
333
334 if (!fOpRedundant)
335 {
336 GDI_RECT rect = WINPR_C_ARRAY_INIT;
337
338 if (x > 0)
339 rect.left = x;
340
341 if (y > 0)
342 rect.top = y;
343
344 if (x + w > 0)
345 rect.right = x + w - 1;
346
347 if (y + h > 0)
348 rect.bottom = y + h - 1;
349
350 if ((rect.left < rect.right) && (rect.top < rect.bottom))
351 {
352 brush = gdi_CreateSolidBrush(gdi->drawing->hdc->bkColor);
353
354 if (!brush)
355 return FALSE;
356
357 const BOOL res = gdi_FillRect(gdi->drawing->hdc, &rect, brush);
358 gdi_DeleteObject((HGDIOBJECT)brush);
359 if (!res)
360 return res;
361 }
362 }
363
364 brush = gdi_CreateSolidBrush(gdi->drawing->hdc->textColor);
365
366 if (!brush)
367 return FALSE;
368
369 gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)brush);
370 rc = gdi_BitBlt(gdi->drawing->hdc, x, y, w, h, gdi_glyph->hdc, sx, sy, GDI_GLYPH_ORDER,
371 &context->gdi->palette);
372 gdi_DeleteObject((HGDIOBJECT)brush);
373 return rc;
374}
375
376static BOOL gdi_Glyph_BeginDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height,
377 UINT32 bgcolor, UINT32 fgcolor, BOOL fOpRedundant)
378{
379 if (!context || !context->gdi)
380 return FALSE;
381
382 rdpGdi* gdi = context->gdi;
383
384 if (!gdi->drawing || !gdi->drawing->hdc)
385 return FALSE;
386
387 if (!fOpRedundant)
388 {
389 if (!gdi_decode_color(gdi, bgcolor, &bgcolor, nullptr))
390 return FALSE;
391
392 if (!gdi_decode_color(gdi, fgcolor, &fgcolor, nullptr))
393 return FALSE;
394
395 if (!gdi_SetClipRgn(gdi->drawing->hdc, x, y, width, height))
396 return FALSE;
397
398 gdi_SetTextColor(gdi->drawing->hdc, bgcolor);
399 gdi_SetBkColor(gdi->drawing->hdc, fgcolor);
400
401 {
402 GDI_RECT rect = WINPR_C_ARRAY_INIT;
403 HGDI_BRUSH brush = gdi_CreateSolidBrush(fgcolor);
404
405 if (!brush)
406 return FALSE;
407
408 if (x > 0)
409 rect.left = x;
410
411 if (y > 0)
412 rect.top = y;
413
414 rect.right = x + width - 1;
415 rect.bottom = y + height - 1;
416
417 BOOL res = TRUE;
418 if ((x + width > rect.left) && (y + height > rect.top))
419 res = gdi_FillRect(gdi->drawing->hdc, &rect, brush);
420
421 gdi_DeleteObject((HGDIOBJECT)brush);
422 if (!res)
423 return FALSE;
424 }
425
426 return gdi_SetNullClipRgn(gdi->drawing->hdc);
427 }
428
429 return TRUE;
430}
431
432static BOOL gdi_Glyph_EndDraw(rdpContext* context, WINPR_ATTR_UNUSED INT32 x,
433 WINPR_ATTR_UNUSED INT32 y, WINPR_ATTR_UNUSED INT32 width,
434 WINPR_ATTR_UNUSED INT32 height, WINPR_ATTR_UNUSED UINT32 bgcolor,
435 WINPR_ATTR_UNUSED UINT32 fgcolor)
436{
437 rdpGdi* gdi = nullptr;
438
439 if (!context || !context->gdi)
440 return FALSE;
441
442 gdi = context->gdi;
443
444 if (!gdi->drawing || !gdi->drawing->hdc)
445 return FALSE;
446
447 return gdi_SetNullClipRgn(gdi->drawing->hdc);
448}
449
450/* Graphics Module */
451BOOL gdi_register_graphics(rdpGraphics* graphics)
452{
453 rdpBitmap bitmap = WINPR_C_ARRAY_INIT;
454 rdpGlyph glyph = WINPR_C_ARRAY_INIT;
455 bitmap.size = sizeof(gdiBitmap);
456 bitmap.New = gdi_Bitmap_New;
457 bitmap.Free = gdi_Bitmap_Free;
458 bitmap.Paint = gdi_Bitmap_Paint;
459 bitmap.Decompress = gdi_Bitmap_Decompress;
460 bitmap.SetSurface = gdi_Bitmap_SetSurface;
461 graphics_register_bitmap(graphics, &bitmap);
462 glyph.size = sizeof(gdiGlyph);
463 glyph.New = gdi_Glyph_New;
464 glyph.Free = gdi_Glyph_Free;
465 glyph.Draw = gdi_Glyph_Draw;
466 glyph.BeginDraw = gdi_Glyph_BeginDraw;
467 glyph.EndDraw = gdi_Glyph_EndDraw;
468 graphics_register_glyph(graphics, &glyph);
469 return TRUE;
470}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.