Coverage Report

Created: 2026-08-31 06:25

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 update_process_glyph_fragments(rdpContext* context, const BYTE* data, UINT32 length,
176
                                           UINT32 cacheId, UINT32 ulCharInc, UINT32 flAccel,
177
                                           UINT32 bgcolor, UINT32 fgcolor, INT32 x, INT32 y,
178
                                           INT32 bkX, INT32 bkY, INT32 bkWidth, INT32 bkHeight,
179
                                           INT32 opX, INT32 opY, INT32 opWidth, INT32 opHeight,
180
                                           BOOL fOpRedundant)
181
0
{
182
0
  UINT32 id = 0;
183
0
  UINT32 size = 0;
184
0
  UINT32 index = 0;
185
0
  const BYTE* fragments = nullptr;
186
0
  RDP_RECT bound = WINPR_C_ARRAY_INIT;
187
0
  BOOL rc = FALSE;
188
189
0
  if (!context || !data || !context->graphics || !context->cache || !context->cache->glyph)
190
0
    return FALSE;
191
192
0
  rdpGraphics* graphics = context->graphics;
193
0
  WINPR_ASSERT(graphics);
194
195
0
  WINPR_ASSERT(context->cache);
196
0
  rdpGlyphCache* glyph_cache = context->cache->glyph;
197
0
  WINPR_ASSERT(glyph_cache);
198
199
0
  {
200
0
    rdpGlyph* glyph = graphics->Glyph_Prototype;
201
0
    if (!glyph)
202
0
      goto fail;
203
204
    /* Limit op rectangle to visible screen. */
205
0
    if (opX < 0)
206
0
    {
207
0
      opWidth += opX;
208
0
      opX = 0;
209
0
    }
210
211
0
    if (opY < 0)
212
0
    {
213
0
      opHeight += opY;
214
0
      opY = 0;
215
0
    }
216
217
0
    if (opWidth < 0)
218
0
      opWidth = 0;
219
220
0
    if (opHeight < 0)
221
0
      opHeight = 0;
222
223
    /* Limit bk rectangle to visible screen. */
224
0
    if (bkX < 0)
225
0
    {
226
0
      bkWidth += bkX;
227
0
      bkX = 0;
228
0
    }
229
230
0
    if (bkY < 0)
231
0
    {
232
0
      bkHeight += bkY;
233
0
      bkY = 0;
234
0
    }
235
236
0
    if (bkWidth < 0)
237
0
      bkWidth = 0;
238
239
0
    if (bkHeight < 0)
240
0
      bkHeight = 0;
241
242
0
    {
243
0
      const UINT32 w = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
244
0
      if (opX + opWidth > (INT64)w)
245
0
      {
246
        /**
247
         * Some Microsoft servers send erroneous high values close to the
248
         * sint16 maximum in the OpRight field of the GlyphIndex, FastIndex and
249
         * FastGlyph drawing orders, probably a result of applications trying to
250
         * clear the text line to the very right end.
251
         * One example where this can be seen is typing in notepad.exe within
252
         * a RDP session to Windows XP Professional SP3.
253
         * This workaround prevents resulting problems in the UI callbacks.
254
         */
255
0
        opWidth = WINPR_ASSERTING_INT_CAST(int, w) - opX;
256
0
      }
257
258
0
      if (bkX + bkWidth > (INT64)w)
259
0
      {
260
        /**
261
         * Some Microsoft servers send erroneous high values close to the
262
         * sint16 maximum in the OpRight field of the GlyphIndex, FastIndex and
263
         * FastGlyph drawing orders, probably a result of applications trying to
264
         * clear the text line to the very right end.
265
         * One example where this can be seen is typing in notepad.exe within
266
         * a RDP session to Windows XP Professional SP3.
267
         * This workaround prevents resulting problems in the UI callbacks.
268
         */
269
0
        bkWidth = WINPR_ASSERTING_INT_CAST(int, w) - bkX;
270
0
      }
271
0
    }
272
273
0
    bound.x = WINPR_ASSERTING_INT_CAST(INT16, bkX);
274
0
    bound.y = WINPR_ASSERTING_INT_CAST(INT16, bkY);
275
0
    bound.width = WINPR_ASSERTING_INT_CAST(INT16, bkWidth);
276
0
    bound.height = WINPR_ASSERTING_INT_CAST(INT16, bkHeight);
277
278
0
    if (!glyph->BeginDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor, fOpRedundant))
279
0
      goto fail;
280
281
0
    if (!IFCALLRESULT(TRUE, glyph->SetBounds, context, bkX, bkY, bkWidth, bkHeight))
282
0
      goto fail;
283
284
0
    while (index < length)
285
0
    {
286
0
      const UINT32 op = data[index++];
287
288
0
      switch (op)
289
0
      {
290
0
        case GLYPH_FRAGMENT_USE:
291
0
          if (index + 1 > length)
292
0
            goto fail;
293
294
0
          id = data[index++];
295
0
          fragments = (const BYTE*)glyph_cache_fragment_get(glyph_cache, id, &size);
296
297
0
          if (fragments == nullptr)
298
0
            goto fail;
299
300
0
          for (UINT32 n = 0; n < size;)
301
0
          {
302
0
            const UINT32 fop = fragments[n++];
303
0
            n = update_glyph_offset(fragments, size, n, &x, &y, ulCharInc, flAccel);
304
305
0
            if (!update_process_glyph(context, fragments, fop, &x, &y, cacheId, flAccel,
306
0
                                      fOpRedundant, &bound))
307
0
              goto fail;
308
0
          }
309
310
0
          break;
311
312
0
        case GLYPH_FRAGMENT_ADD:
313
0
          if (index + 2 > length)
314
0
            goto fail;
315
316
0
          id = data[index++];
317
0
          size = data[index++];
318
0
          if (size > length - index)
319
0
            goto fail;
320
0
          if (!glyph_cache_fragment_put(glyph_cache, id, size, data))
321
0
            goto fail;
322
0
          break;
323
324
0
        default:
325
0
          index = update_glyph_offset(data, length, index, &x, &y, ulCharInc, flAccel);
326
327
0
          if (!update_process_glyph(context, data, op, &x, &y, cacheId, flAccel,
328
0
                                    fOpRedundant, &bound))
329
0
            goto fail;
330
331
0
          break;
332
0
      }
333
0
    }
334
335
0
    if (!glyph->EndDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor))
