Commit fb44feb6 authored by Thomas Faber's avatar Thomas Faber Committed by Alexandre Julliard

usp10: Re-use script caches for the same font.

parent 45afe832
...@@ -1833,6 +1833,7 @@ static void test_ScriptShape(HDC hdc) ...@@ -1833,6 +1833,7 @@ static void test_ScriptShape(HDC hdc)
static const WCHAR test3[] = {0x30b7}; static const WCHAR test3[] = {0x30b7};
HRESULT hr; HRESULT hr;
SCRIPT_CACHE sc = NULL; SCRIPT_CACHE sc = NULL;
SCRIPT_CACHE sc2 = NULL;
WORD glyphs[4], glyphs2[4], logclust[4], glyphs3[4]; WORD glyphs[4], glyphs2[4], logclust[4], glyphs3[4];
SCRIPT_VISATTR attrs[4]; SCRIPT_VISATTR attrs[4];
SCRIPT_ITEM items[4]; SCRIPT_ITEM items[4];
...@@ -1862,6 +1863,10 @@ static void test_ScriptShape(HDC hdc) ...@@ -1862,6 +1863,10 @@ static void test_ScriptShape(HDC hdc)
ok(hr == S_OK, "ScriptShape should return S_OK not %08x\n", hr); ok(hr == S_OK, "ScriptShape should return S_OK not %08x\n", hr);
ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n"); ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n");
hr = ScriptShape(hdc, &sc2, test1, 4, 4, &items[0].a, glyphs, logclust, attrs, &nb);
ok(hr == S_OK, "ScriptShape should return S_OK not %08x\n", hr);
ok(sc2 == sc, "caches %p, %p not identical\n", sc, sc2);
ScriptFreeCache(&sc2);
memset(glyphs,-1,sizeof(glyphs)); memset(glyphs,-1,sizeof(glyphs));
memset(logclust,-1,sizeof(logclust)); memset(logclust,-1,sizeof(logclust));
...@@ -2087,6 +2092,7 @@ static void test_ScriptPlace(HDC hdc) ...@@ -2087,6 +2092,7 @@ static void test_ScriptPlace(HDC hdc)
BOOL ret; BOOL ret;
HRESULT hr; HRESULT hr;
SCRIPT_CACHE sc = NULL; SCRIPT_CACHE sc = NULL;
SCRIPT_CACHE sc2 = NULL;
WORD glyphs[4], logclust[4]; WORD glyphs[4], logclust[4];
SCRIPT_VISATTR attrs[4]; SCRIPT_VISATTR attrs[4];
SCRIPT_ITEM items[2]; SCRIPT_ITEM items[2];
...@@ -2124,6 +2130,11 @@ static void test_ScriptPlace(HDC hdc) ...@@ -2124,6 +2130,11 @@ static void test_ScriptPlace(HDC hdc)
ok(hr == S_OK, "ScriptPlace should return S_OK not %08x\n", hr); ok(hr == S_OK, "ScriptPlace should return S_OK not %08x\n", hr);
ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n"); ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n");
hr = ScriptPlace(hdc, &sc2, glyphs, 4, attrs, &items[0].a, widths, offset, NULL);
ok(hr == S_OK, "ScriptPlace should return S_OK not %08x\n", hr);
ok(sc2 == sc, "caches %p, %p not identical\n", sc, sc2);
ScriptFreeCache(&sc2);
if (widths[0] != 0) if (widths[0] != 0)
{ {
int old_width = widths[0]; int old_width = widths[0];
...@@ -4109,6 +4120,112 @@ static void test_ScriptString_pSize(HDC hdc) ...@@ -4109,6 +4120,112 @@ static void test_ScriptString_pSize(HDC hdc)
ok(hr == S_OK, "Failed to free ssa, hr %#x.\n", hr); ok(hr == S_OK, "Failed to free ssa, hr %#x.\n", hr);
} }
static void test_script_cache_reuse(void)
{
HRESULT hr;
HWND hwnd1, hwnd2;
HDC hdc1, hdc2;
LOGFONTA lf;
HFONT hfont1, hfont2;
HFONT prev_hfont1, prev_hfont2;
SCRIPT_CACHE sc = NULL;
SCRIPT_CACHE sc2;
LONG height;
hwnd1 = create_test_window();
hwnd2 = create_test_window();
hdc1 = GetDC(hwnd1);
hdc2 = GetDC(hwnd2);
ok(hdc1 != NULL && hdc2 != NULL, "Failed to get window dc.\n");
memset(&lf, 0, sizeof(LOGFONTA));
lstrcpyA(lf.lfFaceName, "Tahoma");
lf.lfHeight = 10;
hfont1 = CreateFontIndirectA(&lf);
ok(hfont1 != NULL, "CreateFontIndirectA failed\n");
hfont2 = CreateFontIndirectA(&lf);
ok(hfont2 != NULL, "CreateFontIndirectA failed\n");
ok(hfont1 != hfont2, "Expected fonts %p and %p to differ\n", hfont1, hfont2);
prev_hfont1 = SelectObject(hdc1, hfont1);
ok(prev_hfont1 != NULL, "SelectObject failed: %p\n", prev_hfont1);
prev_hfont2 = SelectObject(hdc2, hfont1);
ok(prev_hfont2 != NULL, "SelectObject failed: %p\n", prev_hfont2);
/* Get a script cache */
hr = ScriptCacheGetHeight(hdc1, &sc, &height);
ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
ok(sc != NULL, "Script cache is NULL\n");
/* Same font, same DC -> same SCRIPT_CACHE */
sc2 = NULL;
hr = ScriptCacheGetHeight(hdc1, &sc2, &height);
ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
ok(sc2 != NULL, "Script cache is NULL\n");
ok(sc == sc2, "Expected caches %p, %p to be identical\n", sc, sc2);
ScriptFreeCache(&sc2);
/* Same font in different DC -> same SCRIPT_CACHE */
sc2 = NULL;
hr = ScriptCacheGetHeight(hdc2, &sc2, &height);
ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
ok(sc2 != NULL, "Script cache is NULL\n");
ok(sc == sc2, "Expected caches %p, %p to be identical\n", sc, sc2);
ScriptFreeCache(&sc2);
/* Same font face & size, but different font handle */
ok(SelectObject(hdc1, hfont2) != NULL, "SelectObject failed\n");
ok(SelectObject(hdc2, hfont2) != NULL, "SelectObject failed\n");
sc2 = NULL;
hr = ScriptCacheGetHeight(hdc1, &sc2, &height);
ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
ok(sc2 != NULL, "Script cache is NULL\n");
ok(sc == sc2, "Expected caches %p, %p to be identical\n", sc, sc2);
ScriptFreeCache(&sc2);
sc2 = NULL;
hr = ScriptCacheGetHeight(hdc2, &sc2, &height);
ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
ok(sc2 != NULL, "Script cache is NULL\n");
ok(sc == sc2, "Expected caches %p, %p to be identical\n", sc, sc2);
ScriptFreeCache(&sc2);
/* Different font size -- now we get a different SCRIPT_CACHE */
SelectObject(hdc1, prev_hfont1);
SelectObject(hdc2, prev_hfont2);
DeleteObject(hfont2);
lf.lfHeight = 20;
hfont2 = CreateFontIndirectA(&lf);
ok(hfont2 != NULL, "CreateFontIndirectA failed\n");
ok(SelectObject(hdc1, hfont2) != NULL, "SelectObject failed\n");
ok(SelectObject(hdc2, hfont2) != NULL, "SelectObject failed\n");
sc2 = NULL;
hr = ScriptCacheGetHeight(hdc1, &sc2, &height);
ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
ok(sc2 != NULL, "Script cache is NULL\n");
ok(sc != sc2, "Expected caches %p, %p to be different\n", sc, sc2);
ScriptFreeCache(&sc2);
sc2 = NULL;
hr = ScriptCacheGetHeight(hdc2, &sc2, &height);
ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
ok(sc2 != NULL, "Script cache is NULL\n");
ok(sc != sc2, "Expected caches %p, %p to be different\n", sc, sc2);
ScriptFreeCache(&sc2);
ScriptFreeCache(&sc);
SelectObject(hdc1, prev_hfont1);
SelectObject(hdc2, prev_hfont2);
DeleteObject(hfont1);
DeleteObject(hfont2);
DestroyWindow(hwnd1);
DestroyWindow(hwnd2);
}
static void init_tests(void) static void init_tests(void)
{ {
HMODULE module = GetModuleHandleA("usp10.dll"); HMODULE module = GetModuleHandleA("usp10.dll");
...@@ -4182,6 +4299,7 @@ START_TEST(usp10) ...@@ -4182,6 +4299,7 @@ START_TEST(usp10)
test_ScriptGetLogicalWidths(); test_ScriptGetLogicalWidths();
test_ScriptIsComplex(); test_ScriptIsComplex();
test_script_cache_reuse();
ReleaseDC(hwnd, hdc); ReleaseDC(hwnd, hdc);
DestroyWindow(hwnd); DestroyWindow(hwnd);
......
...@@ -675,6 +675,16 @@ static const SCRIPT_PROPERTIES *script_props[] = ...@@ -675,6 +675,16 @@ static const SCRIPT_PROPERTIES *script_props[] =
&scriptInformation[80].props, &scriptInformation[81].props &scriptInformation[80].props, &scriptInformation[81].props
}; };
static CRITICAL_SECTION cs_script_cache;
static CRITICAL_SECTION_DEBUG cs_script_cache_dbg =
{
0, 0, &cs_script_cache,
{ &cs_script_cache_dbg.ProcessLocksList, &cs_script_cache_dbg.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": script_cache") }
};
static CRITICAL_SECTION cs_script_cache = { &cs_script_cache_dbg, -1, 0, 0, 0, 0 };
static struct list script_cache_list = LIST_INIT(script_cache_list);
typedef struct { typedef struct {
ScriptCache *sc; ScriptCache *sc;
int numGlyphs; int numGlyphs;
...@@ -853,12 +863,34 @@ static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *ab ...@@ -853,12 +863,34 @@ static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *ab
static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc) static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
{ {
ScriptCache *sc; ScriptCache *sc;
int size; unsigned size;
LOGFONTW lf;
if (!psc) return E_INVALIDARG; if (!psc) return E_INVALIDARG;
if (*psc) return S_OK; if (*psc) return S_OK;
if (!hdc) return E_PENDING; if (!hdc) return E_PENDING;
if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf))
{
return E_INVALIDARG;
}
/* Ensure canonical result by zeroing extra space in lfFaceName */
size = strlenW(lf.lfFaceName);
memset(lf.lfFaceName + size, 0, sizeof(lf.lfFaceName) - size * sizeof(WCHAR));
EnterCriticalSection(&cs_script_cache);
LIST_FOR_EACH_ENTRY(sc, &script_cache_list, ScriptCache, entry)
{
if (!memcmp(&sc->lf, &lf, sizeof(lf)))
{
sc->refcount++;
LeaveCriticalSection(&cs_script_cache);
*psc = sc;
return S_OK;
}
}
LeaveCriticalSection(&cs_script_cache);
if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY; if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
if (!GetTextMetricsW(hdc, &sc->tm)) if (!GetTextMetricsW(hdc, &sc->tm))
{ {
...@@ -872,18 +904,32 @@ static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc) ...@@ -872,18 +904,32 @@ static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
sc->otm->otmSize = size; sc->otm->otmSize = size;
GetOutlineTextMetricsW(hdc, size, sc->otm); GetOutlineTextMetricsW(hdc, size, sc->otm);
} }
if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf))
{
heap_free(sc);
return E_INVALIDARG;
}
sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR); sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR);
if (!set_cache_font_properties(hdc, sc)) if (!set_cache_font_properties(hdc, sc))
{ {
heap_free(sc); heap_free(sc);
return E_INVALIDARG; return E_INVALIDARG;
} }
sc->lf = lf;
sc->refcount = 1;
*psc = sc; *psc = sc;
EnterCriticalSection(&cs_script_cache);
list_add_head(&script_cache_list, &sc->entry);
LIST_FOR_EACH_ENTRY(sc, &script_cache_list, ScriptCache, entry)
{
if (sc != *psc && !memcmp(&sc->lf, &lf, sizeof(lf)))
{
/* Another thread won the race. Use their cache instead of ours */
list_remove(&sc->entry);
sc->refcount++;
LeaveCriticalSection(&cs_script_cache);
heap_free(*psc);
*psc = sc;
return S_OK;
}
}
LeaveCriticalSection(&cs_script_cache);
TRACE("<- %p\n", sc); TRACE("<- %p\n", sc);
return S_OK; return S_OK;
} }
...@@ -1036,6 +1082,17 @@ HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc) ...@@ -1036,6 +1082,17 @@ HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
{ {
unsigned int i; unsigned int i;
INT n; INT n;
EnterCriticalSection(&cs_script_cache);
if (--((ScriptCache *)*psc)->refcount > 0)
{
LeaveCriticalSection(&cs_script_cache);
*psc = NULL;
return S_OK;
}
list_remove(&((ScriptCache *)*psc)->entry);
LeaveCriticalSection(&cs_script_cache);
for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++) for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++)
{ {
heap_free(((ScriptCache *)*psc)->widths[i]); heap_free(((ScriptCache *)*psc)->widths[i]);
......
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
* *
*/ */
#include "wine/list.h"
#define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \ #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
( ( (ULONG)_x4 << 24 ) | \ ( ( (ULONG)_x4 << 24 ) | \
( (ULONG)_x3 << 16 ) | \ ( (ULONG)_x3 << 16 ) | \
...@@ -174,6 +177,8 @@ typedef struct { ...@@ -174,6 +177,8 @@ typedef struct {
} CacheGlyphPage; } CacheGlyphPage;
typedef struct { typedef struct {
struct list entry;
DWORD refcount;
LOGFONTW lf; LOGFONTW lf;
TEXTMETRICW tm; TEXTMETRICW tm;
OUTLINETEXTMETRICW *otm; OUTLINETEXTMETRICW *otm;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment