20#include <freerdp/config.h>
25#include <winpr/assert.h>
26#include <winpr/cast.h>
28#include <freerdp/freerdp.h>
29#include <winpr/stream.h>
31#include <freerdp/log.h>
36#define TAG FREERDP_TAG("cache.glyph")
49} FRAGMENT_CACHE_ENTRY;
53 FRAGMENT_CACHE_ENTRY entries[256];
58 FRAGMENT_CACHE fragCache;
59 GLYPH_CACHE glyphCache[10];
66static rdpGlyph* glyph_cache_get(rdpGlyphCache* glyphCache, UINT32
id, UINT32 index);
69static BOOL glyph_cache_put(rdpGlyphCache* glyphCache, UINT32
id, UINT32 index, rdpGlyph* glyph);
72static const void* glyph_cache_fragment_get(rdpGlyphCache* glyphCache, UINT32 index, UINT32* size);
75static BOOL glyph_cache_fragment_put(rdpGlyphCache* glyphCache, UINT32 index, UINT32 size,
76 const void* fragment);
79static UINT32 update_glyph_offset(
const BYTE* data,
size_t length, UINT32 index, INT32* x, INT32* y,
80 UINT32 ulCharInc, UINT32 flAccel)
82 if ((ulCharInc == 0) && (!(flAccel & SO_CHAR_INC_EQUAL_BM_BASE)))
86 WLog_WARN(TAG,
"glyph offset index out of bound %" PRIu32
" [max %" PRIuz
"]", index,
91 UINT32 offset = data[index++];
96 if (index + 1 < length)
98 offset = data[index++];
99 offset |= ((UINT32)data[index++]) << 8;
102 WLog_WARN(TAG,
"glyph index out of bound %" PRIu32
" [max %" PRIuz
"]", index,
106 if (flAccel & SO_VERTICAL)
107 *y += WINPR_ASSERTING_INT_CAST(int32_t, offset);
109 if (flAccel & SO_HORIZONTAL)
110 *x += WINPR_ASSERTING_INT_CAST(int32_t, offset);
117static BOOL update_process_glyph(rdpContext* context,
const BYTE* data, UINT32 cacheIndex, INT32* x,
118 const INT32* y, UINT32 cacheId, UINT32 flAccel, BOOL fOpRedundant,
124 if (!context || !data || !x || !y || !context->graphics || !context->cache ||
125 !context->cache->glyph)
128 rdpGlyphCache* glyph_cache = context->cache->glyph;
129 rdpGlyph* glyph = glyph_cache_get(glyph_cache, cacheId, cacheIndex);
133 FreeRDP_AllowUnanouncedOrdersFromServer);
135 INT32 dx = glyph->x + *x;
136 INT32 dy = glyph->y + *y;
150 if ((dx <= (bound->x + bound->width)) && (dy <= (bound->y + bound->height)))
152 INT32 dw = WINPR_ASSERTING_INT_CAST(int32_t, glyph->cx) - sx;
153 INT32 dh = WINPR_ASSERTING_INT_CAST(int32_t, glyph->cy) - sy;
155 if ((dw + dx) > (bound->x + bound->width))
156 dw = (bound->x + bound->width) - (dw + dx);
158 if ((dh + dy) > (bound->y + bound->height))
159 dh = (bound->y + bound->height) - (dh + dy);
161 if ((dh > 0) && (dw > 0))
163 if (!glyph->Draw(context, glyph, dx, dy, dw, dh, sx, sy, fOpRedundant))
168 if (flAccel & SO_CHAR_INC_EQUAL_BM_BASE)
169 *x += WINPR_ASSERTING_INT_CAST(int32_t, glyph->cx);
175static BOOL fits_int16(INT32 val)
185static 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,
195 const BYTE* fragments =
nullptr;
196 RDP_RECT bound = WINPR_C_ARRAY_INIT;
199 if (!context || !data || !context->graphics || !context->cache || !context->cache->glyph)
202 rdpGraphics* graphics = context->graphics;
203 WINPR_ASSERT(graphics);
205 WINPR_ASSERT(context->cache);
206 rdpGlyphCache* glyph_cache = context->cache->glyph;
207 WINPR_ASSERT(glyph_cache);
210 rdpGlyph* glyph = graphics->Glyph_Prototype;
254 if (opX + opWidth > (INT64)w)
265 opWidth = WINPR_ASSERTING_INT_CAST(
int, w) - opX;
268 if (bkX + bkWidth > (INT64)w)
279 bkWidth = WINPR_ASSERTING_INT_CAST(
int, w) - bkX;
283 if (!fits_int16(bkX) || !fits_int16(bkY) || !fits_int16(bkWidth) || !fits_int16(bkHeight))
286 bound.x = WINPR_ASSERTING_INT_CAST(INT16, bkX);
287 bound.y = WINPR_ASSERTING_INT_CAST(INT16, bkY);
288 bound.width = WINPR_ASSERTING_INT_CAST(INT16, bkWidth);
289 bound.height = WINPR_ASSERTING_INT_CAST(INT16, bkHeight);
291 if (!glyph->BeginDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor, fOpRedundant))
294 if (!IFCALLRESULT(TRUE, glyph->SetBounds, context, bkX, bkY, bkWidth, bkHeight))
297 while (index < length)
299 const UINT32 op = data[index++];
303 case GLYPH_FRAGMENT_USE:
304 if (index + 1 > length)
308 fragments = (
const BYTE*)glyph_cache_fragment_get(glyph_cache,
id, &size);
310 if (fragments ==
nullptr)
313 for (UINT32 n = 0; n < size;)
315 const UINT32 fop = fragments[n++];
316 n = update_glyph_offset(fragments, size, n, &x, &y, ulCharInc, flAccel);
318 if (!update_process_glyph(context, fragments, fop, &x, &y, cacheId, flAccel,
319 fOpRedundant, &bound))
325 case GLYPH_FRAGMENT_ADD:
326 if (index + 2 > length)
330 size = data[index++];
331 if (size > length - index)
333 if (!glyph_cache_fragment_put(glyph_cache,
id, size, data))
338 index = update_glyph_offset(data, length, index, &x, &y, ulCharInc, flAccel);
340 if (!update_process_glyph(context, data, op, &x, &y, cacheId, flAccel,
341 fOpRedundant, &bound))
348 if (!glyph->EndDraw(context, opX, opY, opWidth, opHeight, bgcolor, fgcolor))
359static BOOL update_gdi_glyph_index(rdpContext* context,
GLYPH_INDEX_ORDER* glyphIndex)
366 if (!context || !glyphIndex || !context->cache)
369 if (glyphIndex->bkRight > glyphIndex->bkLeft)
370 bkWidth = glyphIndex->bkRight - glyphIndex->bkLeft + 1;
372 if (glyphIndex->opRight > glyphIndex->opLeft)
373 opWidth = glyphIndex->opRight - glyphIndex->opLeft + 1;
375 if (glyphIndex->bkBottom > glyphIndex->bkTop)
376 bkHeight = glyphIndex->bkBottom - glyphIndex->bkTop + 1;
378 if (glyphIndex->opBottom > glyphIndex->opTop)
379 opHeight = glyphIndex->opBottom - glyphIndex->opTop + 1;
381 return update_process_glyph_fragments(
382 context, glyphIndex->data, glyphIndex->cbData, glyphIndex->cacheId, glyphIndex->ulCharInc,
383 glyphIndex->flAccel, glyphIndex->backColor, glyphIndex->foreColor, glyphIndex->x,
384 glyphIndex->y, glyphIndex->bkLeft, glyphIndex->bkTop, bkWidth, bkHeight, glyphIndex->opLeft,
385 glyphIndex->opTop, opWidth, opHeight,
386 WINPR_ASSERTING_INT_CAST(int32_t, glyphIndex->fOpRedundant));
390static BOOL update_gdi_fast_index(rdpContext* context,
const FAST_INDEX_ORDER* fastIndex)
398 if (!context || !fastIndex || !context->cache)
401 INT32 opLeft = fastIndex->opLeft;
402 INT32 opTop = fastIndex->opTop;
403 INT32 opRight = fastIndex->opRight;
404 INT32 opBottom = fastIndex->opBottom;
405 INT32 x = fastIndex->x;
406 INT32 y = fastIndex->y;
408 if (opBottom == -32768)
410 BYTE flags = (BYTE)(opTop & 0x0F);
413 opBottom = fastIndex->bkBottom;
416 opRight = fastIndex->bkRight;
419 opTop = fastIndex->bkTop;
422 opLeft = fastIndex->bkLeft;
426 opLeft = fastIndex->bkLeft;
429 opRight = fastIndex->bkRight;
438 x = fastIndex->bkLeft;
441 y = fastIndex->bkTop;
443 if (fastIndex->bkRight > fastIndex->bkLeft)
444 bkWidth = fastIndex->bkRight - fastIndex->bkLeft + 1;
446 if (fastIndex->bkBottom > fastIndex->bkTop)
447 bkHeight = fastIndex->bkBottom - fastIndex->bkTop + 1;
449 if (opRight > opLeft)
450 opWidth = opRight - opLeft + 1;
452 if (opBottom > opTop)
453 opHeight = opBottom - opTop + 1;
455 if (!update_process_glyph_fragments(
456 context, fastIndex->data, fastIndex->cbData, fastIndex->cacheId, fastIndex->ulCharInc,
457 fastIndex->flAccel, fastIndex->backColor, fastIndex->foreColor, x, y, fastIndex->bkLeft,
458 fastIndex->bkTop, bkWidth, bkHeight, opLeft, opTop, opWidth, opHeight, FALSE))
467static BOOL update_gdi_fast_glyph(rdpContext* context,
const FAST_GLYPH_ORDER* fastGlyph)
471 BYTE text_data[4] = WINPR_C_ARRAY_INIT;
480 rdpCache* cache =
nullptr;
482 if (!context || !fastGlyph || !context->cache)
485 cache = context->cache;
486 opLeft = fastGlyph->opLeft;
487 opTop = fastGlyph->opTop;
488 opRight = fastGlyph->opRight;
489 opBottom = fastGlyph->opBottom;
493 if (opBottom == -32768)
495 BYTE flags = (BYTE)(opTop & 0x0F);
498 opBottom = fastGlyph->bkBottom;
501 opRight = fastGlyph->bkRight;
504 opTop = fastGlyph->bkTop;
507 opLeft = fastGlyph->bkLeft;
511 opLeft = fastGlyph->bkLeft;
514 opRight = fastGlyph->bkRight;
521 x = fastGlyph->bkLeft;
524 y = fastGlyph->bkTop;
526 if ((fastGlyph->cbData > 1) && (fastGlyph->glyphData.aj))
529 rdpGlyph* glyph =
nullptr;
532 glyph = Glyph_Alloc(context, glyphData->x, glyphData->y, glyphData->cx, glyphData->cy,
533 glyphData->cb, glyphData->aj);
538 if (!glyph_cache_put(cache->glyph, fastGlyph->cacheId, fastGlyph->data[0], glyph))
540 glyph->Free(context, glyph);
545 text_data[0] = fastGlyph->data[0];
548 if (fastGlyph->bkRight > fastGlyph->bkLeft)
549 bkWidth = fastGlyph->bkRight - fastGlyph->bkLeft + 1;
551 if (fastGlyph->bkBottom > fastGlyph->bkTop)
552 bkHeight = fastGlyph->bkBottom - fastGlyph->bkTop + 1;
554 if (opRight > opLeft)
555 opWidth = opRight - opLeft + 1;
557 if (opBottom > opTop)
558 opHeight = opBottom - opTop + 1;
560 return update_process_glyph_fragments(
561 context, text_data,
sizeof(text_data), fastGlyph->cacheId, fastGlyph->ulCharInc,
562 fastGlyph->flAccel, fastGlyph->backColor, fastGlyph->foreColor, x, y, fastGlyph->bkLeft,
563 fastGlyph->bkTop, bkWidth, bkHeight, opLeft, opTop, opWidth, opHeight, FALSE);
567static BOOL update_gdi_cache_glyph(rdpContext* context,
const CACHE_GLYPH_ORDER* cacheGlyph)
569 if (!context || !cacheGlyph || !context->cache)
572 rdpCache* cache = context->cache;
574 for (
size_t i = 0; i < cacheGlyph->cGlyphs; i++)
576 const GLYPH_DATA* glyph_data = &cacheGlyph->glyphData[i];
577 rdpGlyph* glyph = Glyph_Alloc(context, glyph_data->x, glyph_data->y, glyph_data->cx,
578 glyph_data->cy, glyph_data->cb, glyph_data->aj);
582 if (!glyph_cache_put(cache->glyph, cacheGlyph->cacheId, glyph_data->cacheIndex, glyph))
584 glyph->Free(context, glyph);
593static BOOL update_gdi_cache_glyph_v2(rdpContext* context,
const CACHE_GLYPH_V2_ORDER* cacheGlyphV2)
595 if (!context || !cacheGlyphV2 || !context->cache)
598 rdpCache* cache = context->cache;
600 for (
size_t i = 0; i < cacheGlyphV2->cGlyphs; i++)
602 const GLYPH_DATA_V2* glyphData = &cacheGlyphV2->glyphData[i];
603 rdpGlyph* glyph = Glyph_Alloc(context, glyphData->x, glyphData->y, glyphData->cx,
604 glyphData->cy, glyphData->cb, glyphData->aj);
609 if (!glyph_cache_put(cache->glyph, cacheGlyphV2->cacheId, glyphData->cacheIndex, glyph))
611 glyph->Free(context, glyph);
619rdpGlyph* glyph_cache_get(rdpGlyphCache* glyphCache, UINT32
id, UINT32 index)
621 WINPR_ASSERT(glyphCache);
623 WLog_Print(glyphCache->log, WLOG_DEBUG,
"GlyphCacheGet: id: %" PRIu32
" index: %" PRIu32
"",
id,
626 if (
id >= ARRAYSIZE(glyphCache->glyphCache))
628 WLog_ERR(TAG,
"invalid glyph cache id: %" PRIu32
"",
id);
632 GLYPH_CACHE* cache = &glyphCache->glyphCache[id];
633 if (index >= cache->number)
635 WLog_ERR(TAG,
"index %" PRIu32
" out of range for cache id: %" PRIu32
"", index,
id);
639 rdpGlyph* glyph = cache->entries[index];
641 WLog_ERR(TAG,
"no glyph found at cache index: %" PRIu32
" in cache id: %" PRIu32
"", index,
647BOOL glyph_cache_put(rdpGlyphCache* glyphCache, UINT32
id, UINT32 index, rdpGlyph* glyph)
649 WINPR_ASSERT(glyphCache);
651 if (
id >= ARRAYSIZE(glyphCache->glyphCache))
653 WLog_ERR(TAG,
"invalid glyph cache id: %" PRIu32
"",
id);
657 GLYPH_CACHE* cache = &glyphCache->glyphCache[id];
658 if (index >= cache->number)
660 WLog_ERR(TAG,
"invalid glyph cache index: %" PRIu32
" in cache id: %" PRIu32
"", index,
id);
664 WLog_Print(glyphCache->log, WLOG_DEBUG,
"GlyphCachePut: id: %" PRIu32
" index: %" PRIu32
"",
id,
666 rdpGlyph* prevGlyph = cache->entries[index];
670 WINPR_ASSERT(prevGlyph->Free);
671 prevGlyph->Free(glyphCache->context, prevGlyph);
674 cache->entries[index] = glyph;
678const void* glyph_cache_fragment_get(rdpGlyphCache* glyphCache, UINT32 index, UINT32* size)
680 void* fragment =
nullptr;
682 WINPR_ASSERT(glyphCache);
683 WINPR_ASSERT(glyphCache->fragCache.entries);
687 WLog_ERR(TAG,
"invalid glyph cache fragment index: %" PRIu32
"", index);
691 fragment = glyphCache->fragCache.entries[index].fragment;
692 *size = (BYTE)glyphCache->fragCache.entries[index].size;
693 WLog_Print(glyphCache->log, WLOG_DEBUG,
694 "GlyphCacheFragmentGet: index: %" PRIu32
" size: %" PRIu32
"", index, *size);
697 WLog_ERR(TAG,
"invalid glyph fragment at index:%" PRIu32
"", index);
702BOOL glyph_cache_fragment_put(rdpGlyphCache* glyphCache, UINT32 index, UINT32 size,
703 const void* fragment)
705 WINPR_ASSERT(glyphCache);
706 WINPR_ASSERT(glyphCache->fragCache.entries);
710 WLog_ERR(TAG,
"invalid glyph cache fragment index: %" PRIu32
"", index);
717 void* copy = malloc(size);
722 WLog_Print(glyphCache->log, WLOG_DEBUG,
723 "GlyphCacheFragmentPut: index: %" PRIu32
" size: %" PRIu32
"", index, size);
724 CopyMemory(copy, fragment, size);
726 void* prevFragment = glyphCache->fragCache.entries[index].fragment;
727 glyphCache->fragCache.entries[index].fragment = copy;
728 glyphCache->fragCache.entries[index].size = size;
733void glyph_cache_register_callbacks(rdpUpdate* update)
735 WINPR_ASSERT(update);
736 WINPR_ASSERT(update->context);
737 WINPR_ASSERT(update->primary);
738 WINPR_ASSERT(update->secondary);
742 update->primary->GlyphIndex = update_gdi_glyph_index;
743 update->primary->FastIndex = update_gdi_fast_index;
744 update->primary->FastGlyph = update_gdi_fast_glyph;
745 update->secondary->CacheGlyph = update_gdi_cache_glyph;
746 update->secondary->CacheGlyphV2 = update_gdi_cache_glyph_v2;
750rdpGlyphCache* glyph_cache_new(rdpContext* context)
752 rdpGlyphCache* glyphCache =
nullptr;
753 rdpSettings* settings =
nullptr;
755 WINPR_ASSERT(context);
757 settings = context->settings;
758 WINPR_ASSERT(settings);
760 glyphCache = (rdpGlyphCache*)calloc(1,
sizeof(rdpGlyphCache));
765 glyphCache->log = WLog_Get(
"com.freerdp.cache.glyph");
766 glyphCache->context = context;
768 for (
size_t i = 0; i < 10; i++)
771 freerdp_settings_get_pointer_array(settings, FreeRDP_GlyphCache, i);
772 GLYPH_CACHE* currentCache = &glyphCache->glyphCache[i];
773 currentCache->number = currentGlyph->cacheEntries;
774 currentCache->maxCellSize = currentGlyph->cacheMaximumCellSize;
775 currentCache->entries = (rdpGlyph**)calloc(currentCache->number,
sizeof(rdpGlyph*));
777 if (!currentCache->entries)
783 WINPR_PRAGMA_DIAG_PUSH
784 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
785 glyph_cache_free(glyphCache);
786 WINPR_PRAGMA_DIAG_POP
790void glyph_cache_free(rdpGlyphCache* glyphCache)
794 GLYPH_CACHE* cache = glyphCache->glyphCache;
796 for (
size_t i = 0; i < 10; i++)
798 rdpGlyph** entries = cache[i].entries;
803 for (
size_t j = 0; j < cache[i].number; j++)
805 rdpGlyph* glyph = entries[j];
809 glyph->Free(glyphCache->context, glyph);
810 entries[j] =
nullptr;
814 free((
void*)entries);
815 cache[i].entries =
nullptr;
818 for (
size_t i = 0; i < ARRAYSIZE(glyphCache->fragCache.entries); i++)
820 free(glyphCache->fragCache.entries[i].fragment);
821 glyphCache->fragCache.entries[i].fragment =
nullptr;
832 WINPR_ASSERT(context);
841 for (
size_t x = 0; x < glyph->cGlyphs; x++)
848 const size_t size = src->cb;
849 data->aj = malloc(size);
854 memcpy(data->aj, src->aj, size);
858 if (glyph->unicodeCharacters)
860 if (glyph->cGlyphs == 0)
863 dst->unicodeCharacters = calloc(glyph->cGlyphs,
sizeof(WCHAR));
865 if (!dst->unicodeCharacters)
868 memcpy(dst->unicodeCharacters, glyph->unicodeCharacters,
sizeof(WCHAR) * glyph->cGlyphs);
873 free_cache_glyph_order(context, dst);
877void free_cache_glyph_order(WINPR_ATTR_UNUSED rdpContext* context,
CACHE_GLYPH_ORDER* glyph)
881 for (
size_t x = 0; x < ARRAYSIZE(glyph->glyphData); x++)
882 free(glyph->glyphData[x].aj);
884 free(glyph->unicodeCharacters);
895 WINPR_ASSERT(context);
904 for (
size_t x = 0; x < glyph->cGlyphs; x++)
911 const size_t size = src->cb;
912 data->aj = malloc(size);
917 memcpy(data->aj, src->aj, size);
921 if (glyph->unicodeCharacters)
923 if (glyph->cGlyphs == 0)
926 dst->unicodeCharacters = calloc(glyph->cGlyphs,
sizeof(WCHAR));
928 if (!dst->unicodeCharacters)
931 memcpy(dst->unicodeCharacters, glyph->unicodeCharacters,
sizeof(WCHAR) * glyph->cGlyphs);
936 free_cache_glyph_v2_order(context, dst);
940void free_cache_glyph_v2_order(WINPR_ATTR_UNUSED rdpContext* context,
CACHE_GLYPH_V2_ORDER* glyph)
944 for (
size_t x = 0; x < ARRAYSIZE(glyph->glyphData); x++)
945 free(glyph->glyphData[x].aj);
947 free(glyph->unicodeCharacters);
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.