Coverage Report

Created: 2026-09-01 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/gdi/graphics.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Graphical Objects
4
 *
5
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2016 Armin Novak <armin.novak@thincast.com>
7
 * Copyright 2016 Thincast Technologies GmbH
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
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
41
HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, UINT32 nWidth, UINT32 nHeight, UINT32 SrcFormat,
42
                              BYTE* data)
43
0
{
44
0
  UINT32 nSrcStep = 0;
45
0
  UINT32 nDstStep = 0;
46
0
  BYTE* pSrcData = nullptr;
47
0
  BYTE* pDstData = nullptr;
48
0
  HGDI_BITMAP bitmap = nullptr;
49
50
0
  if (!gdi)
51
0
    return nullptr;
52
53
0
  nDstStep = nWidth * FreeRDPGetBytesPerPixel(gdi->dstFormat);
54
0
  pDstData = winpr_aligned_malloc(1ull * nHeight * nDstStep, 16);
55
56
0
  if (!pDstData)
57
0
    return nullptr;
58
59
0
  pSrcData = data;
60
0
  nSrcStep = nWidth * FreeRDPGetBytesPerPixel(SrcFormat);
61
62
0
  if (!freerdp_image_copy_no_overlap(pDstData, gdi->dstFormat, nDstStep, 0, 0, nWidth, nHeight,
63
0
                                     pSrcData, SrcFormat, nSrcStep, 0, 0, &gdi->palette,
64
0
                                     FREERDP_FLIP_NONE))
65
0
  {
66
0
    winpr_aligned_free(pDstData);
67
0
    return nullptr;
68
0
  }
69
70
0
  bitmap = gdi_CreateBitmap(nWidth, nHeight, gdi->dstFormat, pDstData);
71
0
  if (!bitmap)
72
0
    winpr_aligned_free(pDstData);
73
0
  return bitmap;
74
0
}
75
76
static BOOL gdi_Bitmap_New(rdpContext* context, rdpBitmap* bitmap)
77
0
{
78
0
  gdiBitmap* gdi_bitmap = nullptr;
79
0
  rdpGdi* gdi = context->gdi;
80
0
  gdi_bitmap = (gdiBitmap*)bitmap;
81
0
  gdi_bitmap->hdc = gdi_CreateCompatibleDC(gdi->hdc);
82
83
0
  if (!gdi_bitmap->hdc)
84
0
    return FALSE;
85
86
0
  if (!bitmap->data)
87
0
    gdi_bitmap->bitmap = gdi_CreateCompatibleBitmap(gdi->hdc, bitmap->width, bitmap->height);
88
0
  else
89
0
  {
90
0
    UINT32 format = bitmap->format;
91
0
    gdi_bitmap->bitmap =
92
0
        gdi_create_bitmap(gdi, bitmap->width, bitmap->height, format, bitmap->data);
93
0
  }
94
95
0
  if (!gdi_bitmap->bitmap)
96
0
  {
97
0
    gdi_DeleteDC(gdi_bitmap->hdc);
98
0
    gdi_bitmap->hdc = nullptr;
99
0
    return FALSE;
100
0
  }
101
102
0
  gdi_bitmap->hdc->format = gdi_bitmap->bitmap->format;
103
0
  gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->bitmap);
104
0
  gdi_bitmap->org_bitmap = nullptr;
105
0
  return TRUE;
106
0
}
107
108
static void gdi_Bitmap_Free(WINPR_ATTR_UNUSED rdpContext* context, rdpBitmap* bitmap)
109
0
{
110
0
  gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
111
112
0
  if (gdi_bitmap)
113
0
  {
114
0
    if (gdi_bitmap->hdc)
115
0
      gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->org_bitmap);
116
117
0
    gdi_DeleteObject((HGDIOBJECT)gdi_bitmap->bitmap);
118
0
    gdi_DeleteDC(gdi_bitmap->hdc);
119
0
    winpr_aligned_free(bitmap->data);
120
0
  }
121
122
0
  free(bitmap);
