Coverage Report

Created: 2026-10-15 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/cache/glyph.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Glyph Cache
4
 *
5
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <freerdp/config.h>
21
22
#include <stdio.h>
23
24
#include <winpr/crt.h>
25
#include <winpr/assert.h>
26
#include <winpr/cast.h>
27
28
#include <freerdp/freerdp.h>
29
#include <winpr/stream.h>
30
31
#include <freerdp/log.h>
32
33
#include "glyph.h"
34
#include "cache.h"
35
36
#define TAG FREERDP_TAG("cache.glyph")
37
38
typedef struct
39
{
40
  UINT32 number;
41
  UINT32 maxCellSize;
42
  rdpGlyph** entries;
43
} GLYPH_CACHE;
44
45
typedef struct
46
{
47
  void* fragment;
48
  UINT32 size;
49
} FRAGMENT_CACHE_ENTRY;
50
51
typedef struct
52
{
53
  FRAGMENT_CACHE_ENTRY entries[256];
54
} FRAGMENT_CACHE;
55
56
struct glyphCache
57
{
58
  FRAGMENT_CACHE fragCache;
59
  GLYPH_CACHE glyphCache[10];
60
61
  wLog* log;
62
  rdpContext* context;
63
};
64
65
WINPR_ATTR_NODISCARD
66
static rdpGlyph* glyph_cache_get(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index);
67
68
WINPR_ATTR_NODISCARD
69
static BOOL glyph_cache_put(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index, rdpGlyph* glyph);
70
71
WINPR_ATTR_NODISCARD
72
static const void* glyph_cache_fragment_get(rdpGlyphCache* glyphCache, UINT32 index, UINT32* size);
73
74
WINPR_ATTR_NODISCARD
75
static BOOL glyph_cache_fragment_put(rdpGlyphCache* glyphCache, UINT32 index, UINT32 size,
76
                                     const void* fragment);
77
78
WINPR_ATTR_NODISCARD
79
static UINT32 update_glyph_offset(const BYTE* data, size_t length, UINT32 index, INT32* x, INT32* y,
80
                                  UINT32 ulCharInc, UINT32 flAccel)
81
0
{
82
0
  if ((ulCharInc == 0) && (!(flAccel & SO_CHAR_INC_EQUAL_BM_BASE)))
83
0
  {
84
0
    if (index >= length)
85
0
    {
86
0
      WLog_WARN(TAG, "glyph offset index out of bound %" PRIu32 " [max %" PRIuz "]", index,
87
0
                length);
88
0
      return index;
89
0
    }
90
91
0
    UINT32 offset = data[index++];
92
93
0
    if (offset & 0x80)
94
0
    {
95
96
0
      if (index + 1 < length)
97
0
      {
98
0
        offset = data[index++];
99
0
        offset |= ((UINT32)data[index++]) << 8;
100
0
      }
101
0
      else
102
0
        WLog_WARN(TAG, "glyph index out of bound %" PRIu32 " [max %" PRIuz "]", index,
103
0
                  length);
104
0
    }
105
106
0
    if (flAccel & SO_VERTICAL)
107
0
      *y += WINPR_ASSERTING_INT_CAST(int32_t, offset);
108
109
0
    if (flAccel & SO_HORIZONTAL)
110
0
      *x += WINPR_ASSERTING_INT_CAST(int32_t, offset);
111
0
  }
112
113
0
  return index;
114
0
}
115
116
WINPR_ATTR_NODISCARD
117
static BOOL update_process_glyph(rdpContext* context, const BYTE* data, UINT32 cacheIndex, INT32* x,
118
                                 const INT32* y, UINT32 cacheId, UINT32 flAccel, BOOL fOpRedundant,
119
                                 const RDP_RECT* bound)
