Speed up GetTextExtentExPointW (Take 3)
Dan Hipschman
dsh at linux.ucla.edu
Mon Jul 10 15:24:53 CDT 2006
Hi,
This is a fix for http://bugs.winehq.org/show_bug.cgi?id=5569 It speeds up
the loading of the EULA for the trial version of Acrobat by about 50x. Since
the last attempt at this fix I improved performance in the case where both
lpnFit and alpDx are NULL. I also added a few more tests to cover each of
the four cases where lpnFit and alpDx are NULL/non-NULL. The tests pass in
both Wine and XP Pro. If you don't accept this, please let me know what I can
do to improve it. Thanks.
ChangeLog:
* Replace order n^2 algorithm in GetTextExtentExPointW with an order n one.
---
dlls/gdi/driver.c | 2 -
dlls/gdi/enhmfdrv/init.c | 2 -
dlls/gdi/font.c | 106 ++++++++++++++++++++++++++--------------------
dlls/gdi/freetype.c | 35 ++++++++++-----
dlls/gdi/gdi_private.h | 4 +-
dlls/gdi/mfdrv/init.c | 2 -
dlls/gdi/tests/font.c | 32 +++++++++++++-
7 files changed, 120 insertions(+), 63 deletions(-)
diff --git a/dlls/gdi/driver.c b/dlls/gdi/driver.c
index 8d50e8c..76657a7 100644
--- a/dlls/gdi/driver.c
+++ b/dlls/gdi/driver.c
@@ -121,7 +121,7 @@ #define GET_FUNC(name) driver->funcs.p##
GET_FUNC(GetPixel);
GET_FUNC(GetPixelFormat);
GET_FUNC(GetSystemPaletteEntries);
- GET_FUNC(GetTextExtentPoint);
+ GET_FUNC(GetTextExtentExPoint);
GET_FUNC(GetTextMetrics);
GET_FUNC(IntersectClipRect);
GET_FUNC(InvertRgn);
diff --git a/dlls/gdi/enhmfdrv/init.c b/dlls/gdi/enhmfdrv/init.c
index 82f0222..c3677b0 100644
--- a/dlls/gdi/enhmfdrv/init.c
+++ b/dlls/gdi/enhmfdrv/init.c
@@ -80,7 +80,7 @@ static const DC_FUNCTIONS EMFDRV_Funcs =
NULL, /* pGetPixel */
NULL, /* pGetPixelFormat */
NULL, /* pGetSystemPaletteEntries */
- NULL, /* pGetTextExtentPoint */
+ NULL, /* pGetTextExtentExPoint */
NULL, /* pGetTextMetrics */
EMFDRV_IntersectClipRect, /* pIntersectClipRect */
EMFDRV_InvertRgn, /* pInvertRgn */
diff --git a/dlls/gdi/font.c b/dlls/gdi/font.c
index 33a03f6..d97b334 100644
--- a/dlls/gdi/font.c
+++ b/dlls/gdi/font.c
@@ -1053,27 +1053,7 @@ BOOL WINAPI GetTextExtentPoint32W(
INT count, /* [in] Number of characters in string */
LPSIZE size) /* [out] Address of structure for string size */
{
- BOOL ret = FALSE;
- DC * dc = DC_GetDCPtr( hdc );
- if (!dc) return FALSE;
-
- if(dc->gdiFont)
- ret = WineEngGetTextExtentPoint(dc->gdiFont, str, count, size);
- else if(dc->funcs->pGetTextExtentPoint)
- ret = dc->funcs->pGetTextExtentPoint( dc->physDev, str, count, size );
-
- if (ret)
- {
- size->cx = abs(INTERNAL_XDSTOWS(dc, size->cx));
- size->cy = abs(INTERNAL_YDSTOWS(dc, size->cy));
- size->cx += count * dc->charExtra + dc->breakRem;
- }
-
- GDI_ReleaseObj( hdc );
-
- TRACE("(%p %s %d %p): returning %ld x %ld\n",
- hdc, debugstr_wn (str, count), count, size, size->cx, size->cy );
- return ret;
+ return GetTextExtentExPointW(hdc, str, count, 0, NULL, NULL, size);
}
/***********************************************************************
@@ -1101,9 +1081,10 @@ BOOL WINAPI GetTextExtentPointI(
size->cy = abs(INTERNAL_YDSTOWS(dc, size->cy));
size->cx += count * dc->charExtra;
}
- else if(dc->funcs->pGetTextExtentPoint) {
- FIXME("calling GetTextExtentPoint\n");
- ret = dc->funcs->pGetTextExtentPoint( dc->physDev, (LPCWSTR)indices, count, size );
+ else if(dc->funcs->pGetTextExtentExPoint) {
+ FIXME("calling GetTextExtentExPoint\n");
+ ret = dc->funcs->pGetTextExtentExPoint( dc->physDev, (LPCWSTR)indices,
+ count, 0, NULL, NULL, size );
}
GDI_ReleaseObj( hdc );
@@ -1205,36 +1186,71 @@ BOOL WINAPI GetTextExtentExPointW( HDC h
INT maxExt, LPINT lpnFit,
LPINT alpDx, LPSIZE size )
{
- int index, nFit, extent;
- SIZE tSize;
+ INT nFit = 0;
+ LPINT dxs = NULL;
+ HANDLE heap = NULL;
+ DC *dc;
BOOL ret = FALSE;
- TRACE("(%p, %s, %d)\n",hdc,debugstr_wn(str,count),maxExt);
+ TRACE("(%p, %s, %d)\n", hdc, debugstr_wn(str, count), maxExt);
+
+ dc = DC_GetDCPtr(hdc);
+ if (! dc)
+ return FALSE;
- size->cx = size->cy = nFit = extent = 0;
- for(index = 0; index < count; index++)
+ /* If we need to calculate nFit, then we need the partial extents even if
+ the user hasn't provided us with an array. */
+ if (lpnFit)
{
- if(!GetTextExtentPoint32W( hdc, str, index + 1, &tSize )) goto done;
- /* GetTextExtentPoint includes intercharacter spacing. */
- /* FIXME - justification needs doing yet. Remember that the base
- * data will not be in logical coordinates.
- */
- extent = tSize.cx;
- if( !lpnFit || extent <= maxExt )
- /* It is allowed to be equal. */
+ heap = GetProcessHeap();
+ dxs = alpDx ? alpDx : HeapAlloc(heap, 0, count * sizeof alpDx[0]);
+ if (! dxs)
{
- nFit++;
- if( alpDx ) alpDx[index] = extent;
+ GDI_ReleaseObj(hdc);
+ SetLastError(ERROR_OUTOFMEMORY);
+ return FALSE;
}
- if( tSize.cy > size->cy ) size->cy = tSize.cy;
}
- size->cx = extent;
- if(lpnFit) *lpnFit = nFit;
- ret = TRUE;
+ else
+ dxs = alpDx;
- TRACE("returning %d %ld x %ld\n",nFit,size->cx,size->cy);
+ if (dc->gdiFont)
+ ret = WineEngGetTextExtentExPoint(dc->gdiFont, str, count,
+ 0, NULL, dxs, size);
+ else if (dc->funcs->pGetTextExtentExPoint)
+ ret = dc->funcs->pGetTextExtentExPoint(dc->physDev, str, count,
+ 0, NULL, dxs, size);
-done:
+ /* Perform device size to world size transformations. */
+ if (ret)
+ {
+ INT extra = dc->charExtra, breakRem = dc->breakRem;
+
+ if (dxs)
+ {
+ INT i;
+ for (i = 0; i < count; ++i)
+ {
+ dxs[i] = abs(INTERNAL_XDSTOWS(dc, dxs[i]));
+ dxs[i] += (i+1) * extra + breakRem;
+ if (dxs[i] <= maxExt)
+ ++nFit;
+ }
+ }
+ size->cx = abs(INTERNAL_XDSTOWS(dc, size->cx));
+ size->cy = abs(INTERNAL_YDSTOWS(dc, size->cy));
+ size->cx += count * extra + breakRem;
+ }
+
+ if (lpnFit)
+ *lpnFit = nFit;
+
+ if (dxs && ! alpDx)
+ HeapFree(heap, 0, dxs);
+
+ GDI_ReleaseObj( hdc );
+
+ TRACE("returning %d %ld x %ld\n", nFit, size->cx, size->cy);
return ret;
}
diff --git a/dlls/gdi/freetype.c b/dlls/gdi/freetype.c
index 9f52e60..2020d9d 100644
--- a/dlls/gdi/freetype.c
+++ b/dlls/gdi/freetype.c
@@ -3849,32 +3849,43 @@ BOOL WineEngGetCharABCWidthsI(GdiFont fo
}
/*************************************************************
- * WineEngGetTextExtentPoint
+ * WineEngGetTextExtentExPoint
*
*/
-BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
- LPSIZE size)
+BOOL WineEngGetTextExtentExPoint(GdiFont font, LPCWSTR wstr, INT count,
+ INT max_ext, LPINT pnfit, LPINT dxs, LPSIZE size)
{
- INT idx;
+ INT i, nfit = 0, ext;
GLYPHMETRICS gm;
TEXTMETRICW tm;
FT_UInt glyph_index;
GdiFont linked_font;
- TRACE("%p, %s, %d, %p\n", font, debugstr_wn(wstr, count), count,
- size);
+ TRACE("%p, %s, %d, %d, %p\n", font, debugstr_wn(wstr, count),
+ count, max_ext, size);
size->cx = 0;
WineEngGetTextMetrics(font, &tm);
size->cy = tm.tmHeight;
- for(idx = 0; idx < count; idx++) {
- get_glyph_index_linked(font, wstr[idx], &linked_font, &glyph_index);
+ for (i = 0; i < count; ++i)
+ {
+ get_glyph_index_linked(font, wstr[i], &linked_font, &glyph_index);
WineEngGetGlyphOutline(linked_font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
&gm, 0, NULL, NULL);
- size->cx += linked_font->gm[glyph_index].adv;
+ ext = size->cx += linked_font->gm[glyph_index].adv;
+ if (! pnfit || ext <= max_ext)
+ {
+ ++nfit;
+ if (dxs)
+ dxs[i] = ext;
+ }
}
- TRACE("return %ld,%ld\n", size->cx, size->cy);
+
+ if (pnfit)
+ *pnfit = nfit;
+
+ TRACE("return %ld, %ld, %d\n", size->cx, size->cy, nfit);
return TRUE;
}
@@ -4149,8 +4160,8 @@ BOOL WineEngGetCharABCWidthsI(GdiFont fo
return FALSE;
}
-BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
- LPSIZE size)
+BOOL WineEngGetTextExtentExPoint(GdiFont font, LPCWSTR wstr, INT count,
+ INT max_ext, LPINT nfit, LPINT dx, LPSIZE size)
{
ERR("called but we don't have FreeType\n");
return FALSE;
diff --git a/dlls/gdi/gdi_private.h b/dlls/gdi/gdi_private.h
index 610babb..a0de444 100644
--- a/dlls/gdi/gdi_private.h
+++ b/dlls/gdi/gdi_private.h
@@ -107,7 +107,7 @@ typedef struct tagDC_FUNCS
COLORREF (*pGetPixel)(PHYSDEV,INT,INT);
INT (*pGetPixelFormat)(PHYSDEV);
UINT (*pGetSystemPaletteEntries)(PHYSDEV,UINT,UINT,LPPALETTEENTRY);
- BOOL (*pGetTextExtentPoint)(PHYSDEV,LPCWSTR,INT,LPSIZE);
+ BOOL (*pGetTextExtentExPoint)(PHYSDEV,LPCWSTR,INT,INT,LPINT,LPINT,LPSIZE);
BOOL (*pGetTextMetrics)(PHYSDEV,TEXTMETRICW*);
INT (*pIntersectClipRect)(PHYSDEV,INT,INT,INT,INT);
BOOL (*pInvertRgn)(PHYSDEV,HRGN);
@@ -379,7 +379,7 @@ extern DWORD WineEngGetGlyphOutline(GdiF
extern BOOL WineEngGetLinkedHFont(DC *dc, WCHAR c, HFONT *new_hfont, UINT *glyph);
extern UINT WineEngGetOutlineTextMetrics(GdiFont, UINT, LPOUTLINETEXTMETRICW);
extern UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags);
-extern BOOL WineEngGetTextExtentPoint(GdiFont, LPCWSTR, INT, LPSIZE);
+extern BOOL WineEngGetTextExtentExPoint(GdiFont, LPCWSTR, INT, INT, LPINT, LPINT, LPSIZE);
extern BOOL WineEngGetTextExtentPointI(GdiFont, const WORD *, INT, LPSIZE);
extern INT WineEngGetTextFace(GdiFont, INT, LPWSTR);
extern BOOL WineEngGetTextMetrics(GdiFont, LPTEXTMETRICW);
diff --git a/dlls/gdi/mfdrv/init.c b/dlls/gdi/mfdrv/init.c
index bf964a7..c85501a 100644
--- a/dlls/gdi/mfdrv/init.c
+++ b/dlls/gdi/mfdrv/init.c
@@ -80,7 +80,7 @@ static const DC_FUNCTIONS MFDRV_Funcs =
NULL, /* pGetPixel */
NULL, /* pGetPixelFormat */
NULL, /* pGetSystemPaletteEntries */
- NULL, /* pGetTextExtentPoint */
+ NULL, /* pGetTextExtentExPoint */
NULL, /* pGetTextMetrics */
MFDRV_IntersectClipRect, /* pIntersectClipRect */
MFDRV_InvertRgn, /* pInvertRgn */
diff --git a/dlls/gdi/tests/font.c b/dlls/gdi/tests/font.c
index 393bc27..4cc64fd 100644
--- a/dlls/gdi/tests/font.c
+++ b/dlls/gdi/tests/font.c
@@ -336,11 +336,14 @@ static void test_GetCharABCWidthsW(void)
static void test_text_extents(void)
{
+ const static WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
+ LPINT extents;
+ INT i, len, fit1, fit2;
LOGFONTA lf;
TEXTMETRICA tm;
HDC hdc;
HFONT hfont;
- SIZE sz;
+ SIZE sz, sz1, sz2;
memset(&lf, 0, sizeof(lf));
strcpy(lf.lfFaceName, "Arial");
@@ -353,6 +356,33 @@ static void test_text_extents(void)
GetTextExtentPointA(hdc, "o", 1, &sz);
ok(sz.cy == tm.tmHeight, "cy %ld tmHeight %ld\n", sz.cy, tm.tmHeight);
+ len = lstrlenW(wt);
+ extents = HeapAlloc(GetProcessHeap(), 0, len * sizeof extents[0]);
+ memset(extents, 0, len * sizeof extents[0]);
+ GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
+ GetTextExtentPointW(hdc, wt, len, &sz2);
+ ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
+ "results from GetTextExtentExPointW and GetTextExtentPointW differ\n");
+ for (i = 1; i < len; ++i)
+ ok(extents[i-1] <= extents[i],
+ "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
+ i);
+ ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
+ ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
+ ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
+ GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
+ ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
+ ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
+ GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
+ ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
+ GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
+ ok(extents[0] == extents[2] && extents[1] == extents[3],
+ "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
+ GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
+ ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
+ "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
+ HeapFree(GetProcessHeap(), 0, extents);
+
SelectObject(hdc, hfont);
DeleteObject(hfont);
ReleaseDC(NULL, hdc);
More information about the wine-patches
mailing list