123
0
}
124
125
static BOOL gdi_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap)
126
0
{
127
0
  gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
128
0
  UINT32 width = bitmap->right - bitmap->left + 1;
129
0
  UINT32 height = bitmap->bottom - bitmap->top + 1;
130
0
  return gdi_BitBlt(context->gdi->primary->hdc, WINPR_ASSERTING_INT_CAST(int, bitmap->left),
131
0
                    WINPR_ASSERTING_INT_CAST(int, bitmap->top),
132
0
                    WINPR_ASSERTING_INT_CAST(int, width), WINPR_ASSERTING_INT_CAST(int, height),
133
0
                    gdi_bitmap->hdc, 0, 0, GDI_SRCCOPY, &context->gdi->palette);
134
0
}
135
136
WINPR_ATTR_NODISCARD
137
static BOOL gdi_Bitmap_Rfx(rdpContext* context, rdpBitmap* bitmap, const BYTE* pSrcData,
138
                           UINT32 SrcSize)
139
0
{
140
0
  WINPR_ASSERT(context);
141
0
  WINPR_ASSERT(bitmap);
142
0
  WINPR_ASSERT(pSrcData || (SrcSize == 0));
143
144
0
  REGION16 invalidRegion = WINPR_C_ARRAY_INIT;
145
0
  region16_init(&invalidRegion);
146
147
0
  const UINT32 stride = bitmap->width * FreeRDPGetBytesPerPixel(bitmap->format);
148
149
0
  const BOOL rc =
150
0
      rfx_process_message(context->codecs->rfx, pSrcData, SrcSize, bitmap->left, bitmap->top,
151
0
                          bitmap->data, bitmap->format, stride, bitmap->height, &invalidRegion);
152
0
  region16_uninit(&invalidRegion);
153
154
0
  if (!rc)
155
0
  {
156
0
    WLog_ERR(TAG, "rfx_process_message failed");
157
0
    return FALSE;
158
0
  }
159
0
  return TRUE;
160
0
}
161
162
static 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
0
{
166
0
  WINPR_ASSERT(context);
167
0
  WINPR_ASSERT(bitmap);
168
169
0
  UINT32 SrcSize = length;
170
0
  rdpGdi* gdi = context->gdi;
171
0
  WINPR_ASSERT(gdi);
172
173
0
  bitmap->compressed = FALSE;
174
0
  bitmap->format = gdi->dstFormat;
175
176
0
  if ((FreeRDPGetBytesPerPixel(bitmap->format) == 0) || (DstWidth == 0) || (DstHeight == 0) ||
177
0
      (DstWidth > UINT32_MAX / DstHeight) ||
178
0
      ((DstWidth * DstHeight) > (UINT32_MAX / FreeRDPGetBytesPerPixel(bitmap->format))))
179
0
  {
180
0
    WLog_ERR(TAG, "invalid input data");
181
0
    return FALSE;
182
0
  }
183
184
0
  const DWORD xbpp = FreeRDPGetBytesPerPixel(bitmap->format);
185
186
0
  WINPR_ASSERT(!bitmap->data);
187
0
  const UINT32 stride = DstWidth * xbpp;
188
0
  bitmap->length = stride * DstHeight;
189
0
  bitmap->data = (BYTE*)winpr_aligned_malloc(bitmap->length, 16);
190
191
0
  if (!bitmap->data)
192
0
    return FALSE;
193
194
0
  if (compressed)
195
0
  {
196
0
    WINPR_ASSERT(context->codecs);
197
0
    switch (codecId)
198
0
    {
199
0
      case RDP_CODEC_ID_REMOTEFX:
200
0
      case RDP_CODEC_ID_IMAGE_REMOTEFX:
201
0
        if (!gdi_Bitmap_Rfx(context, bitmap, pSrcData, SrcSize))
202
0
          return FALSE;
203
0
        break;
204
0
      case RDP_CODEC_ID_NSCODEC:
205
0
      {
206
0
        const int status = nsc_process_message(
207
0
            context->codecs->nsc, 32, DstWidth, DstHeight, pSrcData, SrcSize, bitmap->data,
208
0
            bitmap->format, stride, 0, 0, DstWidth, DstHeight, FREERDP_FLIP_VERTICAL);
209
210
0
        if (status < 1)
211
0
        {
212
0
          WLog_ERR(TAG, "nsc_process_message failed");
213
0
          return FALSE;
214
0
        }
215
0
      }
216
0
      break;
217
0
      default:
218
0
        if (bpp < 32)
219
0
        {
220
          /* Bitmap has an alpha channel, source does not necessarily have one.
221
           * Prefill the alpha channel to 0xff
222
           */
223
0
          if (FreeRDPColorHasAlpha(bitmap->format))
224
0
            memset(bitmap->data, 0xff, bitmap->length);
225
226
0
          if (!interleaved_decompress(context->codecs->interleaved, pSrcData, SrcSize,
227
0
                                      DstWidth, DstHeight, bpp, bitmap->data,
228
0
                                      bitmap->format, stride, 0, 0, DstWidth, DstHeight,
229
0
                                      &gdi->palette))
230
0
          {
231
0
            WLog_ERR(TAG, "interleaved_decompress failed");
232
0
            return FALSE;
233
0
          }
234
0
        }
235
0
        else
236
0
        {
237
0
          const BOOL fidelity = freerdp_settings_get_bool(
238
0
              context->settings, FreeRDP_DrawAllowDynamicColorFidelity);
239
0
          freerdp_planar_switch_bgr(context->codecs->planar, fidelity);
240
0
          if (!freerdp_bitmap_decompress_planar(
241
0
                  context->codecs->planar, pSrcData, SrcSize, DstWidth, DstHeight,
242
0
                  bitmap->data, bitmap->format, stride, 0, 0, DstWidth, DstHeight, TRUE))
243
0
          {
244
0
            WLog_ERR(TAG, "freerdp_bitmap_decompress_planar failed");
245
0
            return FALSE;
246
0
          }
247
0
        }
248
0
        break;
249
0
    }
250
0
  }
251
0
  else
252
0
  {
253
0
    const UINT32 SrcFormat = gdi_get_pixel_format(bpp);
254
0
    const size_t sbpp = FreeRDPGetBytesPerPixel(SrcFormat);
255
0
    const size_t dbpp = FreeRDPGetBytesPerPixel(bitmap->format);
256
257
0
    if ((sbpp == 0) || (dbpp == 0))
258
0
      return FALSE;
259
0
    else
260
0
    {
261
0
      const size_t dstSize = SrcSize * dbpp / sbpp;
262
263
0
      if (dstSize < bitmap->length)
264
0
      {
265
0
        WLog_ERR(TAG, "dstSize %" PRIuz " < bitmap->length %" PRIu32, dstSize,
266
0
                 bitmap->length);
267
0
        return FALSE;
268
0
      }
269
0
    }
270
271
0
    if (!freerdp_image_copy_no_overlap(bitmap->data, bitmap->format, stride, 0, 0, DstWidth,
272
0
                                       DstHeight, pSrcData, SrcFormat, 0, 0, 0, &gdi->palette,
273
0
                                       FREERDP_FLIP_VERTICAL))
274
0
    {
275
0
      WLog_ERR(TAG, "freerdp_image_copy failed");
276
0
      return FALSE;
277
0
    }
278
0
  }
279
280
0
  return TRUE;
281
0
}
282
283
static BOOL gdi_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary)
284
0
{
285
0
  rdpGdi* gdi = nullptr;
286
287
0
  if (!context)
288
0
    return FALSE;
289
290
0
  gdi = context->gdi;
291
292
0
  if (!gdi)
293
0
    return FALSE;
294
295
0
  if (primary)
296
0
    gdi->drawing = gdi->primary;
297
0
  else
298
0
    gdi->drawing = (gdiBitmap*)bitmap;
299
300
0
  return TRUE;
301
0
}
302
303
/* Glyph Class */
304
static BOOL gdi_Glyph_New(rdpContext* context, rdpGlyph* glyph)
305
0
{
306
0
  if (!context || !glyph)
307
0
    return FALSE;
308
309
0
  gdiGlyph* gdi_glyph = (gdiGlyph*)glyph;
310
0
  gdi_glyph->hdc = gdi_GetDC();
311
312
0
  if (!gdi_glyph->hdc)
313
0
    return FALSE;
314
315
0
  gdi_glyph->hdc->format = PIXEL_FORMAT_MONO;
316
0
  BYTE* data = freerdp_glyph_convert_ex(glyph->cx, glyph->cy, glyph->aj, glyph->cb);
317
318
0
  if (!data)
319
0
  {
320
0
    gdi_DeleteDC(gdi_glyph->hdc);
321
0
    return FALSE;
322
0
  }
323
324
0
  gdi_glyph->bitmap = gdi_CreateBitmap(glyph->cx, glyph->cy, PIXEL_FORMAT_MONO, data);
325
326
0
  if (!gdi_glyph->bitmap)
327
0
  {
328
0
    gdi_DeleteDC(gdi_glyph->hdc);
329
0
    winpr_aligned_free(data);
330
0
    return FALSE;
331
0
  }
332
333
0
  gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->bitmap);