336
0
      goto fail;
337
0
  }
338
339
0
  rc = TRUE;
340
341
0
fail:
342
0
  return rc;
343
0
}
344
345
WINPR_ATTR_NODISCARD
346
static BOOL update_gdi_glyph_index(rdpContext* context, GLYPH_INDEX_ORDER* glyphIndex)
347
0
{
348
0
  INT32 bkWidth = 0;
349
0
  INT32 bkHeight = 0;
350
0
  INT32 opWidth = 0;
351
0
  INT32 opHeight = 0;
352
353
0
  if (!context || !glyphIndex || !context->cache)
354
0
    return FALSE;
355
356
0
  if (glyphIndex->bkRight > glyphIndex->bkLeft)
357
0
    bkWidth = glyphIndex->bkRight - glyphIndex->bkLeft + 1;
358
359
0
  if (glyphIndex->opRight > glyphIndex->opLeft)
360
0
    opWidth = glyphIndex->opRight - glyphIndex->opLeft + 1;
361
362
0
  if (glyphIndex->bkBottom > glyphIndex->bkTop)
363
0
    bkHeight = glyphIndex->bkBottom - glyphIndex->bkTop + 1;
364
365
0
  if (glyphIndex->opBottom > glyphIndex->opTop)
366
0
    opHeight = glyphIndex->opBottom - glyphIndex->opTop + 1;
367
368
0
  return update_process_glyph_fragments(
369
0
      context, glyphIndex->data, glyphIndex->cbData, glyphIndex->cacheId, glyphIndex->ulCharInc,
370
0
      glyphIndex->flAccel, glyphIndex->backColor, glyphIndex->foreColor, glyphIndex->x,
371
0
      glyphIndex->y, glyphIndex->bkLeft, glyphIndex->bkTop, bkWidth, bkHeight, glyphIndex->opLeft,
372
0
      glyphIndex->opTop, opWidth, opHeight,
373
0
      WINPR_ASSERTING_INT_CAST(int32_t, glyphIndex->fOpRedundant));
374
0
}
375
376
WINPR_ATTR_NODISCARD
377
static BOOL update_gdi_fast_index(rdpContext* context, const FAST_INDEX_ORDER* fastIndex)
378
0
{
379
0
  INT32 opWidth = 0;
380
0
  INT32 opHeight = 0;
381
0
  INT32 bkWidth = 0;
382
0
  INT32 bkHeight = 0;
383
0
  BOOL rc = FALSE;
384
385
0
  if (!context || !fastIndex || !context->cache)
386
0
    return FALSE;
387
388
0
  INT32 opLeft = fastIndex->opLeft;
389
0
  INT32 opTop = fastIndex->opTop;
390
0
  INT32 opRight = fastIndex->opRight;
391
0
  INT32 opBottom = fastIndex->opBottom;
392
0
  INT32 x = fastIndex->x;
393
0
  INT32 y = fastIndex->y;
394
395
0
  if (opBottom == -32768)
396
0
  {
397
0
    BYTE flags = (BYTE)(opTop & 0x0F);
398
399
0
    if (flags & 0x01)
400
0
      opBottom = fastIndex->bkBottom;
401
402
0
    if (flags & 0x02)
403
0
      opRight = fastIndex->bkRight;
404
405
0
    if (flags & 0x04)
406
0
      opTop = fastIndex->bkTop;
407
408
0
    if (flags & 0x08)
409
0
      opLeft = fastIndex->bkLeft;
410
0
  }
411
412
0
  if (opLeft == 0)
413
0
    opLeft = fastIndex->bkLeft;
414
415
0
  if (opRight == 0)
416
0
    opRight = fastIndex->bkRight;
417
418
  /* Server can send a massive number (32766) which appears to be
419
   * undocumented special behavior for "Erase all the way right".
420
   * X11 has nondeterministic results asking for a draw that wide. */
421
0
  if (opRight > (INT64)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth))
