diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 15379da40f4..181a0a094da 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -109,6 +109,7 @@ extern HRESULT create_fontface(DWRITE_FONT_FACE_TYPE,UINT32,IDWriteFontFile* con extern HRESULT create_font_collection(IDWriteFactory*,IDWriteFontFileEnumerator*,BOOL,IDWriteFontCollection**) DECLSPEC_HIDDEN; extern BOOL is_system_collection(IDWriteFontCollection*) DECLSPEC_HIDDEN; extern HRESULT get_local_refkey(const WCHAR*,const FILETIME*,void**,UINT32*) DECLSPEC_HIDDEN; +extern HRESULT get_filestream_from_file(IDWriteFontFile*,IDWriteFontFileStream**) DECLSPEC_HIDDEN; /* Opentype font table functions */ extern HRESULT opentype_analyze_font(IDWriteFontFileStream*,UINT32*,DWRITE_FONT_FILE_TYPE*,DWRITE_FONT_FACE_TYPE*,BOOL*) DECLSPEC_HIDDEN; @@ -124,10 +125,10 @@ extern HRESULT bidi_computelevels(const WCHAR*,UINT32,UINT8,UINT8*,UINT8*) DECLS extern WCHAR bidi_get_mirrored_char(WCHAR) DECLSPEC_HIDDEN; /* FreeType integration */ -struct ft_fontface; extern BOOL init_freetype(void) DECLSPEC_HIDDEN; -extern HRESULT alloc_ft_fontface(const void*,UINT32,UINT32,struct ft_fontface**) DECLSPEC_HIDDEN; -extern void release_ft_fontface(struct ft_fontface*) DECLSPEC_HIDDEN; +extern void release_freetype(void) DECLSPEC_HIDDEN; +extern HRESULT freetype_get_design_glyph_metrics(IDWriteFontFace2*,UINT16,UINT16,DWRITE_GLYPH_METRICS*) DECLSPEC_HIDDEN; +extern void freetype_notify_cacheremove(IDWriteFontFace2*) DECLSPEC_HIDDEN; /* Glyph shaping */ enum SCRIPT_JUSTIFY diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index 8b57809a7d1..e7712d7bde3 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -97,6 +97,11 @@ struct dwrite_fonttable { UINT32 size; }; +#define GLYPH_BLOCK_SHIFT 8 +#define GLYPH_BLOCK_SIZE (1UL << GLYPH_BLOCK_SHIFT) +#define GLYPH_BLOCK_MASK (GLYPH_BLOCK_SIZE - 1) +#define GLYPH_MAX 65536 + struct dwrite_fontface { IDWriteFontFace2 IDWriteFontFace2_iface; LONG ref; @@ -111,7 +116,7 @@ struct dwrite_fontface { DWRITE_FONT_METRICS1 metrics; struct dwrite_fonttable cmap; - struct ft_fontface *ft; + DWRITE_GLYPH_METRICS *glyphs[GLYPH_MAX/GLYPH_BLOCK_SIZE]; }; struct dwrite_fontfile { @@ -124,8 +129,6 @@ struct dwrite_fontfile { IDWriteFontFileStream *stream; }; -static HRESULT get_filestream_from_file(IDWriteFontFile*,IDWriteFontFileStream**); - static inline struct dwrite_fontface *impl_from_IDWriteFontFace2(IDWriteFontFace2 *iface) { return CONTAINING_RECORD(iface, struct dwrite_fontface, IDWriteFontFace2_iface); @@ -151,6 +154,31 @@ static inline struct dwrite_fontcollection *impl_from_IDWriteFontCollection(IDWr return CONTAINING_RECORD(iface, struct dwrite_fontcollection, IDWriteFontCollection_iface); } +static HRESULT get_cached_glyph_metrics(struct dwrite_fontface *fontface, UINT16 glyph, DWRITE_GLYPH_METRICS *metrics) +{ + static const DWRITE_GLYPH_METRICS nil; + DWRITE_GLYPH_METRICS *block = fontface->glyphs[glyph >> GLYPH_BLOCK_SHIFT]; + + if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(DWRITE_GLYPH_METRICS))) return S_FALSE; + memcpy(metrics, &block[glyph & GLYPH_BLOCK_MASK], sizeof(*metrics)); + return S_OK; +} + +static HRESULT set_cached_glyph_metrics(struct dwrite_fontface *fontface, UINT16 glyph, DWRITE_GLYPH_METRICS *metrics) +{ + DWRITE_GLYPH_METRICS **block = &fontface->glyphs[glyph >> GLYPH_BLOCK_SHIFT]; + + if (!*block) { + /* start new block */ + *block = heap_alloc_zero(sizeof(*metrics) * GLYPH_BLOCK_SIZE); + if (!*block) + return E_OUTOFMEMORY; + } + + memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], metrics, sizeof(*metrics)); + return S_OK; +} + static inline void* get_fontface_cmap(struct dwrite_fontface *fontface) { BOOL exists = FALSE; @@ -247,7 +275,11 @@ static ULONG WINAPI dwritefontface_Release(IDWriteFontFace2 *iface) if (This->files[i]) IDWriteFontFile_Release(This->files[i]); } - release_ft_fontface(This->ft); + + for (i = 0; i < sizeof(This->glyphs)/sizeof(This->glyphs[0]); i++) + heap_free(This->glyphs[i]); + + freetype_notify_cacheremove(iface); heap_free(This); } @@ -321,11 +353,34 @@ static UINT16 WINAPI dwritefontface_GetGlyphCount(IDWriteFontFace2 *iface) } static HRESULT WINAPI dwritefontface_GetDesignGlyphMetrics(IDWriteFontFace2 *iface, - UINT16 const *glyph_indices, UINT32 glyph_count, DWRITE_GLYPH_METRICS *metrics, BOOL is_sideways) + UINT16 const *glyphs, UINT32 glyph_count, DWRITE_GLYPH_METRICS *ret, BOOL is_sideways) { struct dwrite_fontface *This = impl_from_IDWriteFontFace2(iface); - FIXME("(%p)->(%p %u %p %d): stub\n", This, glyph_indices, glyph_count, metrics, is_sideways); - return E_NOTIMPL; + HRESULT hr; + UINT32 i; + + TRACE("(%p)->(%p %u %p %d)\n", This, glyphs, glyph_count, ret, is_sideways); + + if (!glyphs) + return E_INVALIDARG; + + if (is_sideways) + FIXME("sideways metrics are not supported.\n"); + + for (i = 0; i < glyph_count; i++) { + DWRITE_GLYPH_METRICS metrics; + + hr = get_cached_glyph_metrics(This, glyphs[i], &metrics); + if (hr != S_OK) { + freetype_get_design_glyph_metrics(iface, This->metrics.designUnitsPerEm, glyphs[i], &metrics); + hr = set_cached_glyph_metrics(This, glyphs[i], &metrics); + if (FAILED(hr)) + return hr; + } + ret[i] = metrics; + } + + return S_OK; } static HRESULT WINAPI dwritefontface_GetGlyphIndices(IDWriteFontFace2 *iface, UINT32 const *codepoints, @@ -1433,7 +1488,7 @@ static HRESULT init_font_collection(struct dwrite_fontcollection *collection, BO return S_OK; } -static HRESULT get_filestream_from_file(IDWriteFontFile *file, IDWriteFontFileStream **stream) +HRESULT get_filestream_from_file(IDWriteFontFile *file, IDWriteFontFileStream **stream) { IDWriteFontFileLoader *loader; const void *key; @@ -1932,10 +1987,7 @@ HRESULT create_fontface(DWRITE_FONT_FACE_TYPE facetype, UINT32 files_number, IDW DWRITE_FONT_SIMULATIONS simulations, IDWriteFontFace2 **ret) { struct dwrite_fontface *fontface; - const void *data_ptr; HRESULT hr = S_OK; - void *context; - UINT64 size; int i; *ret = NULL; @@ -1963,7 +2015,7 @@ HRESULT create_fontface(DWRITE_FONT_FACE_TYPE facetype, UINT32 files_number, IDW fontface->cmap.size = 0; fontface->index = index; fontface->simulations = simulations; - fontface->ft = NULL; + memset(fontface->glyphs, 0, sizeof(fontface->glyphs)); for (i = 0; i < fontface->file_count; i++) { hr = get_stream_from_file(font_files[i], &fontface->streams[i]); @@ -1978,25 +2030,8 @@ HRESULT create_fontface(DWRITE_FONT_FACE_TYPE facetype, UINT32 files_number, IDW get_font_properties_from_stream(fontface->streams[0], facetype, index, &fontface->metrics, NULL, NULL, NULL); - hr = IDWriteFontFileStream_GetFileSize(fontface->streams[0], &size); - if (FAILED(hr)) - goto fail; - - hr = IDWriteFontFileStream_ReadFileFragment(fontface->streams[0], &data_ptr, 0, size, &context); - if (FAILED(hr)) - goto fail; - - hr = alloc_ft_fontface(data_ptr, size, fontface->index, &fontface->ft); - IDWriteFontFileStream_ReleaseFileFragment(fontface->streams[0], context); - if (FAILED(hr)) - goto fail; - *ret = &fontface->IDWriteFontFace2_iface; return S_OK; - -fail: - IDWriteFontFace2_Release(&fontface->IDWriteFontFace2_iface); - return hr; } /* IDWriteLocalFontFileLoader and its required IDWriteFontFileStream */ diff --git a/dlls/dwrite/freetype.c b/dlls/dwrite/freetype.c index 86d85b34739..66dd1c98cf0 100644 --- a/dlls/dwrite/freetype.c +++ b/dlls/dwrite/freetype.c @@ -18,15 +18,19 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define COBJMACROS + #include "config.h" #include "wine/port.h" #ifdef HAVE_FT2BUILD_H #include +#include FT_CACHE_H #include FT_FREETYPE_H #endif /* HAVE_FT2BUILD_H */ #include "windef.h" +#include "dwrite_2.h" #include "wine/library.h" #include "wine/debug.h" @@ -36,8 +40,18 @@ WINE_DEFAULT_DEBUG_CHANNEL(dwrite); #ifdef HAVE_FREETYPE +static CRITICAL_SECTION freetype_cs; +static CRITICAL_SECTION_DEBUG critsect_debug = +{ + 0, 0, &freetype_cs, + { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": freetype_cs") } +}; +static CRITICAL_SECTION freetype_cs = { &critsect_debug, -1, 0, 0, 0, 0 }; + static void *ft_handle = NULL; static FT_Library library = 0; +static FTC_Manager cache_manager = 0; typedef struct { FT_Int major; @@ -46,16 +60,62 @@ typedef struct } FT_Version_t; #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL -MAKE_FUNCPTR(FT_Done_Face); +MAKE_FUNCPTR(FT_Done_FreeType); MAKE_FUNCPTR(FT_Init_FreeType); MAKE_FUNCPTR(FT_Library_Version); +MAKE_FUNCPTR(FT_Load_Glyph); MAKE_FUNCPTR(FT_New_Memory_Face); +MAKE_FUNCPTR(FTC_Manager_New); +MAKE_FUNCPTR(FTC_Manager_Done); +MAKE_FUNCPTR(FTC_Manager_LookupSize); +MAKE_FUNCPTR(FTC_Manager_RemoveFaceID); #undef MAKE_FUNCPTR -struct ft_fontface +static FT_Error face_requester(FTC_FaceID face_id, FT_Library library, FT_Pointer request_data, FT_Face *face) { - FT_Face face; -}; + IDWriteFontFace *fontface = (IDWriteFontFace*)face_id; + IDWriteFontFileStream *stream; + IDWriteFontFile *file; + const void *data_ptr; + UINT32 index, count; + FT_Error fterror; + UINT64 data_size; + void *context; + HRESULT hr; + + *face = NULL; + + count = 1; + hr = IDWriteFontFace_GetFiles(fontface, &count, &file); + if (FAILED(hr)) + return FT_Err_Ok; + + hr = get_filestream_from_file(file, &stream); + IDWriteFontFile_Release(file); + if (FAILED(hr)) + return FT_Err_Ok; + + hr = IDWriteFontFileStream_GetFileSize(stream, &data_size); + if (FAILED(hr)) { + fterror = FT_Err_Invalid_Stream_Read; + goto fail; + } + + hr = IDWriteFontFileStream_ReadFileFragment(stream, &data_ptr, 0, data_size, &context); + if (FAILED(hr)) { + fterror = FT_Err_Invalid_Stream_Read; + goto fail; + } + + index = IDWriteFontFace_GetIndex(fontface); + fterror = pFT_New_Memory_Face(library, data_ptr, data_size, index, face); + IDWriteFontFileStream_ReleaseFileFragment(stream, context); + +fail: + IDWriteFontFileStream_Release(stream); + + return fterror; +} BOOL init_freetype(void) { @@ -68,10 +128,15 @@ BOOL init_freetype(void) } #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(ft_handle, #f, NULL, 0)) == NULL){WARN("Can't find symbol %s\n", #f); goto sym_not_found;} - LOAD_FUNCPTR(FT_Done_Face) + LOAD_FUNCPTR(FT_Done_FreeType) LOAD_FUNCPTR(FT_Init_FreeType) LOAD_FUNCPTR(FT_Library_Version) + LOAD_FUNCPTR(FT_Load_Glyph) LOAD_FUNCPTR(FT_New_Memory_Face) + LOAD_FUNCPTR(FTC_Manager_New) + LOAD_FUNCPTR(FTC_Manager_Done) + LOAD_FUNCPTR(FTC_Manager_LookupSize) + LOAD_FUNCPTR(FTC_Manager_RemoveFaceID) #undef LOAD_FUNCPTR if (pFT_Init_FreeType(&library) != 0) { @@ -82,6 +147,15 @@ BOOL init_freetype(void) } pFT_Library_Version(library, &FT_Version.major, &FT_Version.minor, &FT_Version.patch); + /* init cache manager */ + if (pFTC_Manager_New(library, 0, 0, 0, &face_requester, NULL, &cache_manager) != 0) { + ERR("Failed to init FreeType cache\n"); + pFT_Done_FreeType(library); + wine_dlclose(ft_handle, NULL, 0); + ft_handle = NULL; + return FALSE; + } + TRACE("FreeType version is %d.%d.%d\n", FT_Version.major, FT_Version.minor, FT_Version.patch); return TRUE; @@ -92,30 +166,48 @@ sym_not_found: return FALSE; } -HRESULT alloc_ft_fontface(const void *data_ptr, UINT32 data_size, UINT32 index, struct ft_fontface **ftface) +void release_freetype(void) { - FT_Face face; - FT_Error err; - - *ftface = heap_alloc_zero(sizeof(struct ft_fontface)); - if (!*ftface) - return E_OUTOFMEMORY; - - err = pFT_New_Memory_Face(library, data_ptr, data_size, index, &face); - if (err) { - ERR("FT_New_Memory_Face rets %d\n", err); - return E_FAIL; - } - (*ftface)->face = face; - - return S_OK; + pFTC_Manager_Done(cache_manager); + pFT_Done_FreeType(library); } -void release_ft_fontface(struct ft_fontface *ftface) +void freetype_notify_cacheremove(IDWriteFontFace2 *fontface) { - if (!ftface) return; - pFT_Done_Face(ftface->face); - heap_free(ftface); + EnterCriticalSection(&freetype_cs); + pFTC_Manager_RemoveFaceID(cache_manager, fontface); + LeaveCriticalSection(&freetype_cs); +} + +HRESULT freetype_get_design_glyph_metrics(IDWriteFontFace2 *fontface, UINT16 unitsperEm, UINT16 glyph, DWRITE_GLYPH_METRICS *ret) +{ + FTC_ScalerRec scaler; + FT_Size size; + + scaler.face_id = fontface; + scaler.width = unitsperEm; + scaler.height = unitsperEm; + scaler.pixel = 1; + scaler.x_res = 0; + scaler.y_res = 0; + + EnterCriticalSection(&freetype_cs); + if (pFTC_Manager_LookupSize(cache_manager, &scaler, &size) == 0) { + if (pFT_Load_Glyph(size->face, glyph, FT_LOAD_NO_SCALE) == 0) { + FT_Glyph_Metrics *metrics = &size->face->glyph->metrics; + + ret->leftSideBearing = metrics->horiBearingX; + ret->advanceWidth = metrics->horiAdvance; + ret->rightSideBearing = metrics->horiAdvance - metrics->horiBearingX - metrics->width; + ret->topSideBearing = metrics->vertBearingY; + ret->advanceHeight = metrics->vertAdvance; + ret->bottomSideBearing = metrics->vertAdvance - metrics->vertBearingY - metrics->height; + ret->verticalOriginY = metrics->height + metrics->vertBearingY; + } + } + LeaveCriticalSection(&freetype_cs); + + return S_OK; } #else /* HAVE_FREETYPE */ @@ -125,14 +217,17 @@ BOOL init_freetype(void) return FALSE; } -HRESULT alloc_ft_fontface(const void *data_ptr, UINT32 data_size, UINT32 index, struct ft_fontface **face) +void release_freetype(void) { - *face = NULL; - return S_FALSE; } -void release_ft_fontface(struct ft_fontface *face) +void freetype_notify_cacheremove(IDWriteFontFace2 *fontface) { } +HRESULT freetype_get_design_glyph_metrics(IDWriteFontFace2 *fontface, UINT16 unitsperEm, UINT16 glyph, DWRITE_GLYPH_METRICS *ret) +{ + return E_NOTIMPL; +} + #endif /* HAVE_FREETYPE */ diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index 4af8338cbcd..58b536e3aba 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -2528,6 +2528,56 @@ static void test_ReadFileFragment(void) DeleteFileW(test_fontfile); } +static void test_GetDesignGlyphMetrics(void) +{ + DWRITE_GLYPH_METRICS metrics[2]; + IDWriteFontFace *fontface; + IDWriteFactory *factory; + IDWriteFontFile *file; + UINT16 indices[2]; + UINT32 codepoint; + HRESULT hr; + + factory = create_factory(); + + create_testfontfile(test_fontfile); + + hr = IDWriteFactory_CreateFontFileReference(factory, test_fontfile, NULL, &file); + ok(hr == S_OK, "got 0x%08x\n",hr); + + hr = IDWriteFactory_CreateFontFace(factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &file, + 0, DWRITE_FONT_SIMULATIONS_NONE, &fontface); + ok(hr == S_OK, "got 0x%08x\n",hr); + IDWriteFontFile_Release(file); + + codepoint = 'A'; + indices[0] = 0; + hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &indices[0]); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(indices[0] > 0, "got %u\n", indices[0]); + + hr = IDWriteFontFace_GetDesignGlyphMetrics(fontface, NULL, 0, metrics, FALSE); + ok(hr == E_INVALIDARG, "got 0x%08x\n",hr); + + hr = IDWriteFontFace_GetDesignGlyphMetrics(fontface, NULL, 1, metrics, FALSE); + ok(hr == E_INVALIDARG, "got 0x%08x\n",hr); + + hr = IDWriteFontFace_GetDesignGlyphMetrics(fontface, indices, 0, metrics, FALSE); + ok(hr == S_OK, "got 0x%08x\n",hr); + + /* missing glyphs are ignored */ + indices[1] = 1; + memset(metrics, 0xcc, sizeof(metrics)); + hr = IDWriteFontFace_GetDesignGlyphMetrics(fontface, indices, 2, metrics, FALSE); + ok(hr == S_OK, "got 0x%08x\n",hr); + ok(metrics[0].advanceWidth == 1000, "got %d\n", metrics[0].advanceWidth); + ok(metrics[1].advanceWidth == 0, "got %d\n", metrics[1].advanceWidth); + + IDWriteFontFace_Release(fontface); + IDWriteFactory_Release(factory); + DeleteFileW(test_fontfile); +} + START_TEST(font) { IDWriteFactory *factory; @@ -2561,6 +2611,7 @@ START_TEST(font) test_ConvertFontToLOGFONT(); test_CreateStreamFromKey(); test_ReadFileFragment(); + test_GetDesignGlyphMetrics(); IDWriteFactory_Release(factory); }