120
0
{
121
0
  INT32 sx = 0;
122
0
  INT32 sy = 0;
123
124
0
  if (!context || !data || !x || !y || !context->graphics || !context->cache ||
125
0
      !context->cache->glyph)
126
0
    return FALSE;
127
128
0
  rdpGlyphCache* glyph_cache = context->cache->glyph;
129
0
  rdpGlyph* glyph = glyph_cache_get(glyph_cache, cacheId, cacheIndex);
130
131
0
  if (!glyph)
132
0
    return freerdp_settings_get_bool(context->settings,
133
0
                                     FreeRDP_AllowUnanouncedOrdersFromServer);
134
135
0
  INT32 dx = glyph->x + *x;
136
0
  INT32 dy = glyph->y + *y;
137
138
0
  if (dx < bound->x)
139
0
  {
140
0
    sx = bound->x - dx;
141
0
    dx = bound->x;
142
0
  }
143
144
0
  if (dy < bound->y)
145
0
  {
146
0
    sy = bound->y - dy;
147
0
    dy = bound->y;
148
0
  }
149
150
0
  if ((dx <= (bound->x + bound->width)) && (dy <= (bound->y + bound->height)))
151
0
  {
152
0
    INT32 dw = WINPR_ASSERTING_INT_CAST(int32_t, glyph->cx) - sx;
153
0
    INT32 dh = WINPR_ASSERTING_INT_CAST(int32_t, glyph->cy) - sy;
154
155
0
    if ((dw + dx) > (bound->x + bound->width))
156
0
      dw = (bound->x + bound->width) - (dw + dx);
157
158
0
    if ((dh + dy) > (bound->y + bound->height))
159
0
      dh = (bound->y + bound->height) - (dh + dy);
160
161
0
    if ((dh > 0) && (dw > 0))
162
0
    {
163
0
      if (!glyph->Draw(context, glyph, dx, dy, dw, dh, sx, sy, fOpRedundant))
164
0
        return FALSE;
165
0
    }
166
0
  }
167
168
0
  if (flAccel & SO_CHAR_INC_EQUAL_BM_BASE)
169
0
    *x += WINPR_ASSERTING_INT_CAST(int32_t, glyph->cx);
170
171
0
  return TRUE;
172
0
}
173
174
WINPR_ATTR_NODISCARD
175
static BOOL fits_int16(INT32 val)
176
0
{
177
0
  if (val < INT16_MIN)
178
0
    return FALSE;
179
0
  if (val > INT16_MAX)
180
0
    return FALSE;
181
0
  return TRUE;
182
0
}
183
184
WINPR_ATTR_NODISCARD
185
static BOOL update_process_glyph_fragments(rdpContext* context, const BYTE* data, UINT32 length,
186
                                           UINT32 cacheId, UINT32 ulCharInc, UINT32 flAccel,
187
                                           UINT32 bgcolor, UINT32 fgcolor, INT32 x, INT32 y,
188
                                           INT32 bkX, INT32 bkY, INT32 bkWidth, INT32 bkHeight,
189
                                           INT32 opX, INT32 opY, INT32 opWidth, INT32 opHeight,
190
                                           BOOL fOpRedundant)
