diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 10617983b69..e0373dc2891 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -22,6 +22,7 @@ #include "wine/debug.h" #include "wine/list.h" +#include "wine/rbtree.h" #define MS_GSUB_TAG DWRITE_MAKE_OPENTYPE_TAG('G','S','U','B') #define MS_GPOS_TAG DWRITE_MAKE_OPENTYPE_TAG('G','P','O','S') @@ -240,6 +241,13 @@ struct dwrite_fontface font_object_handle font_object; void *data_context; p_dwrite_fontface_get_font_object get_font_object; + struct + { + struct wine_rb_tree tree; + struct list mru; + size_t max_size; + size_t size; + } cache; CRITICAL_SECTION cs; USHORT simulations; @@ -725,8 +733,8 @@ struct font_backend_funcs int (CDECL *get_glyph_outline)(font_object_handle object, float emsize, unsigned int simulations, UINT16 glyph, struct dwrite_outline *outline); UINT16 (CDECL *get_glyph_count)(font_object_handle object); - INT32 (CDECL *get_glyph_advance)(void *key, float em_size, UINT16 index, DWRITE_MEASURING_MODE measuring_mode, - BOOL *has_contours); + INT32 (CDECL *get_glyph_advance)(font_object_handle object, float em_size, UINT16 glyph, + DWRITE_MEASURING_MODE measuring_mode, BOOL *has_contours); void (CDECL *get_glyph_bbox)(struct dwrite_glyphbitmap *bitmap_desc); BOOL (CDECL *get_glyph_bitmap)(struct dwrite_glyphbitmap *bitmap_desc); void (CDECL *get_design_glyph_metrics)(font_object_handle object, UINT16 upem, UINT16 ascent, unsigned int simulations, diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index 174ab7a3ded..f6ac43e65ac 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -52,6 +52,111 @@ void dwrite_fontface_get_glyph_bbox(struct dwrite_glyphbitmap *bitmap) font_funcs->get_glyph_bbox(bitmap); } +struct cache_key +{ + float size; + unsigned short glyph; + unsigned short mode; +}; + +struct cache_entry +{ + struct wine_rb_entry entry; + struct list mru; + struct cache_key key; + float advance; + unsigned int has_contours : 1; +}; + +static struct cache_entry * fontface_get_cache_entry(struct dwrite_fontface *fontface, const struct cache_key *key) +{ + struct cache_entry *entry; + struct wine_rb_entry *e; + + if (!(e = wine_rb_get(&fontface->cache.tree, key))) return NULL; + entry = WINE_RB_ENTRY_VALUE(e, struct cache_entry, entry); + list_remove(&entry->mru); + list_add_head(&fontface->cache.mru, &entry->mru); + return WINE_RB_ENTRY_VALUE(entry, struct cache_entry, entry); +} + +static size_t fontface_get_cache_entry_size(const struct cache_entry *entry) +{ + return sizeof(*entry); +} + +static float fontface_get_glyph_advance(struct dwrite_fontface *fontface, float fontsize, unsigned short glyph, + unsigned short mode, BOOL *has_contours) +{ + struct cache_key key = { .size = fontsize, .glyph = glyph, .mode = mode }; + struct cache_entry *entry, *old_entry; + size_t size; + BOOL value; + + if (!(entry = fontface_get_cache_entry(fontface, &key))) + { + if (!(entry = calloc(1, sizeof(*entry)))) + return 0.0f; + + entry->advance = font_funcs->get_glyph_advance(fontface->get_font_object(fontface), fontsize, glyph, mode, &value); + entry->has_contours = !!value; + entry->key = key; + + size = fontface_get_cache_entry_size(entry); + if ((fontface->cache.size + size > fontface->cache.max_size) && !list_empty(&fontface->cache.mru)) + { + old_entry = LIST_ENTRY(list_tail(&fontface->cache.mru), struct cache_entry, mru); + fontface->cache.size -= fontface_get_cache_entry_size(old_entry); + wine_rb_remove(&fontface->cache.tree, &old_entry->entry); + list_remove(&old_entry->mru); + free(old_entry); + } + + list_add_head(&fontface->cache.mru, &entry->mru); + + if (wine_rb_put(&fontface->cache.tree, &key, &entry->entry) == -1) + { + WARN("Failed to add cache entry.\n"); + return 0.0f; + } + + fontface->cache.size += size; + } + + *has_contours = entry->has_contours; + return entry->advance; +} + +static int fontface_cache_compare(const void *k, const struct wine_rb_entry *e) +{ + const struct cache_entry *entry = WINE_RB_ENTRY_VALUE(e, const struct cache_entry, entry); + const struct cache_key *key = k, *key2 = &entry->key; + + if (key->size != key2->size) return key->size < key2->size ? -1 : 1; + if (key->glyph != key2->glyph) return (int)key->glyph - (int)key2->glyph; + if (key->mode != key2->mode) return (int)key->mode - (int)key2->mode; + return 0; +} + +static void fontface_cache_init(struct dwrite_fontface *fontface) +{ + wine_rb_init(&fontface->cache.tree, fontface_cache_compare); + list_init(&fontface->cache.mru); + fontface->cache.max_size = 0x8000; +} + +static void fontface_cache_clear(struct dwrite_fontface *fontface) +{ + struct cache_entry *entry, *entry2; + + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &fontface->cache.mru, struct cache_entry, mru) + { + list_remove(&entry->mru); + free(entry); + } + memset(&fontface->cache, 0, sizeof(fontface->cache)); +} + struct dwrite_font_propvec { FLOAT stretch; FLOAT style; @@ -656,6 +761,7 @@ static ULONG WINAPI dwritefontface_Release(IDWriteFontFace5 *iface) IDWriteFontFileStream_ReleaseFileFragment(fontface->stream, fontface->data_context); IDWriteFontFileStream_Release(fontface->stream); } + fontface_cache_clear(fontface); dwrite_cmap_release(&fontface->cmap); IDWriteFactory7_Release(fontface->factory); @@ -1019,7 +1125,7 @@ static HRESULT WINAPI dwritefontface_GetGdiCompatibleGlyphMetrics(IDWriteFontFac UINT32 adjustment = fontface_get_horz_metric_adjustment(fontface); DWRITE_MEASURING_MODE mode; FLOAT scale, size; - HRESULT hr; + HRESULT hr = S_OK; UINT32 i; TRACE("%p, %.8e, %.8e, %p, %d, %p, %u, %p, %d.\n", iface, emSize, ppdip, m, use_gdi_natural, glyphs, @@ -1032,16 +1138,18 @@ static HRESULT WINAPI dwritefontface_GetGdiCompatibleGlyphMetrics(IDWriteFontFac scale = size / fontface->metrics.designUnitsPerEm; mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC; - for (i = 0; i < glyph_count; i++) { + EnterCriticalSection(&fontface->cs); + for (i = 0; i < glyph_count; ++i) + { DWRITE_GLYPH_METRICS *ret = metrics + i; DWRITE_GLYPH_METRICS design; BOOL has_contours; hr = IDWriteFontFace5_GetDesignGlyphMetrics(iface, glyphs + i, 1, &design, is_sideways); if (FAILED(hr)) - return hr; + break; - ret->advanceWidth = font_funcs->get_glyph_advance(iface, size, glyphs[i], mode, &has_contours); + ret->advanceWidth = fontface_get_glyph_advance(fontface, size, glyphs[i], mode, &has_contours); if (has_contours) ret->advanceWidth = round_metric(ret->advanceWidth * fontface->metrics.designUnitsPerEm / size + adjustment); else @@ -1056,6 +1164,7 @@ static HRESULT WINAPI dwritefontface_GetGdiCompatibleGlyphMetrics(IDWriteFontFac SCALE_METRIC(verticalOriginY); #undef SCALE_METRIC } + LeaveCriticalSection(&fontface->cs); return S_OK; } @@ -1172,8 +1281,8 @@ static int fontface_get_design_advance(struct dwrite_fontface *fontface, DWRITE_ switch (measuring_mode) { case DWRITE_MEASURING_MODE_NATURAL: - advance = font_funcs->get_glyph_advance(&fontface->IDWriteFontFace5_iface, fontface->metrics.designUnitsPerEm, - glyph, measuring_mode, &has_contours); + advance = fontface_get_glyph_advance(fontface, fontface->metrics.designUnitsPerEm, glyph, + measuring_mode, &has_contours); if (has_contours) advance += adjustment; @@ -1187,8 +1296,7 @@ static int fontface_get_design_advance(struct dwrite_fontface *fontface, DWRITE_ if (transform && memcmp(transform, &identity, sizeof(*transform))) FIXME("Transform is not supported.\n"); - advance = font_funcs->get_glyph_advance(&fontface->IDWriteFontFace5_iface, emsize, glyph, measuring_mode, - &has_contours); + advance = fontface_get_glyph_advance(fontface, emsize, glyph, measuring_mode, &has_contours); if (has_contours) advance = round_metric(advance * fontface->metrics.designUnitsPerEm / emsize + adjustment); else @@ -1212,11 +1320,13 @@ static HRESULT WINAPI dwritefontface1_GetDesignGlyphAdvances(IDWriteFontFace5 *i if (is_sideways) FIXME("sideways mode not supported\n"); + EnterCriticalSection(&fontface->cs); for (i = 0; i < glyph_count; ++i) { advances[i] = fontface_get_design_advance(fontface, DWRITE_MEASURING_MODE_NATURAL, fontface->metrics.designUnitsPerEm, 1.0f, NULL, glyphs[i], is_sideways); } + LeaveCriticalSection(&fontface->cs); return S_OK; } @@ -1243,11 +1353,14 @@ static HRESULT WINAPI dwritefontface1_GetGdiCompatibleGlyphAdvances(IDWriteFontF } measuring_mode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC; + + EnterCriticalSection(&fontface->cs); for (i = 0; i < glyph_count; ++i) { advances[i] = fontface_get_design_advance(fontface, measuring_mode, em_size, ppdip, transform, glyphs[i], is_sideways); } + LeaveCriticalSection(&fontface->cs); return S_OK; } @@ -5101,6 +5214,7 @@ HRESULT create_fontface(const struct fontface_desc *desc, struct list *cached_li fontface->stream = desc->stream; IDWriteFontFileStream_AddRef(fontface->stream); InitializeCriticalSection(&fontface->cs); + fontface_cache_init(fontface); stream_desc.stream = fontface->stream; stream_desc.face_type = desc->face_type; @@ -6027,7 +6141,9 @@ float fontface_get_scaled_design_advance(struct dwrite_fontface *fontface, DWRIT if (is_sideways) FIXME("Sideways mode is not supported.\n"); + EnterCriticalSection(&fontface->cs); advance = fontface_get_design_advance(fontface, measuring_mode, emsize, ppdip, transform, glyph, is_sideways); + LeaveCriticalSection(&fontface->cs); switch (measuring_mode) { diff --git a/dlls/dwrite/freetype.c b/dlls/dwrite/freetype.c index 503f8b04496..a2304f264d5 100644 --- a/dlls/dwrite/freetype.c +++ b/dlls/dwrite/freetype.c @@ -781,30 +781,25 @@ static BOOL CDECL freetype_get_glyph_bitmap(struct dwrite_glyphbitmap *bitmap) return ret; } -static INT32 CDECL freetype_get_glyph_advance(void *key, float emSize, UINT16 index, +static INT32 CDECL freetype_get_glyph_advance(font_object_handle object, float emsize, UINT16 glyph, DWRITE_MEASURING_MODE mode, BOOL *has_contours) { - FTC_ImageTypeRec imagetype; - FT_Glyph glyph; - INT32 advance; + FT_Face face = object; + INT32 advance = 0; + FT_Size size; - imagetype.face_id = key; - imagetype.width = 0; - imagetype.height = emSize; - imagetype.flags = FT_LOAD_DEFAULT; - if (mode == DWRITE_MEASURING_MODE_NATURAL) - imagetype.flags |= FT_LOAD_NO_HINTING; + *has_contours = FALSE; - RtlEnterCriticalSection(&freetype_cs); - if (pFTC_ImageCache_Lookup(image_cache, &imagetype, index, &glyph, NULL) == 0) { - *has_contours = glyph->format == FT_GLYPH_FORMAT_OUTLINE && ((FT_OutlineGlyph)glyph)->outline.n_contours; - advance = glyph->advance.x >> 16; + if (!(size = freetype_set_face_size(face, emsize))) + return 0; + + if (!pFT_Load_Glyph(face, glyph, mode == DWRITE_MEASURING_MODE_NATURAL ? FT_LOAD_NO_HINTING : 0)) + { + advance = face->glyph->advance.x >> 6; + *has_contours = freetype_glyph_has_contours(face); } - else { - *has_contours = FALSE; - advance = 0; - } - RtlLeaveCriticalSection(&freetype_cs); + + pFT_Done_Size(size); return advance; } @@ -863,8 +858,8 @@ static UINT16 CDECL null_get_glyph_count(font_object_handle object) return 0; } -static INT32 CDECL null_get_glyph_advance(void *key, float emSize, UINT16 index, DWRITE_MEASURING_MODE mode, - BOOL *has_contours) +static INT32 CDECL null_get_glyph_advance(font_object_handle object, float emsize, UINT16 glyph, + DWRITE_MEASURING_MODE mode, BOOL *has_contours) { *has_contours = FALSE; return 0;