usp10: Store glyph mappings and widths in the script cache.

Hans Leidekker hans at codeweavers.com
Thu Oct 9 08:17:29 CDT 2008


Fixes http://bugs.winehq.org/show_bug.cgi?id=12307

 -Hans

diff --git a/dlls/usp10/tests/usp10.c b/dlls/usp10/tests/usp10.c
index 67a4ea9..a791214 100644
--- a/dlls/usp10/tests/usp10.c
+++ b/dlls/usp10/tests/usp10.c
@@ -61,13 +61,22 @@ static void test_ScriptShape(HDC hdc)
     hr = ScriptShape(hdc, &sc, test1, 4, 4, &items[0].a, glyphs, NULL, attrs, NULL);
     ok(hr == E_INVALIDARG, "ScriptShape should return E_INVALIDARG not %08x\n", hr);
 
+    hr = ScriptShape(NULL, &sc, test1, 4, 4, &items[0].a, glyphs, NULL, attrs, &nb);
+    ok(hr == E_PENDING, "ScriptShape should return E_PENDING not %08x\n", hr);
+
     hr = ScriptShape(hdc, &sc, test1, 4, 4, &items[0].a, glyphs, NULL, attrs, &nb);
     ok(!hr, "ScriptShape should return S_OK not %08x\n", hr);
     ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n");
 
+    hr = ScriptShape(NULL, &sc, test1, 4, 4, &items[0].a, glyphs, NULL, attrs, &nb);
+    ok(!hr, "ScriptShape should return S_OK not %08x\n", hr);
+
     hr = ScriptPlace(hdc, &sc, glyphs, 4, NULL, &items[0].a, widths, NULL, NULL);
     ok(hr == E_INVALIDARG, "ScriptPlace should return E_INVALIDARG not %08x\n", hr);
 
+    hr = ScriptPlace(NULL, &sc, glyphs, 4, attrs, &items[0].a, widths, NULL, NULL);
+    ok(hr == E_PENDING, "ScriptPlace should return E_PENDING not %08x\n", hr);
+
     hr = ScriptPlace(hdc, &sc, glyphs, 4, attrs, &items[0].a, widths, NULL, NULL);
     ok(!hr, "ScriptPlace should return S_OK not %08x\n", hr);
     ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n");
@@ -1394,7 +1403,7 @@ START_TEST(usp10)
     ok( hdc != NULL, "HDC failed to be created %p\n", hdc);
 
     memset(&lf, 0, sizeof(LOGFONTA));
-    lstrcpyA(lf.lfFaceName, "Symbol");
+    lstrcpyA(lf.lfFaceName, "Tahoma");
     lf.lfHeight = 10;
     lf.lfWeight = 3;
     lf.lfWidth = 10;
diff --git a/dlls/usp10/usp10.c b/dlls/usp10/usp10.c
index 28ba2bc..2bc0617 100644
--- a/dlls/usp10/usp10.c
+++ b/dlls/usp10/usp10.c
@@ -138,10 +138,16 @@ static const SCRIPT_PROPERTIES *script_props[] =
     &props[73]
 };
 
+#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
+
 typedef struct {
-    HDC hdc;
     LOGFONTW lf;
     TEXTMETRICW tm;
+    WORD *glyphs[GLYPH_MAX / GLYPH_BLOCK_SIZE];
+    ABC *widths[GLYPH_MAX / GLYPH_BLOCK_SIZE];
 } ScriptCache;
 
 typedef struct {
@@ -155,6 +161,7 @@ typedef struct {
 } StringGlyphs;
 
 typedef struct {
+    HDC hdc;
     BOOL invalid;
     int clip_len;
     ScriptCache *sc;
@@ -187,69 +194,78 @@ static inline BOOL heap_free(LPVOID mem)
     return HeapFree(GetProcessHeap(), 0, mem);
 }
 
-static HDC get_cache_hdc(SCRIPT_CACHE *psc)
-{
-    return ((ScriptCache *)*psc)->hdc;
-}
-
-static WCHAR get_cache_default_char(SCRIPT_CACHE *psc)
+static inline WCHAR get_cache_default_char(SCRIPT_CACHE *psc)
 {
     return ((ScriptCache *)*psc)->tm.tmDefaultChar;
 }
 
-static LONG get_cache_height(SCRIPT_CACHE *psc)
+static inline LONG get_cache_height(SCRIPT_CACHE *psc)
 {
     return ((ScriptCache *)*psc)->tm.tmHeight;
 }
 
-static BYTE get_cache_pitch_family(SCRIPT_CACHE *psc)
+static inline BYTE get_cache_pitch_family(SCRIPT_CACHE *psc)
 {
     return ((ScriptCache *)*psc)->tm.tmPitchAndFamily;
 }
 
-static HRESULT init_script_cache(const HDC hdc, ScriptCache *sc)
+static inline WORD get_cache_glyph(SCRIPT_CACHE *psc, WCHAR c)
 {
-    if (!GetTextMetricsW(hdc, &sc->tm)) return E_INVALIDARG;
-    if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf)) return E_INVALIDARG;
-    sc->hdc = hdc;
-    return S_OK;
+    WORD *block = ((ScriptCache *)*psc)->glyphs[c >> GLYPH_BLOCK_SHIFT];
+
+    if (!block) return 0;
+    return block[c & GLYPH_BLOCK_MASK];
 }
 