191
0
{
192
0
  UINT32 id = 0;
193
0
  UINT32 size = 0;
194
0
  UINT32 index = 0;
195
0
  const BYTE* fragments = nullptr;
196
0
  RDP_RECT bound = WINPR_C_ARRAY_INIT;
197
0
  BOOL rc = FALSE;
198
199
0
  if (!context || !data || !context->graphics || !context->cache || !context->cache->glyph)
200
0
    return FALSE;
201
202
0
  rdpGraphics* graphics = context->graphics;
203
0
  WINPR_ASSERT(graphics);
204
205
0
  WINPR_ASSERT(context->cache);
206
0
  rdpGlyphCache* glyph_cache = context->cache->glyph;
207
0
  WINPR_ASSERT(glyph_cache);
208
209
0
  {
210
0
    rdpGlyph* glyph = graphics->Glyph_Prototype;
211
0
    if (!glyph)
212
0
      goto fail;
213
214
    /* Limit op rectangle to visible screen. */
215
0
    if (opX < 0)
216
0
    {
217
0
      opWidth += opX;
218
0
      opX = 0;
219
0
    }
220
221
0
    if (opY < 0)
222
0
    {
223
0
      opHeight += opY;
224
0
      opY = 0;
225
0
    }
226
227
0
    if (opWidth < 0)
228
0
      opWidth = 0;
229
230
0
    if (opHeight < 0)
231
0
      opHeight = 0;
232
233
    /* Limit bk rectangle to visible screen. */
234
0
    if (bkX < 0)
235
0
    {
236
0
      bkWidth += bkX;
237
0
      bkX = 0;
238
0
    }
239
240
0
    if (bkY < 0)
241
0
    {
242
0
      bkHeight += bkY;
243
0
      bkY = 0;
244
0
    }
245
246
0
    if (bkWidth < 0)
247
0
      bkWidth = 0;
248
249
0
    if (bkHeight < 0)
250
0
      bkHeight = 0;
251
252
0
    {
253
0
      const UINT32 w = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
254
0
      if (opX + opWidth > (INT64)w)
255
0
      {
256
        /**
257
         * Some Microsoft servers send erroneous high values close to the
258
         * sint16 maximum in the OpRight field of the GlyphIndex, FastIndex and
259
         * FastGlyph drawing orders, probably a result of applications trying to
260
         * clear the text line to the very right end.
261
         * One example where this can be seen is typing in notepad.exe within
262
         * a RDP session to Windows XP Professional SP3.
263
         * This workaround prevents resulting problems in the UI callbacks.
264
         */
265
0
        opWidth = WINPR_ASSERTING_INT_CAST(int, w) - opX;
266
0
      }
267
268
0
      if (bkX + bkWidth > (INT64)w)
269
0
      {
270
        /**
271
         * Some Microsoft servers send erroneous high values close to the
272
         * sint16 maximum in the OpRight field of the GlyphIndex, FastIndex and
273
         * FastGlyph drawing orders, probably a result of applications trying to
274
         * clear the text line to the very right end.
275
         * One example where this can be seen is typing in notepad.exe within
276
         * a RDP session to Windows XP Professional SP3.
277
         * This workaround prevents resulting problems in the UI callbacks.
278
         */
279
0
        bkWidth = WINPR_ASSERTING_INT_CAST(int, w) - bkX;
280
0
      }
281
0
    }
282
283
0
    if (!fits_int16(bkX) || !fits_int16(bkY) || !fits_int16(bkWidth) || !fits_int16(bkHeight))
284
0
      goto fail;
285
286
0
    bound.x = WINPR_ASSERTING_INT_CAST(INT16, bkX);
287
0
    bound.y = WINPR_ASSERTING_INT_CAST(INT16, bkY);
288
0
    bound.width = WINPR_ASSERTING_INT_CAST(INT16, bkWidth);
289
0
    bound.height = WINPR_ASSERTING_INT_CAST(INT16, bkHeight);
290
291
0
    if (!glyph->BeginDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor, fOpRedundant))
292
0
      goto fail;
293
294
0
    if (!IFCALLRESULT(TRUE, glyph->SetBounds, context, bkX, bkY, bkWidth, bkHeight))
295
0
      goto fail;
296
297
0
    while (index < length)
298
0
    {
299
0
      const UINT32 op = data[index++];
300
301
0
      switch (op)
302
0
      {
303
0
        case GLYPH_FRAGMENT_USE:
304
0
          if (index + 1 > length)
305
0
            goto fail;
306
307
0
          id = data[index++];
308
0
          fragments = (const BYTE*)glyph_cache_fragment_get(glyph_cache, id, &size);
309
310
0
          if (fragments == nullptr)
311
0
            goto fail;
312
313
0
          for (UINT32 n = 0; n < size;)
314
0
          {
315
0
            const UINT32 fop = fragments[n++];
316
0
            n = update_glyph_offset(fragments, size, n, &x, &y, ulCharInc, flAccel);
317
318
0
            if (!update_process_glyph(context, fragments, fop, &x, &y, cacheId, flAccel,
319
0
                                      fOpRedundant, &bound))
320
0
              goto fail;
321
0
          }
322
323
0
          break;
324
325
0
        case GLYPH_FRAGMENT_ADD:
326
0
          if (index + 2 > length)
327
0
            goto fail;
328
329
0
          id = data[index++];
330
0
          size = data[index++];
331
0
          if (size > length - index)
332
0
            goto fail;
333
0
          if (!glyph_cache_fragment_put(glyph_cache, id, size, data))
334
0
            goto fail;
335
0
          break;
336
337
0
        default:
338
0
          index = update_glyph_offset(data, length, index, &x, &y, ulCharInc, flAccel);
339
340
0
          if (!update_process_glyph(context, data, op, &x, &y, cacheId, flAccel,
341
0
                                    fOpRedundant, &bound))
342
0
            goto fail;
343
344
0
          break;
345
0
      }
346
0
    }
347
348
0
    if (!glyph->EndDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor))
349
0
      goto fail;
350
0
  }
351
352
0
  rc = TRUE;
353
354
0
fail:
355
0
  return rc;