422
0
    opRight = (int)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
423
424
0
  if (x == -32768)
425
0
    x = fastIndex->bkLeft;
426
427
0
  if (y == -32768)
428
0
    y = fastIndex->bkTop;
429
430
0
  if (fastIndex->bkRight > fastIndex->bkLeft)
431
0
    bkWidth = fastIndex->bkRight - fastIndex->bkLeft + 1;
432
433
0
  if (fastIndex->bkBottom > fastIndex->bkTop)
434
0
    bkHeight = fastIndex->bkBottom - fastIndex->bkTop + 1;
435
436
0
  if (opRight > opLeft)
437
0
    opWidth = opRight - opLeft + 1;
438
439
0
  if (opBottom > opTop)
440
0
    opHeight = opBottom - opTop + 1;
441
442
0
  if (!update_process_glyph_fragments(
443
0
          context, fastIndex->data, fastIndex->cbData, fastIndex->cacheId, fastIndex->ulCharInc,
444
0
          fastIndex->flAccel, fastIndex->backColor, fastIndex->foreColor, x, y, fastIndex->bkLeft,
445
0
          fastIndex->bkTop, bkWidth, bkHeight, opLeft, opTop, opWidth, opHeight, FALSE))
446
0
    goto fail;
447
448
0
  rc = TRUE;
449
0
fail:
450
0
  return rc;