-static HRESULT get_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
+static inline WORD set_cache_glyph(SCRIPT_CACHE *psc, WCHAR c, WORD glyph)
 {
-    if (!psc) return E_INVALIDARG;
-    if (!*psc)
-    {
-        HRESULT ret;
-        ScriptCache *sc;
+    WORD **block = &((ScriptCache *)*psc)->glyphs[c >> GLYPH_BLOCK_SHIFT];
 
-        if (!hdc) return E_PENDING;
-        if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
-        ret = init_script_cache(hdc, sc);
-        if (ret != S_OK)
-        {
-            heap_free(sc);
-            return ret;
-        }
-        *psc = sc;
-    }
-    TRACE("<- %p\n", *psc);
-    return S_OK;
+    if (!*block && !(*block = heap_alloc_zero(sizeof(WORD) * GLYPH_BLOCK_SIZE))) return 0;
+    return ((*block)[c & GLYPH_BLOCK_MASK] = glyph);
 }
 
-static HFONT select_cached_font(SCRIPT_CACHE *psc)
+static inline BOOL get_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
 {
-    HFONT old_font;
-    ScriptCache *sc = *psc;
+    static const ABC nil;
+    ABC *block = ((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
 
-    old_font = SelectObject(sc->hdc, CreateFontIndirectW(&sc->lf));
-    return old_font;
+    if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(ABC))) return FALSE;
+    memcpy(abc, &block[glyph & GLYPH_BLOCK_MASK], sizeof(ABC));
+    return TRUE;
 }
 
-static void unselect_cached_font(SCRIPT_CACHE *psc, HFONT old_font)
+static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
 {
-    ScriptCache *sc = *psc;
-    DeleteObject(SelectObject(sc->hdc, old_font));
+    ABC **block = &((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
+
+    if (!*block && !(*block = heap_alloc_zero(sizeof(ABC) * GLYPH_BLOCK_SIZE))) return FALSE;
+    memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], abc, sizeof(ABC));
+    return TRUE;
+}
+
+static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
+{
+    ScriptCache *sc;
+
+    if (!psc) return E_INVALIDARG;
+    if (*psc) return S_OK;
+    if (!hdc) return E_PENDING;
+
+    if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
+    if (!GetTextMetricsW(hdc, &sc->tm))
+    {
+        heap_free(sc);
+        return E_INVALIDARG;
+    }
+    if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf))
+    {
+        heap_free(sc);
+        return E_INVALIDARG;
+    }
+    *psc = sc;
+    TRACE("<- %p\n", sc);
+    return S_OK;
 }
 
 /***********************************************************************
@@ -285,10 +301,16 @@ HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
 {
     TRACE("%p\n", psc);
 
-    if (psc)
+    if (psc && *psc)
     {
-       heap_free(*psc);
-       *psc = NULL;
+        unsigned int i;
+        for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++)
+        {
+            heap_free(((ScriptCache *)*psc)->glyphs[i]);
+            heap_free(((ScriptCache *)*psc)->widths[i]);
+        }
+        heap_free(*psc);
+        *psc = NULL;
     }
     return S_OK;
 }
@@ -338,10 +360,7 @@ HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPR
     TRACE("%p,%p,%p\n", hdc, psc, sfp);
 
     if (!sfp) return E_INVALIDARG;
-
-    hr = get_script_cache(hdc, psc);
-    if (hr != S_OK)
-        return hr;
+    if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
 
     if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
         return E_INVALIDARG;
@@ -613,6 +632,7 @@ HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString,
 
     /* FIXME: handle clipping */
     analysis->clip_len = cString;
+    analysis->hdc = hdc;
 
     hr = ScriptItemize(pString, cString, num_items, psControl, psState, analysis->pItem,
                        &analysis->numItems);
@@ -757,7 +777,7 @@ HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa,
         cnt += analysis->glyphs[item].numGlyphs; /* point to the end of the copied text */
     }
 