356
0
}
357
358
WINPR_ATTR_NODISCARD
359
static BOOL update_gdi_glyph_index(rdpContext* context, GLYPH_INDEX_ORDER* glyphIndex)
360
0
{
361
0
  INT32 bkWidth = 0;
362
0
  INT32 bkHeight = 0;
363
0
  INT32 opWidth = 0;
364
0
  INT32 opHeight = 0;
365
366
0
  if (!context || !glyphIndex || !context->cache)
367
0
    return FALSE;
368
369
0
  if (glyphIndex->bkRight > glyphIndex->bkLeft)
370
0
    bkWidth = glyphIndex->bkRight - glyphIndex->bkLeft + 1;
371
372
0
  if (glyphIndex->opRight > glyphIndex->opLeft)
373
0
    opWidth = glyphIndex->opRight - glyphIndex->opLeft + 1;
374
375
0
  if (glyphIndex->bkBottom > glyphIndex->bkTop)
376
0
    bkHeight = glyphIndex->bkBottom - glyphIndex->bkTop + 1;
377
378
0
  if (glyphIndex->opBottom > glyphIndex->opTop)
379
0
    opHeight = glyphIndex->opBottom - glyphIndex->opTop + 1;
380
381
0
  return update_process_glyph_fragments(
382
0
      context, glyphIndex->data, glyphIndex->cbData, glyphIndex->cacheId, glyphIndex->ulCharInc,
383
0
      glyphIndex->flAccel, glyphIndex->backColor, glyphIndex->foreColor, glyphIndex->x,
384
0
      glyphIndex->y, glyphIndex->bkLeft, glyphIndex->bkTop, bkWidth, bkHeight, glyphIndex->opLeft,
385
0
      glyphIndex->opTop, opWidth, opHeight,
386
0
      WINPR_ASSERTING_INT_CAST(int32_t, glyphIndex->fOpRedundant));
387
0
}
388
389
WINPR_ATTR_NODISCARD
390
static BOOL update_gdi_fast_index(rdpContext* context, const FAST_INDEX_ORDER* fastIndex)
391
0
{
392
0
  INT32 opWidth = 0;
393
0
  INT32 opHeight = 0;
394
0
  INT32 bkWidth = 0;
395
0
  INT32 bkHeight = 0;
396
0
  BOOL rc = FALSE;
397
398
0
  if (!context || !fastIndex || !context->cache)
399
0
    return FALSE;
400
401
0
  INT32 opLeft = fastIndex->opLeft;
402
0
  INT32 opTop = fastIndex->opTop;
403
0
  INT32 opRight = fastIndex->opRight;
404
0
  INT32 opBottom = fastIndex->opBottom;
405
0
  INT32 x = fastIndex->x;
406
0
  INT32 y = fastIndex->y;
407
408
0
  if (opBottom == -32768)
409
0
  {
410
0
    BYTE flags = (BYTE)(opTop & 0x0F);
411
412
0
    if (flags & 0x01)
413
0
      opBottom = fastIndex->bkBottom;
414
415
0
    if (flags & 0x02)
416
0
      opRight = fastIndex->bkRight;
417
418
0
    if (flags & 0x04)
419
0
      opTop = fastIndex->bkTop;
420
421
0
    if (flags & 0x08)
422
0
      opLeft = fastIndex->bkLeft;
423
0
  }
424
425
0
  if (opLeft == 0)
426
0
    opLeft = fastIndex->bkLeft;
427
428
0
  if (opRight == 0)
429
0
    opRight = fastIndex->bkRight;
430
431
  /* Server can send a massive number (32766) which appears to be
432
   * undocumented special behavior for "Erase all the way right".
433
   * X11 has nondeterministic results asking for a draw that wide. */
434
0
  if (opRight > (INT64)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth))
435
0
    opRight = (int)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
436
437
0
  if (x == -32768)
438
0
    x = fastIndex->bkLeft;
439
440
0
  if (y == -32768)
441
0
    y = fastIndex->bkTop;
442
443
0
  if (fastIndex->bkRight > fastIndex->bkLeft)
444
0
    bkWidth = fastIndex->bkRight - fastIndex->bkLeft + 1;
445
446
0
  if (fastIndex->bkBottom > fastIndex->bkTop)
447
0
    bkHeight = fastIndex->bkBottom - fastIndex->bkTop + 1;
448
449
0
  if (opRight > opLeft)
450
0
    opWidth = opRight - opLeft + 1;
451
452
0
  if (opBottom > opTop)
453
0
    opHeight = opBottom - opTop + 1;
454
455
0
  if (!update_process_glyph_fragments(
456
0
          context, fastIndex->data, fastIndex->cbData, fastIndex->cacheId, fastIndex->ulCharInc,
457
0
          fastIndex->flAccel, fastIndex->backColor, fastIndex->foreColor, x, y, fastIndex->bkLeft,
458
0
          fastIndex->bkTop, bkWidth, bkHeight, opLeft, opTop, opWidth, opHeight, FALSE))
459
0
    goto fail;
460
461
0
  rc = TRUE;
462
0
fail:
463
0
  return rc;