451
0
}
452
453
WINPR_ATTR_NODISCARD
454
static BOOL update_gdi_fast_glyph(rdpContext* context, const FAST_GLYPH_ORDER* fastGlyph)
455
0
{
456
0
  INT32 x = 0;
457
0
  INT32 y = 0;
458
0
  BYTE text_data[4] = WINPR_C_ARRAY_INIT;
459
0
  INT32 opLeft = 0;
460
0
  INT32 opTop = 0;
461
0
  INT32 opRight = 0;
462
0
  INT32 opBottom = 0;
463
0
  INT32 opWidth = 0;
464
0
  INT32 opHeight = 0;
465
0
  INT32 bkWidth = 0;
466
0
  INT32 bkHeight = 0;
467
0
  rdpCache* cache = nullptr;
468
469
0
  if (!context || !fastGlyph || !context->cache)
470
0
    return FALSE;
471
472
0
  cache = context->cache;
473
0
  opLeft = fastGlyph->opLeft;
474
0
  opTop = fastGlyph->opTop;
475
0
  opRight = fastGlyph->opRight;
476
0
  opBottom = fastGlyph->opBottom;
477
0
  x = fastGlyph->x;
478
0
  y = fastGlyph->y;
479
480
0
  if (opBottom == -32768)
481
0
  {
482
0
    BYTE flags = (BYTE)(opTop & 0x0F);
483
484
0
    if (flags & 0x01)
485
0
      opBottom = fastGlyph->bkBottom;
486
487
0
    if (flags & 0x02)
488
0
      opRight = fastGlyph->bkRight;
489
490
0
    if (flags & 0x04)
491
0
      opTop = fastGlyph->bkTop;
492
493
0
    if (flags & 0x08)
494
0
      opLeft = fastGlyph->bkLeft;
495
0
  }
496
497
0
  if (opLeft == 0)
498
0
    opLeft = fastGlyph->bkLeft;
499
500
0
  if (opRight == 0)
501
0
    opRight = fastGlyph->bkRight;
502
503
  /* See update_gdi_fast_index opRight comment. */
504
0
  if (opRight > (INT64)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth))
505
0
    opRight = (int)freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth);
506
507
0
  if (x == -32768)
508
0
    x = fastGlyph->bkLeft;
509
510
0
  if (y == -32768)
511
0
    y = fastGlyph->bkTop;
512
513
0
  if ((fastGlyph->cbData > 1) && (fastGlyph->glyphData.aj))
514
0
  {
515
    /* got option font that needs to go into cache */
516
0
    rdpGlyph* glyph = nullptr;
517
0
    const GLYPH_DATA_V2* glyphData = &fastGlyph->glyphData;
518
519
0
    glyph = Glyph_Alloc(context, glyphData->x, glyphData->y, glyphData->cx, glyphData->cy,
520
0
                        glyphData->cb, glyphData->aj);
521
522
0
    if (!glyph)
523
0
      return FALSE;
524
525
0
    if (!glyph_cache_put(cache->glyph, fastGlyph->cacheId, fastGlyph->data[0], glyph))
526
0
    {
527
0
      glyph->Free(context, glyph);
528
0
      return FALSE;
529
0
    }
530
0
  }
531
532
0
  text_data[0] = fastGlyph->data[0];
533
0
  text_data[1] = 0;
534
535
0
  if (fastGlyph->bkRight > fastGlyph->bkLeft)
536
0
    bkWidth = fastGlyph->bkRight - fastGlyph->bkLeft + 1;
537
538
0
  if (fastGlyph->bkBottom > fastGlyph->bkTop)
539
0
    bkHeight = fastGlyph->bkBottom - fastGlyph->bkTop + 1;
540
541
0
  if (opRight > opLeft)
542
0
    opWidth = opRight - opLeft + 1;
543
544
0
  if (opBottom > opTop)
545
0
    opHeight = opBottom - opTop + 1;
546
547
0
  return update_process_glyph_fragments(
548
0
      context, text_data, sizeof(text_data), fastGlyph->cacheId, fastGlyph->ulCharInc,
549
0
      fastGlyph->flAccel, fastGlyph->backColor, fastGlyph->foreColor, x, y, fastGlyph->bkLeft,
550
0
      fastGlyph->bkTop, bkWidth, bkHeight, opLeft, opTop, opWidth, opHeight, FALSE);
