Coverage Report

Created: 2024-09-08 06:16

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