464
0
}
465
466
WINPR_ATTR_NODISCARD
467
static BOOL update_gdi_fast_glyph(rdpContext* context, const FAST_GLYPH_ORDER* fastGlyph)
468
0
{
469
0
  INT32 x = 0;
470
0
  INT32 y = 0;
471
0
  BYTE text_data[4] = WINPR_C_ARRAY_INIT;
472
0
  INT32 opLeft = 0;
473
0
  INT32 opTop = 0;
474
0
  INT32 opRight = 0;
475
0
  INT32 opBottom = 0;
476
0
  INT32 opWidth = 0;
477
0
  INT32 opHeight = 0;
478
0
  INT32 bkWidth = 0;
479
0
  INT32 bkHeight = 0;
480
0
  rdpCache* cache = nullptr;
481
482
0
  if (!context || !fastGlyph || !context->cache)
483
0
    return FALSE;
484
485
0
  cache = context->cache;
486
0
  opLeft = fastGlyph->opLeft;
487
0
  opTop = fastGlyph->opTop;
488
0
  opRight = fastGlyph->opRight;
489
0
  opBottom = fastGlyph->opBottom;
490
0
  x = fastGlyph->x;
491
0
  y = fastGlyph->y;
492
493
0
  if (opBottom == -32768)
494
0
  {
495
0
    BYTE flags = (BYTE)(opTop & 0x0F);
496
497
0
    if (flags & 0x01)
498
0
      opBottom = fastGlyph->bkBottom;
499
500
0
    if (flags & 0x02)
501
0
      opRight = fastGlyph->bkRight;
502
503
0
    if (flags & 0x04)
504
0
      opTop = fastGlyph->bkTop;
505
506
0
    if (flags & 0x08)
507
0
      opLeft = fastGlyph->bkLeft;
508
0
  }
509
510
0
  if (opLeft == 0)
511
0
    opLeft = fastGlyph->bkLeft;
512
513
0
  if (opRight == 0)
514
0
    opRight = fastGlyph->bkRight;
515
516
  /* See update_gdi_fast_index opRight comment. */
517
0
  if (opRight > (INT64)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth))
518
0
    opRight = (int)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
519
520
0
  if (x == -32768)
521
0
    x = fastGlyph->bkLeft;
522
523
0
  if (y == -32768)
524
0
    y = fastGlyph->bkTop;
525
526
0
  if ((fastGlyph->cbData > 1) && (fastGlyph->glyphData.aj))
527
0
  {
528
    /* got option font that needs to go into cache */
529
0
    rdpGlyph* glyph = nullptr;
530
0
    const GLYPH_DATA_V2* glyphData = &fastGlyph->glyphData;
531
532
0
    glyph = Glyph_Alloc(context, glyphData->x, glyphData->y, glyphData->cx, glyphData->cy,
533
0
                        glyphData->cb, glyphData->aj);
534
535
0
    if (!glyph)
536
0
      return FALSE;
537
538
0
    if (!glyph_cache_put(cache->glyph, fastGlyph->cacheId, fastGlyph->data[0], glyph))
539
0
    {
540
0
      glyph->Free(context, glyph);
541
0
      return FALSE;
542
0
    }
543
0
  }
544
545
0
  text_data[0] = fastGlyph->data[0];
546
0
  text_data[1] = 0;
547
548
0
  if (fastGlyph->bkRight > fastGlyph->bkLeft)
549
0
    bkWidth = fastGlyph->bkRight - fastGlyph->bkLeft + 1;
550
551
0
  if (fastGlyph->bkBottom > fastGlyph->bkTop)
552
0
    bkHeight = fastGlyph->bkBottom - fastGlyph->bkTop + 1;
553
554
0
  if (opRight > opLeft)
555
0
    opWidth = opRight - opLeft + 1;
556
557
0
  if (opBottom > opTop)
558
0
    opHeight = opBottom - opTop + 1;
559
560
0
  return update_process_glyph_fragments(
561
0
      context, text_data, sizeof(text_data), fastGlyph->cacheId, fastGlyph->ulCharInc,
562
0
      fastGlyph->flAccel, fastGlyph->backColor, fastGlyph->foreColor, x, y, fastGlyph->bkLeft,
563
0
      fastGlyph->bkTop, bkWidth, bkHeight, opLeft, opTop, opWidth, opHeight, FALSE);