-    hr = ScriptTextOut(analysis->sc->hdc, (SCRIPT_CACHE *)&analysis->sc, iX, iY,
+    hr = ScriptTextOut(analysis->hdc, (SCRIPT_CACHE *)&analysis->sc, iX, iY,
                        uOptions, prc, &analysis->pItem->a, NULL, 0, glyphs, cnt,
                        analysis->glyphs->piAdvance, NULL, analysis->glyphs->pGoffset);
     TRACE("ScriptTextOut hr=%08x\n", hr);
@@ -1242,9 +1262,8 @@ HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
                            SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust,
                            SCRIPT_VISATTR *psva, int *pcGlyphs)
 {
-    int cnt;
     HRESULT hr;
-    HFONT hfont;
+    unsigned int i;
 
     TRACE("(%p, %p, %p, %d, %d, %p)\n",  hdc, psc, pwcChars, cChars, cMaxGlyphs, psa);
     if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
@@ -1253,38 +1272,42 @@ HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
     if (!psva || !pcGlyphs) return E_INVALIDARG;
     if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
 
-    hr = get_script_cache(hdc, psc);
-    if (hr != S_OK) return hr;
-
     *pcGlyphs = cChars;
-
-    hfont = select_cached_font(psc);
+    if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
 
     if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE) && !psa->fNoGlyphIndex)
     {
-        GetGlyphIndicesW(get_cache_hdc(psc), pwcChars, cChars, pwOutGlyphs, 0);
+        for (i = 0; i < cChars; i++)
+        {
+            if (!(pwOutGlyphs[i] = get_cache_glyph(psc, pwcChars[i])))
+            {
+                WORD glyph;
+                if (!hdc) return E_PENDING;
+                if (GetGlyphIndicesW(hdc, &pwcChars[i], 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE;
+                pwOutGlyphs[i] = set_cache_glyph(psc, pwcChars[i], glyph);
+            }
+        }
     }
     else
     {
         TRACE("no glyph translation\n");
-        for (cnt = 0; cnt < cChars; cnt++) pwOutGlyphs[cnt] = pwcChars[cnt];
+        for (i = 0; i < cChars; i++) pwOutGlyphs[i] = pwcChars[i];
     }
 
     if (psva)
     {
         /* set up a valid SCRIPT_VISATTR and LogClust for each char in this run */
-        for (cnt = 0; cnt < cChars; cnt++)
+        for (i = 0; i < cChars; i++)
         {
             /* FIXME: set to better values */
-            psva[cnt].uJustification = 2;
-            psva[cnt].fClusterStart  = 1;
-            psva[cnt].fDiacritic     = 0;
-            psva[cnt].fZeroWidth     = 0;
+            psva[i].uJustification = 2;
+            psva[i].fClusterStart  = 1;
+            psva[i].fDiacritic     = 0;
+            psva[i].fZeroWidth     = 0;
 
-            if (pwLogClust) pwLogClust[cnt] = cnt;
+            if (pwLogClust) pwLogClust[i] = i;
         }
     }
-    unselect_cached_font(psc, hfont);
     return S_OK;
 }
 
@@ -1313,60 +1336,46 @@ HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs,
                            SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
 {
     HRESULT hr;
-    LPABC lpABC;
-    HFONT hfont;
     unsigned int i;
 
     TRACE("(%p, %p, %p, %s, %d, %p, %p, %p)\n",  hdc, psc, pwGlyphs,
           debugstr_wn(pwGlyphs, cGlyphs), cGlyphs, psva, psa, piAdvance);
 
     if (!psva) return E_INVALIDARG;
+    if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
 
-    hr = get_script_cache(hdc, psc);
-    if (hr != S_OK) return hr;
-
-    hfont = select_cached_font(psc);
-
-    /*   Here we need to calculate the width of the run unit.  At this point the input string
-     *   has been converted to glyphs and we still need to translate back to the original chars
-     *   to get the correct ABC widths.   */
-
-    if (!(lpABC = heap_alloc_zero(sizeof(ABC) * cGlyphs))) return E_OUTOFMEMORY;
     if (pABC) memset(pABC, 0, sizeof(ABC));
-
-    if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE) && !psa->fNoGlyphIndex)
-    {
-        GetCharABCWidthsI(get_cache_hdc(psc), 0, cGlyphs, (WORD *)pwGlyphs, lpABC);
-    }
-    else
+    for (i = 0; i < cGlyphs; i++)
     {
-        INT width;
-        for (i = 0; i < cGlyphs; i++)
+        ABC abc;
+        if (!get_cache_glyph_widths(psc, pwGlyphs[i], &abc))
         {
-            GetCharWidth32W(get_cache_hdc(psc), pwGlyphs[i], pwGlyphs[i], &width);
-            lpABC[i].abcB = width;
+            if (!hdc) return E_PENDING;
+            if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE) && !psa->fNoGlyphIndex)
+            {
+                if (!GetCharABCWidthsI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &abc)) return S_FALSE;
+            }
+            else
+            {
+                INT width;
+                if (!GetCharWidth32W(hdc, pwGlyphs[i], pwGlyphs[i], &width)) return S_FALSE;
+                abc.abcB = width;
+                abc.abcA = abc.abcC = 0;
+            }
+            set_cache_glyph_widths(psc, pwGlyphs[i], &abc);
         }