551
0
}
552
553
WINPR_ATTR_NODISCARD
554
static BOOL update_gdi_cache_glyph(rdpContext* context, const CACHE_GLYPH_ORDER* cacheGlyph)
555
0
{
556
0
  if (!context || !cacheGlyph || !context->cache)
557
0
    return FALSE;
558
559
0
  rdpCache* cache = context->cache;
560
561
0
  for (size_t i = 0; i < cacheGlyph->cGlyphs; i++)
562
0
  {
563
0
    const GLYPH_DATA* glyph_data = &cacheGlyph->glyphData[i];
564
0
    rdpGlyph* glyph = Glyph_Alloc(context, glyph_data->x, glyph_data->y, glyph_data->cx,
565
0
                                  glyph_data->cy, glyph_data->cb, glyph_data->aj);
566
0
    if (!glyph)
567
0
      return FALSE;
568
569
0
    if (!glyph_cache_put(cache->glyph, cacheGlyph->cacheId, glyph_data->cacheIndex, glyph))
570
0
    {
571
0
      glyph->Free(context, glyph);
572
0
      return FALSE;
573
0
    }
574
0
  }
575
576
0
  return TRUE;
577
0
}
578
579
WINPR_ATTR_NODISCARD
580
static BOOL update_gdi_cache_glyph_v2(rdpContext* context, const CACHE_GLYPH_V2_ORDER* cacheGlyphV2)
581
0
{
582
0
  if (!context || !cacheGlyphV2 || !context->cache)
583
0
    return FALSE;
584
585
0
  rdpCache* cache = context->cache;
586
587
0
  for (size_t i = 0; i < cacheGlyphV2->cGlyphs; i++)
588
0
  {
589
0
    const GLYPH_DATA_V2* glyphData = &cacheGlyphV2->glyphData[i];
590
0
    rdpGlyph* glyph = Glyph_Alloc(context, glyphData->x, glyphData->y, glyphData->cx,
591
0
                                  glyphData->cy, glyphData->cb, glyphData->aj);
592
593
0
    if (!glyph)
594
0
      return FALSE;
595
596
0
    if (!glyph_cache_put(cache->glyph, cacheGlyphV2->cacheId, glyphData->cacheIndex, glyph))
597
0
    {
598
0
      glyph->Free(context, glyph);
599
0
      return FALSE;
600
0
    }
601
0
  }
602
603
0
  return TRUE;
604
0
}
605
606
rdpGlyph* glyph_cache_get(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index)
607
0
{
608
0
  WINPR_ASSERT(glyphCache);
609
610
0
  WLog_Print(glyphCache->log, WLOG_DEBUG, "GlyphCacheGet: id: %" PRIu32 " index: %" PRIu32 "", id,
611
0
             index);
612
613
0
  if (id >= ARRAYSIZE(glyphCache->glyphCache))
614
0
  {
615
0
    WLog_ERR(TAG, "invalid glyph cache id: %" PRIu32 "", id);
616
0
    return nullptr;
617
0
  }
618
619
0
  GLYPH_CACHE* cache = &glyphCache->glyphCache[id];
620
0
  if (index >= cache->number)
621
0
  {
622
0
    WLog_ERR(TAG, "index %" PRIu32 " out of range for cache id: %" PRIu32 "", index, id);
623
0
    return nullptr;
624
0
  }
625
626
0
  rdpGlyph* glyph = cache->entries[index];
627
0
  if (!glyph)
628
0
    WLog_ERR(TAG, "no glyph found at cache index: %" PRIu32 " in cache id: %" PRIu32 "", index,
629
0
             id);
630
631
0
  return glyph;
632
0
}
633
634
BOOL glyph_cache_put(rdpGlyphCache* glyphCache, UINT32 id, UINT32 index, rdpGlyph* glyph)
635
0
{
636
0
  WINPR_ASSERT(glyphCache);
637
638
0
  if (id >= ARRAYSIZE(glyphCache->glyphCache))
639
0
  {
640
0
    WLog_ERR(TAG, "invalid glyph cache id: %" PRIu32 "", id);
641
0
    return FALSE;
642
0
  }
643
644
0
  GLYPH_CACHE* cache = &glyphCache->glyphCache[id];
645
0
  if (index >= cache->number)
646
0
  {
647
0
    WLog_ERR(TAG, "invalid glyph cache index: %" PRIu32 " in cache id: %" PRIu32 "", index, id);
648
0
    return FALSE;
649
0
  }
650
651
0
  WLog_Print(glyphCache->log, WLOG_DEBUG, "GlyphCachePut: id: %" PRIu32 " index: %" PRIu32 "", id,
652
0
             index);
653
0
  rdpGlyph* prevGlyph = cache->entries[index];
654
655
0
  if (prevGlyph)
656
0
  {
657
0
    WINPR_ASSERT(prevGlyph->Free);
658
0
    prevGlyph->Free(glyphCache->context, prevGlyph);
659
0
  }
660
661
0
  cache->entries[index] = glyph;
662
0
  return TRUE;
663
0
}
664
665
const void* glyph_cache_fragment_get(rdpGlyphCache* glyphCache, UINT32 index, UINT32* size)
666
0
{
667
0
  void* fragment = nullptr;
668
669
0
  WINPR_ASSERT(glyphCache);
670
0
  WINPR_ASSERT(glyphCache->fragCache.entries);
671
672
0
  if (index > 255)
673
0
  {
674
0
    WLog_ERR(TAG, "invalid glyph cache fragment index: %" PRIu32 "", index);
675
0
    return nullptr;
676
0
  }
677
678
0
  fragment = glyphCache->fragCache.entries[index].fragment;
679
0
  *size = (BYTE)glyphCache->fragCache.entries[index].size;
680
0
  WLog_Print(glyphCache->log, WLOG_DEBUG,
681
0
             "GlyphCacheFragmentGet: index: %" PRIu32 " size: %" PRIu32 "", index, *size);
682
683
0
  if (!fragment)
684
0
    WLog_ERR(TAG, "invalid glyph fragment at index:%" PRIu32 "", index);
685
686
0
  return fragment;
687
0
}
688
689
BOOL glyph_cache_fragment_put(rdpGlyphCache* glyphCache, UINT32 index, UINT32 size,
690
                              const void* fragment)