564
0
}
565
566
WINPR_ATTR_NODISCARD
567
static BOOL update_gdi_cache_glyph(rdpContext* context, const CACHE_GLYPH_ORDER* cacheGlyph)
568
0
{
569
0
  if (!context || !cacheGlyph || !context->cache)
570
0
    return FALSE;
571
572
0
  rdpCache* cache = context->cache;
573
574
0
  for (size_t i = 0; i < cacheGlyph->cGlyphs; i++)
575
0
  {
576
0
    const GLYPH_DATA* glyph_data = &cacheGlyph->glyphData[i];
577
0
    rdpGlyph* glyph = Glyph_Alloc(context, glyph_data->x, glyph_data->y, glyph_data->cx,
578
0
                                  glyph_data->cy, glyph_data->cb, glyph_data->aj);
579
0
    if (!glyph)
580
0
      return FALSE;
581
582
0
    if (!glyph_cache_put(cache->glyph, cacheGlyph->cacheId, glyph_data->cacheIndex, glyph))
583
0
    {
584
0
      glyph->Free(context, glyph);
585
0
      return FALSE;
586
0
    }
587
0
  }
588
589
0
  return TRUE;
590
0
}
591
592
WINPR_ATTR_NODISCARD
593
static BOOL update_gdi_cache_glyph_v2(rdpContext* context, const CACHE_GLYPH_V2_ORDER* cacheGlyphV2)
594
0
{
595
0
  if (!context || !cacheGlyphV2 || !context->cache)
596
0
    return FALSE;
597
598
0
  rdpCache* cache = context->cache;
599
600
0
  for (size_t i = 0; i < cacheGlyphV2->cGlyphs; i++)
601
0
  {
602
0
    const GLYPH_DATA_V2* glyphData = &cacheGlyphV2->glyphData[i];
603
0
    rdpGlyph* glyph = Glyph_Alloc(context, glyphData->x, glyphData->y, glyphData->cx,
604
0
                                  glyphData->cy, glyphData->cb, glyphData->aj);
605
606
0
    if (!glyph)
607
0
      return FALSE;
608
609
0
    if (!glyph_cache_put(cache->glyph, cacheGlyphV2->cacheId, glyphData->cacheIndex, glyph))
610
0
    {
611
0
      glyph->Free(context, glyph);
612
0
      return FALSE;
613
0
    }
614
0
  }
615
616
0
  return TRUE;
617
0
}
618
619
rdpGlyph* glyph_cache_get(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index)
620
0
{
621
0
  WINPR_ASSERT(glyphCache);
622
623
0
  WLog_Print(glyphCache->log, WLOG_DEBUG, "GlyphCacheGet: id: %" PRIu32 " index: %" PRIu32 "", id,
624
0
             index);
625
626
0
  if (id >= ARRAYSIZE(glyphCache->glyphCache))
627
0
  {
628
0
    WLog_ERR(TAG, "invalid glyph cache id: %" PRIu32 "", id);
629
0
    return nullptr;
630
0
  }
631
632
0
  GLYPH_CACHE* cache = &glyphCache->glyphCache[id];
633
0
  if (index >= cache->number)
634
0
  {
635
0
    WLog_ERR(TAG, "index %" PRIu32 " out of range for cache id: %" PRIu32 "", index, id);
636
0
    return nullptr;
637
0
  }
638
639
0
  rdpGlyph* glyph = cache->entries[index];
640
0
  if (!glyph)
641
0
    WLog_ERR(TAG, "no glyph found at cache index: %" PRIu32 " in cache id: %" PRIu32 "", index,
642
0
             id);
643
644
0
  return glyph;
645
0
}
646
647
BOOL glyph_cache_put(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index, rdpGlyph* glyph)
648
0
{
649
0
  WINPR_ASSERT(glyphCache);
650
651
0
  if (id >= ARRAYSIZE(glyphCache->glyphCache))
652
0
  {
653
0
    WLog_ERR(TAG, "invalid glyph cache id: %" PRIu32 "", id);
654
0
    return FALSE;
655
0
  }
656
657
0
  GLYPH_CACHE* cache = &glyphCache->glyphCache[id];
658
0
  if (index >= cache->number)
659
0
  {
660
0
    WLog_ERR(TAG, "invalid glyph cache index: %" PRIu32 " in cache id: %" PRIu32 "", index, id);
661
0
    return FALSE;
662
0
  }
663
664
0
  WLog_Print(glyphCache->log, WLOG_DEBUG, "GlyphCachePut: id: %" PRIu32 " index: %" PRIu32 "", id,
665
0
             index);
666
0
  rdpGlyph* prevGlyph = cache->entries[index];
667
668
0
  if (prevGlyph)
669
0
  {
670
0
    WINPR_ASSERT(prevGlyph->Free);
671
0
    prevGlyph->Free(glyphCache->context, prevGlyph);
672
0
  }
673
674
0
  cache->entries[index] = glyph;
675
0
  return TRUE;
676
0
}
677
678
const void* glyph_cache_fragment_get(rdpGlyphCache* glyphCache, UINT32 index, UINT32* size)
679
0
{
680
0
  void* fragment = nullptr;
681
682
0
  WINPR_ASSERT(glyphCache);
683
0
  WINPR_ASSERT(glyphCache->fragCache.entries);
684
685
0
  if (index > 255)
686
0
  {
687
0
    WLog_ERR(TAG, "invalid glyph cache fragment index: %" PRIu32 "", index);
688
0
    return nullptr;
689
0
  }
690
691
0
  fragment = glyphCache->fragCache.entries[index].fragment;
692
0
  *size = (BYTE)glyphCache->fragCache.entries[index].size;
693
0
  WLog_Print(glyphCache->log, WLOG_DEBUG,
694
0
             "GlyphCacheFragmentGet: index: %" PRIu32 " size: %" PRIu32 "", index, *size);
695
696
0
  if (!fragment)
697
0
    WLog_ERR(TAG, "invalid glyph fragment at index:%" PRIu32 "", index);
698
699
0
  return fragment;
700
0
}
701
702
BOOL glyph_cache_fragment_put(rdpGlyphCache* glyphCache, UINT32 index, UINT32 size,
703
                              const void* fragment)