334
0
  gdi_glyph->org_bitmap = nullptr;
335
0
  return TRUE;
336
0
}
337
338
static void gdi_Glyph_Free(WINPR_ATTR_UNUSED rdpContext* context, rdpGlyph* glyph)
339
0
{
340
0
  gdiGlyph* gdi_glyph = nullptr;
341
0
  gdi_glyph = (gdiGlyph*)glyph;
342
343
0
  if (gdi_glyph)
344
0
  {
345
0
    gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->org_bitmap);
346
0
    gdi_DeleteObject((HGDIOBJECT)gdi_glyph->bitmap);
347
0
    gdi_DeleteDC(gdi_glyph->hdc);
348
0
    free(glyph->aj);
349
0
    free(glyph);
350
0
  }
351
0
}
352
353
static 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
0
{
356
0
  const gdiGlyph* gdi_glyph = nullptr;
357
0
  rdpGdi* gdi = nullptr;
358
0
  HGDI_BRUSH brush = nullptr;
359
0
  BOOL rc = FALSE;
360
361
0
  if (!context || !glyph)
362
0
    return FALSE;
363
364
0
  gdi = context->gdi;
365
0
  gdi_glyph = (const gdiGlyph*)glyph;
366
367
0
  if (!fOpRedundant)
368
0
  {
369
0
    GDI_RECT rect = WINPR_C_ARRAY_INIT;
370
371
0
    if (x > 0)
372
0
      rect.left = x;
373
374
0
    if (y > 0)
375
0
      rect.top = y;
376
377
0
    if (x + w > 0)
378
0
      rect.right = x + w - 1;
379
380
0
    if (y + h > 0)
381
0
      rect.bottom = y + h - 1;
382
383
0
    if ((rect.left < rect.right) && (rect.top < rect.bottom))
384
0
    {
385
0
      brush = gdi_CreateSolidBrush(gdi->drawing->hdc->bkColor);
386
387
0
      if (!brush)
388
0
        return FALSE;
389
390
0
      const BOOL res = gdi_FillRect(gdi->drawing->hdc, &rect, brush);
391
0
      gdi_DeleteObject((HGDIOBJECT)brush);
392
0
      if (!res)
393
0
        return res;
394
0
    }
395
0
  }
396
397
0
  brush = gdi_CreateSolidBrush(gdi->drawing->hdc->textColor);
398
399
0
  if (!brush)
400
0
    return FALSE;
401
402
0
  gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)brush);
