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