704
0
{
705
0
  WINPR_ASSERT(glyphCache);
706
0
  WINPR_ASSERT(glyphCache->fragCache.entries);
707
708
0
  if (index > 255)
709
0
  {
710
0
    WLog_ERR(TAG, "invalid glyph cache fragment index: %" PRIu32 "", index);
711
0
    return FALSE;
712
0
  }
713
714
0
  if (size == 0)
715
0
    return FALSE;
716
717
0
  void* copy = malloc(size);
718
719
0
  if (!copy)
720
0
    return FALSE;
721
722
0
  WLog_Print(glyphCache->log, WLOG_DEBUG,
723
0
             "GlyphCacheFragmentPut: index: %" PRIu32 " size: %" PRIu32 "", index, size);
724
0
  CopyMemory(copy, fragment, size);
725
726
0
  void* prevFragment = glyphCache->fragCache.entries[index].fragment;
727
0
  glyphCache->fragCache.entries[index].fragment = copy;
728
0
  glyphCache->fragCache.entries[index].size = size;
729
0
  free(prevFragment);
730
0
  return TRUE;
731
0
}
732
733
void glyph_cache_register_callbacks(rdpUpdate* update)
734
0
{
735
0
  WINPR_ASSERT(update);
736
0
  WINPR_ASSERT(update->context);
737
0
  WINPR_ASSERT(update->primary);
738
0
  WINPR_ASSERT(update->secondary);
739
740
0
  if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
741
0
  {
742
0
    update->primary->GlyphIndex = update_gdi_glyph_index;
743
0
    update->primary->FastIndex = update_gdi_fast_index;
744
0
    update->primary->FastGlyph = update_gdi_fast_glyph;
745
0
    update->secondary->CacheGlyph = update_gdi_cache_glyph;
746
0
    update->secondary->CacheGlyphV2 = update_gdi_cache_glyph_v2;
747
0
  }
748
0
}
749
750
rdpGlyphCache* glyph_cache_new(rdpContext* context)
751
0
{
752
0
  rdpGlyphCache* glyphCache = nullptr;
753
0
  rdpSettings* settings = nullptr;
754
755
0
  WINPR_ASSERT(context);
756
757
0
  settings = context->settings;
758
0
  WINPR_ASSERT(settings);
759
760
0
  glyphCache = (rdpGlyphCache*)calloc(1, sizeof(rdpGlyphCache));
761
762
0
  if (!glyphCache)
763
0
    return nullptr;
764
765
0
  glyphCache->log = WLog_Get("com.freerdp.cache.glyph");
766
0
  glyphCache->context = context;
767
768
0
  for (size_t i = 0; i < 10; i++)
769
0
  {
770
0
    const GLYPH_CACHE_DEFINITION* currentGlyph =
771
0
        freerdp_settings_get_pointer_array(settings, FreeRDP_GlyphCache, i);
772
0
    GLYPH_CACHE* currentCache = &glyphCache->glyphCache[i];
773
0
    currentCache->number = currentGlyph->cacheEntries;
774
0
    currentCache->maxCellSize = currentGlyph->cacheMaximumCellSize;
775
0
    currentCache->entries = (rdpGlyph**)calloc(currentCache->number, sizeof(rdpGlyph*));
776
777
0
    if (!currentCache->entries)
778
0
      goto fail;
779
0
  }
780
781
0
  return glyphCache;
782
0
fail:
783
0
  WINPR_PRAGMA_DIAG_PUSH
784
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
785
0
  glyph_cache_free(glyphCache);
786
0
  WINPR_PRAGMA_DIAG_POP
787
0
  return nullptr;
788
0
}
789
790
void glyph_cache_free(rdpGlyphCache* glyphCache)
791
0
{
792
0
  if (glyphCache)
793
0
  {
794
0
    GLYPH_CACHE* cache = glyphCache->glyphCache;
795
796
0
    for (size_t i = 0; i < 10; i++)
797
0
    {
798
0
      rdpGlyph** entries = cache[i].entries;
799
800
0
      if (!entries)
801
0
        continue;
802
803
0
      for (size_t j = 0; j < cache[i].number; j++)
804
0
      {
805
0
        rdpGlyph* glyph = entries[j];
806
807
0
        if (glyph)
808
0
        {
809
0
          glyph->Free(glyphCache->context, glyph);
810
0
          entries[j] = nullptr;
811
0
        }
812
0
      }
813
814
0
      free((void*)entries);
815
0
      cache[i].entries = nullptr;
816
0
    }
817
818
0
    for (size_t i = 0; i < ARRAYSIZE(glyphCache->fragCache.entries); i++)
819
0
    {
820
0
      free(glyphCache->fragCache.entries[i].fragment);
821
0
      glyphCache->fragCache.entries[i].fragment = nullptr;
822
0
    }
823
824
0
    free(glyphCache);
825
0
  }
826
0
}
827
828
CACHE_GLYPH_ORDER* copy_cache_glyph_order(rdpContext* context, const CACHE_GLYPH_ORDER* glyph)
829
0
{
830
0
  CACHE_GLYPH_ORDER* dst = nullptr;
831
832
0
  WINPR_ASSERT(context);
833
834
0
  dst = calloc(1, sizeof(CACHE_GLYPH_ORDER));
835
836
0
  if (!dst || !glyph)
837
0
    goto fail;
838
839
0
  *dst = *glyph;
840
841
0
  for (size_t x = 0; x < glyph->cGlyphs; x++)
842
0
  {
843
0
    const GLYPH_DATA* src = &glyph->glyphData[x];
844
0
    GLYPH_DATA* data = &dst->glyphData[x];
845
846
0
    if (src->aj)
847
0
    {
848
0
      const size_t size = src->cb;
849
0
      data->aj = malloc(size);
850
851
0
      if (!data->aj)
852
0
        goto fail;
853
854
0
      memcpy(data->aj, src->aj, size);
855
0
    }
856
0
  }
857
858
0
  if (glyph->unicodeCharacters)
859
0
  {
860
0
    if (glyph->cGlyphs == 0)
861
0
      goto fail;
862
863
0
    dst->unicodeCharacters = calloc(glyph->cGlyphs, sizeof(WCHAR));
864
865
0
    if (!dst->unicodeCharacters)
866
0
      goto fail;
867
868
0
    memcpy(dst->unicodeCharacters, glyph->unicodeCharacters, sizeof(WCHAR) * glyph->cGlyphs);
869
0
  }
870
871
0
  return dst;
872
0
fail:
873
0
  free_cache_glyph_order(context, dst);
874
0
  return nullptr;
875
0
}
876
877
void free_cache_glyph_order(WINPR_ATTR_UNUSED rdpContext* context, CACHE_GLYPH_ORDER* glyph)
878
0
{
879
0
  if (glyph)
880
0
  {
881
0
    for (size_t x = 0; x < ARRAYSIZE(glyph->glyphData); x++)
882
0
      free(glyph->glyphData[x].aj);
883
884
0
    free(glyph->unicodeCharacters);
885
0
  }
886
887
0
  free(glyph);
888
0
}
889
890
CACHE_GLYPH_V2_ORDER* copy_cache_glyph_v2_order(rdpContext* context,
891
                                                const CACHE_GLYPH_V2_ORDER* glyph)