691
0
{
692
0
  WINPR_ASSERT(glyphCache);
693
0
  WINPR_ASSERT(glyphCache->fragCache.entries);
694
695
0
  if (index > 255)
696
0
  {
697
0
    WLog_ERR(TAG, "invalid glyph cache fragment index: %" PRIu32 "", index);
698
0
    return FALSE;
699
0
  }
700
701
0
  if (size == 0)
702
0
    return FALSE;
703
704
0
  void* copy = malloc(size);
705
706
0
  if (!copy)
707
0
    return FALSE;
708
709
0
  WLog_Print(glyphCache->log, WLOG_DEBUG,
710
0
             "GlyphCacheFragmentPut: index: %" PRIu32 " size: %" PRIu32 "", index, size);
711
0
  CopyMemory(copy, fragment, size);
712
713
0
  void* prevFragment = glyphCache->fragCache.entries[index].fragment;
714
0
  glyphCache->fragCache.entries[index].fragment = copy;
715
0
  glyphCache->fragCache.entries[index].size = size;
716
0
  free(prevFragment);
717
0
  return TRUE;
718
0
}
719
720
void glyph_cache_register_callbacks(rdpUpdate* update)
721
0
{
722
0
  WINPR_ASSERT(update);
723
0
  WINPR_ASSERT(update->context);
724
0
  WINPR_ASSERT(update->primary);
725
0
  WINPR_ASSERT(update->secondary);
726
727
0
  if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
728
0
  {
729
0
    update->primary->GlyphIndex = update_gdi_glyph_index;
730
0
    update->primary->FastIndex = update_gdi_fast_index;
731
0
    update->primary->FastGlyph = update_gdi_fast_glyph;
732
0
    update->secondary->CacheGlyph = update_gdi_cache_glyph;
733
0
    update->secondary->CacheGlyphV2 = update_gdi_cache_glyph_v2;
734
0
  }
735
0
}
736
737
rdpGlyphCache* glyph_cache_new(rdpContext* context)
738
0
{
739
0
  rdpGlyphCache* glyphCache = nullptr;
740
0
  rdpSettings* settings = nullptr;
741
742
0
  WINPR_ASSERT(context);
743
744
0
  settings = context->settings;
745
0
  WINPR_ASSERT(settings);
746
747
0
  glyphCache = (rdpGlyphCache*)calloc(1, sizeof(rdpGlyphCache));
748
749
0
  if (!glyphCache)
750
0
    return nullptr;
751
752
0
  glyphCache->log = WLog_Get("com.freerdp.cache.glyph");
753
0
  glyphCache->context = context;
754
755
0
  for (size_t i = 0; i < 10; i++)
756
0
  {
757
0
    const GLYPH_CACHE_DEFINITION* currentGlyph =
758
0
        freerdp_settings_get_pointer_array(settings, FreeRDP_GlyphCache, i);
759
0
    GLYPH_CACHE* currentCache = &glyphCache->glyphCache[i];
760
0
    currentCache->number = currentGlyph->cacheEntries;
761
0
    currentCache->maxCellSize = currentGlyph->cacheMaximumCellSize;
762
0
    currentCache->entries = (rdpGlyph**)calloc(currentCache->number, sizeof(rdpGlyph*));
763
764
0
    if (!currentCache->entries)
765
0
      goto fail;
766
0
  }
767
768
0
  return glyphCache;
769
0
fail:
770
0
  WINPR_PRAGMA_DIAG_PUSH
771
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
772
0
  glyph_cache_free(glyphCache);
773
0
  WINPR_PRAGMA_DIAG_POP
774
0
  return nullptr;
775
0
}
776
777
void glyph_cache_free(rdpGlyphCache* glyphCache)
778
0
{
779
0
  if (glyphCache)
780
0
  {
781
0
    GLYPH_CACHE* cache = glyphCache->glyphCache;
782
783
0
    for (size_t i = 0; i < 10; i++)
784
0
    {
785
0
      rdpGlyph** entries = cache[i].entries;
786
787
0
      if (!entries)
788
0
        continue;
789
790
0
      for (size_t j = 0; j < cache[i].number; j++)
791
0
      {
792
0
        rdpGlyph* glyph = entries[j];
793
794
0
        if (glyph)
795
0
        {
796
0
          glyph->Free(glyphCache->context, glyph);
797
0
          entries[j] = nullptr;
798
0
        }
799
0
      }
800
801
0
      free((void*)entries);
802
0
      cache[i].entries = nullptr;
803
0
    }
804
805
0
    for (size_t i = 0; i < ARRAYSIZE(glyphCache->fragCache.entries); i++)
806
0
    {
807
0
      free(glyphCache->fragCache.entries[i].fragment);
808
0
      glyphCache->fragCache.entries[i].fragment = nullptr;
809
0
    }
810
811
0
    free(glyphCache);
812
0
  }
813
0
}
814
815
CACHE_GLYPH_ORDER* copy_cache_glyph_order(rdpContext* context, const CACHE_GLYPH_ORDER* glyph)
816
0
{
817
0
  CACHE_GLYPH_ORDER* dst = nullptr;
818
819
0
  WINPR_ASSERT(context);
820
821
0
  dst = calloc(1, sizeof(CACHE_GLYPH_ORDER));
822
823
0
  if (!dst || !glyph)
824
0
    goto fail;
825
826
0
  *dst = *glyph;
827
828
0
  for (size_t x = 0; x < glyph->cGlyphs; x++)
829
0
  {
830
0
    const GLYPH_DATA* src = &glyph->glyphData[x];
831
0
    GLYPH_DATA* data = &dst->glyphData[x];
832
833
0
    if (src->aj)
834
0
    {
835
0
      const size_t size = src->cb;
836
0
      data->aj = malloc(size);
837
838
0
      if (!data->aj)
839
0
        goto fail;
840
841
0
      memcpy(data->aj, src->aj, size);
842
0
    }
843
0
  }
844
845
0
  if (glyph->unicodeCharacters)
846
0
  {
847
0
    if (glyph->cGlyphs == 0)
848
0
      goto fail;
849
850
0
    dst->unicodeCharacters = calloc(glyph->cGlyphs, sizeof(WCHAR));
851
852
0
    if (!dst->unicodeCharacters)
853
0
      goto fail;
854
855
0
    memcpy(dst->unicodeCharacters, glyph->unicodeCharacters, sizeof(WCHAR) * glyph->cGlyphs);
856
0
  }
857
858
0
  return dst;
859
0
fail:
860
0
  free_cache_glyph_order(context, dst);
861
0
  return nullptr;
862
0
}
863
864
void free_cache_glyph_order(WINPR_ATTR_UNUSED rdpContext* context, CACHE_GLYPH_ORDER* glyph)
865
0
{
866
0
  if (glyph)
867
0
  {
868
0
    for (size_t x = 0; x < ARRAYSIZE(glyph->glyphData); x++)
869
0
      free(glyph->glyphData[x].aj);
870
871
0
    free(glyph->unicodeCharacters);
872
0
  }
873
874
0
  free(glyph);
875
0
}
876
877
CACHE_GLYPH_V2_ORDER* copy_cache_glyph_v2_order(rdpContext* context,
878
                                                const CACHE_GLYPH_V2_ORDER* glyph)