403
0
  rc = gdi_BitBlt(gdi->drawing->hdc, x, y, w, h, gdi_glyph->hdc, sx, sy, GDI_GLYPH_ORDER,
404
0
                  &context->gdi->palette);
405
0
  gdi_DeleteObject((HGDIOBJECT)brush);
406
0
  return rc;
407
0
}
408
409
static BOOL gdi_Glyph_BeginDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height,
410
                                UINT32 bgcolor, UINT32 fgcolor, BOOL fOpRedundant)
411
0
{
412
0
  if (!context || !context->gdi)
413
0
    return FALSE;
414
415
0
  rdpGdi* gdi = context->gdi;
416
417
0
  if (!gdi->drawing || !gdi->drawing->hdc)
418
0
    return FALSE;
419
420
0
  if (!fOpRedundant)
421
0
  {
422
0
    if (!gdi_decode_color(gdi, bgcolor, &bgcolor, nullptr))
423
0
      return FALSE;
424
425
0
    if (!gdi_decode_color(gdi, fgcolor, &fgcolor, nullptr))
426
0
      return FALSE;
427
428
0
    if (!gdi_SetClipRgn(gdi->drawing->hdc, x, y, width, height))
429
0
      return FALSE;
430
431
0
    gdi_SetTextColor(gdi->drawing->hdc, bgcolor);
432
0
    gdi_SetBkColor(gdi->drawing->hdc, fgcolor);
433
434
0
    {
435
0
      GDI_RECT rect = WINPR_C_ARRAY_INIT;
436
0
      HGDI_BRUSH brush = gdi_CreateSolidBrush(fgcolor);
437
438
0
      if (!brush)
439
0
        return FALSE;
440
441
0
      if (x > 0)
442
0
        rect.left = x;
443
444
0
      if (y > 0)
445
0
        rect.top = y;
446
447
0
      rect.right = x + width - 1;
448
0
      rect.bottom = y + height - 1;
449
450
0
      BOOL res = TRUE;
451
0
      if ((x + width > rect.left) && (y + height > rect.top))
452
0
        res = gdi_FillRect(gdi->drawing->hdc, &rect, brush);
453
454
0
      gdi_DeleteObject((HGDIOBJECT)brush);
455
0
      if (!res)
456
0
        return FALSE;
457
0
    }
458
459
0
    return gdi_SetNullClipRgn(gdi->drawing->hdc);
460
0
  }
461
462
0
  return TRUE;
463
0
}
464
465
static 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
0
{
470
0
  rdpGdi* gdi = nullptr;
471
472
0
  if (!context || !context->gdi)
473
0
    return FALSE;
474
475
0
  gdi = context->gdi;
476
477
0
  if (!gdi->drawing || !gdi->drawing->hdc)
478
0
    return FALSE;
479
480
0
  return gdi_SetNullClipRgn(gdi->drawing->hdc);
481
0
}
482
483
/* Graphics Module */
484
BOOL gdi_register_graphics(rdpGraphics* graphics)
485
0
{
486
0
  rdpBitmap bitmap = WINPR_C_ARRAY_INIT;
487
0
  rdpGlyph glyph = WINPR_C_ARRAY_INIT;
488
0
  bitmap.size = sizeof(gdiBitmap);
489
0
  bitmap.New = gdi_Bitmap_New;
490
0
  bitmap.Free = gdi_Bitmap_Free;
491
0
  bitmap.Paint = gdi_Bitmap_Paint;
492
0
  bitmap.Decompress = gdi_Bitmap_Decompress;
493
0
  bitmap.SetSurface = gdi_Bitmap_SetSurface;
494
0
  graphics_register_bitmap(graphics, &bitmap);
495
0
  glyph.size = sizeof(gdiGlyph);
496
0
  glyph.New = gdi_Glyph_New;
497
0
  glyph.Free = gdi_Glyph_Free;
498
0
  glyph.Draw = gdi_Glyph_Draw;
499
0
  glyph.BeginDraw = gdi_Glyph_BeginDraw;
500
0
  glyph.EndDraw = gdi_Glyph_EndDraw;
501
0
  graphics_register_glyph(graphics, &glyph);
502
0
  return TRUE;
503
0
}