892
0
{
893
0
  CACHE_GLYPH_V2_ORDER* dst = nullptr;
894
895
0
  WINPR_ASSERT(context);
896
897
0
  dst = calloc(1, sizeof(CACHE_GLYPH_V2_ORDER));
898
899
0
  if (!dst || !glyph)
900
0
    goto fail;
901
902
0
  *dst = *glyph;
903
904
0
  for (size_t x = 0; x < glyph->cGlyphs; x++)
905
0
  {
906
0
    const GLYPH_DATA_V2* src = &glyph->glyphData[x];
907
0
    GLYPH_DATA_V2* data = &dst->glyphData[x];
908
909
0
    if (src->aj)
910
0
    {
911
0
      const size_t size = src->cb;
912
0
      data->aj = malloc(size);
913
914
0
      if (!data->aj)
915
0
        goto fail;
916
917
0
      memcpy(data->aj, src->aj, size);
918
0
    }
919
0
  }
920
921
0
  if (glyph->unicodeCharacters)
922
0
  {
923
0
    if (glyph->cGlyphs == 0)
924
0
      goto fail;
925
926
0
    dst->unicodeCharacters = calloc(glyph->cGlyphs, sizeof(WCHAR));
927
928
0
    if (!dst->unicodeCharacters)
929
0
      goto fail;
930
931
0
    memcpy(dst->unicodeCharacters, glyph->unicodeCharacters, sizeof(WCHAR) * glyph->cGlyphs);
932
0
  }
933
934
0
  return dst;
935
0
fail:
936
0
  free_cache_glyph_v2_order(context, dst);
937
0
  return nullptr;
938
0
}
939
940
void free_cache_glyph_v2_order(WINPR_ATTR_UNUSED rdpContext* context, CACHE_GLYPH_V2_ORDER* glyph)
941
0
{
942
0
  if (glyph)
943
0
  {
944
0
    for (size_t x = 0; x < ARRAYSIZE(glyph->glyphData); x++)
945
0
      free(glyph->glyphData[x].aj);
946
947
0
    free(glyph->unicodeCharacters);
948
0
  }
949
950
0
  free(glyph);
951
0
}