879
0
{
880
0
  CACHE_GLYPH_V2_ORDER* dst = nullptr;
881
882
0
  WINPR_ASSERT(context);
883
884
0
  dst = calloc(1, sizeof(CACHE_GLYPH_V2_ORDER));
885
886
0
  if (!dst || !glyph)
887
0
    goto fail;
888
889
0
  *dst = *glyph;
890
891
0
  for (size_t x = 0; x < glyph->cGlyphs; x++)
892
0
  {
893
0
    const GLYPH_DATA_V2* src = &glyph->glyphData[x];
894
0
    GLYPH_DATA_V2* data = &dst->glyphData[x];
895
896
0
    if (src->aj)
897
0
    {
898
0
      const size_t size = src->cb;
899
0
      data->aj = malloc(size);
900
901
0
      if (!data->aj)
902
0
        goto fail;
903
904
0
      memcpy(data->aj, src->aj, size);
905
0
    }
906
0
  }
907
908
0
  if (glyph->unicodeCharacters)
909
0
  {
910
0
    if (glyph->cGlyphs == 0)
911
0
      goto fail;
912
913
0
    dst->unicodeCharacters = calloc(glyph->cGlyphs, sizeof(WCHAR));
914
915
0
    if (!dst->unicodeCharacters)
916
0
      goto fail;
917
918
0
    memcpy(dst->unicodeCharacters, glyph->unicodeCharacters, sizeof(WCHAR) * glyph->cGlyphs);
919
0
  }
920
921
0
  return dst;
922
0
fail:
923
0
  free_cache_glyph_v2_order(context, dst);
924
0
  return nullptr;
925
0
}
926
927
void free_cache_glyph_v2_order(WINPR_ATTR_UNUSED rdpContext* context, CACHE_GLYPH_V2_ORDER* glyph)
928
0
{
929
0
  if (glyph)
930
0
  {
931
0
    for (size_t x = 0; x < ARRAYSIZE(glyph->glyphData); x++)
932
0
      free(glyph->glyphData[x].aj);
933
934
0
    free(glyph->unicodeCharacters);
935
0
  }
936
937
0
  free(glyph);
938
0
}