-    }
-
-    for (i = 0; i < cGlyphs; i++)
-    {
-        TRACE("     Glyph=%04x, abcA=%d, abcB=%d, abcC=%d index=%d\n",
-              pwGlyphs[i], lpABC[i].abcA, lpABC[i].abcB, lpABC[i].abcC, i);
-
         if (pABC)
         {
-            pABC->abcA += lpABC[i].abcA;
-            pABC->abcB += lpABC[i].abcB;
-            pABC->abcC += lpABC[i].abcC;
+            pABC->abcA += abc.abcA;
+            pABC->abcB += abc.abcB;
+            pABC->abcC += abc.abcC;
         }
         /* FIXME: set to more reasonable values */
         if (pGoffset)  pGoffset[i].du = pGoffset[i].dv = 0;
-        if (piAdvance) piAdvance[i] = lpABC[i].abcA + lpABC[i].abcB + lpABC[i].abcC;
+        if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC;
     }
-    if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
 
-    heap_free(lpABC);
-    unselect_cached_font(psc, hfont);
+    if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
     return S_OK;
 }
 
@@ -1391,20 +1400,32 @@ HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars
                              int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
 {
     HRESULT hr;
-    HFONT hfont;
+    unsigned int i;
 
     TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars),
           cChars, dwFlags, pwOutGlyphs);
 
-    hr = get_script_cache(hdc, psc);
-    if (hr != S_OK) return hr;
+    if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
 
-    hfont = select_cached_font(psc);
-    if (GetGlyphIndicesW(get_cache_hdc(psc), pwcInChars, cChars, pwOutGlyphs, 0) == GDI_ERROR)
-        hr = S_FALSE;
-
-    unselect_cached_font(psc, hfont);
-    return hr;
+    if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
+    {
+        for (i = 0; i < cChars; i++)
+        {
+            if (!(pwOutGlyphs[i] = get_cache_glyph(psc, pwcInChars[i])))
+            {
+                WORD glyph;
+                if (!hdc) return E_PENDING;
+                if (GetGlyphIndicesW(hdc, &pwcInChars[i], 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE;
+                pwOutGlyphs[i] = set_cache_glyph(psc, pwcInChars[i], glyph);
+            }
+        }
+    }
+    else
+    {
+        TRACE("no glyph translation\n");
+        for (i = 0; i < cChars; i++) pwOutGlyphs[i] = pwcInChars[i];
+    }
+    return S_OK;
 }
 
 /***********************************************************************
@@ -1456,9 +1477,7 @@ HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
     TRACE("(%p, %p, %p)\n", hdc, psc, height);
 
     if (!height) return E_INVALIDARG;
-
-    hr = get_script_cache(hdc, psc);
-    if (hr != S_OK) return hr;
+    if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
 
     *height = get_cache_height(psc);
     return S_OK;
@@ -1481,19 +1500,29 @@ HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
  */
 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
 {
-    HFONT hfont;
-    HRESULT hr = S_OK;
+    HRESULT hr;
 
     TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
 
-    hr = get_script_cache(hdc, psc);
-    if (hr != S_OK) return hr;
+    if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
 
-    hfont = select_cached_font(psc);
-    if (!GetCharABCWidthsI(get_cache_hdc(psc), 0, 1, &glyph, abc)) hr = E_HANDLE;
-
-    unselect_cached_font(psc, hfont);
-    return hr;
+    if (!get_cache_glyph_widths(psc, glyph, abc))
+    {
+        if (!hdc) return E_PENDING;
+        if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
+        {
+            if (!GetCharABCWidthsI(hdc, 0, 1, &glyph, abc)) return S_FALSE;
+        }
+        else
+        {
+            INT width;
+            if (!GetCharWidth32W(hdc, glyph, glyph, &width)) return S_FALSE;
+            abc->abcB = width;
+            abc->abcA = abc->abcC = 0;
+        }
+        set_cache_glyph_widths(psc, glyph, abc);
+    }
+    return S_OK;
 }
 
 /***********************************************************************



More information about the wine-patches mailing list