Properly handle glyph positions and take target coordinate transformation into account when caching glyphs. (This is suboptimal, since we might cache duplicates of the same glyphs, but it works). Fixes CORE-4657.

svn path=/trunk/; revision=58418
This commit is contained in:
Timo Kreuzer 2013-03-03 17:20:44 +00:00
parent aeed6a7831
commit b6825a1328

View file

@ -59,6 +59,7 @@ typedef struct _FONT_CACHE_ENTRY
FT_Face Face; FT_Face Face;
FT_BitmapGlyph BitmapGlyph; FT_BitmapGlyph BitmapGlyph;
int Height; int Height;
MATRIX mxWorldToDevice;
} FONT_CACHE_ENTRY, *PFONT_CACHE_ENTRY; } FONT_CACHE_ENTRY, *PFONT_CACHE_ENTRY;
static LIST_ENTRY FontCacheListHead; static LIST_ENTRY FontCacheListHead;
static UINT FontCacheNumEntries; static UINT FontCacheNumEntries;
@ -161,15 +162,11 @@ InitFontSupport(VOID)
VOID VOID
FtSetCoordinateTransform( FtSetCoordinateTransform(
FT_Face face, FT_Face face,
PDC pdc) PMATRIX pmx)
{ {
FT_Matrix ftmatrix; FT_Matrix ftmatrix;
PMATRIX pmx;
FLOATOBJ efTemp; FLOATOBJ efTemp;
/* Get the DC's world-to-device transformation matrix */
pmx = DC_pmxWorldToDevice(pdc);
/* Create a freetype matrix, by converting to 16.16 fixpoint format */ /* Create a freetype matrix, by converting to 16.16 fixpoint format */
efTemp = pmx->efM11; efTemp = pmx->efM11;
FLOATOBJ_MulLong(&efTemp, 0x00010000); FLOATOBJ_MulLong(&efTemp, 0x00010000);
@ -1343,12 +1340,24 @@ ftGdiGetRasterizerCaps(LPRASTERIZER_STATUS lprs)
return FALSE; return FALSE;
} }
static
BOOL
SameScaleMatrix(
PMATRIX pmx1,
PMATRIX pmx2)
{
return (FLOATOBJ_Equal(&pmx1->efM11, &pmx2->efM11) &&
FLOATOBJ_Equal(&pmx1->efM12, &pmx2->efM12) &&
FLOATOBJ_Equal(&pmx1->efM21, &pmx2->efM21) &&
FLOATOBJ_Equal(&pmx1->efM22, &pmx2->efM22));
}
FT_BitmapGlyph APIENTRY FT_BitmapGlyph APIENTRY
ftGdiGlyphCacheGet( ftGdiGlyphCacheGet(
FT_Face Face, FT_Face Face,
INT GlyphIndex, INT GlyphIndex,
INT Height) INT Height,
PMATRIX pmx)
{ {
PLIST_ENTRY CurrentEntry; PLIST_ENTRY CurrentEntry;
PFONT_CACHE_ENTRY FontEntry; PFONT_CACHE_ENTRY FontEntry;
@ -1357,9 +1366,10 @@ ftGdiGlyphCacheGet(
while (CurrentEntry != &FontCacheListHead) while (CurrentEntry != &FontCacheListHead)
{ {
FontEntry = (PFONT_CACHE_ENTRY)CurrentEntry; FontEntry = (PFONT_CACHE_ENTRY)CurrentEntry;
if (FontEntry->Face == Face && if ((FontEntry->Face == Face) &&
FontEntry->GlyphIndex == GlyphIndex && (FontEntry->GlyphIndex == GlyphIndex) &&
FontEntry->Height == Height) (FontEntry->Height == Height) &&
(SameScaleMatrix(&FontEntry->mxWorldToDevice, pmx)))
break; break;
CurrentEntry = CurrentEntry->Flink; CurrentEntry = CurrentEntry->Flink;
} }
@ -1379,6 +1389,7 @@ ftGdiGlyphCacheSet(
FT_Face Face, FT_Face Face,
INT GlyphIndex, INT GlyphIndex,
INT Height, INT Height,
PMATRIX pmx,
FT_GlyphSlot GlyphSlot, FT_GlyphSlot GlyphSlot,
FT_Render_Mode RenderMode) FT_Render_Mode RenderMode)
{ {
@ -1428,6 +1439,7 @@ ftGdiGlyphCacheSet(
NewEntry->Face = Face; NewEntry->Face = Face;
NewEntry->BitmapGlyph = BitmapGlyph; NewEntry->BitmapGlyph = BitmapGlyph;
NewEntry->Height = Height; NewEntry->Height = Height;
NewEntry->mxWorldToDevice = *pmx;
InsertHeadList(&FontCacheListHead, &NewEntry->ListEntry); InsertHeadList(&FontCacheListHead, &NewEntry->ListEntry);
if (FontCacheNumEntries++ > MAX_FONT_CACHE) if (FontCacheNumEntries++ > MAX_FONT_CACHE)
@ -1585,7 +1597,7 @@ ftGdiGetGlyphOutline(
/* FIXME: Should set character height if neg */ /* FIXME: Should set character height if neg */
// (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? // (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ?
// dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); // dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)));
// FtSetCoordinateTransform(face, dc); // FtSetCoordinateTransform(face, DC_pmxWorldToDevice(dc));
TEXTOBJ_UnlockText(TextObj); TEXTOBJ_UnlockText(TextObj);
@ -2132,6 +2144,7 @@ TextIntGetTextExtentPoint(PDC dc,
BOOL use_kerning; BOOL use_kerning;
FT_Render_Mode RenderMode; FT_Render_Mode RenderMode;
BOOLEAN Render; BOOLEAN Render;
PMATRIX pmxWorldToDevice;
FontGDI = ObjToGDI(TextObj->Font, FONT); FontGDI = ObjToGDI(TextObj->Font, FONT);
@ -2186,7 +2199,9 @@ TextIntGetTextExtentPoint(PDC dc,
DPRINT1("Error in setting pixel sizes: %u\n", error); DPRINT1("Error in setting pixel sizes: %u\n", error);
} }
FtSetCoordinateTransform(face, dc); /* Get the DC's world-to-device transformation matrix */
pmxWorldToDevice = DC_pmxWorldToDevice(dc);
FtSetCoordinateTransform(face, pmxWorldToDevice);
use_kerning = FT_HAS_KERNING(face); use_kerning = FT_HAS_KERNING(face);
previous = 0; previous = 0;
@ -2199,7 +2214,8 @@ TextIntGetTextExtentPoint(PDC dc,
glyph_index = FT_Get_Char_Index(face, *String); glyph_index = FT_Get_Char_Index(face, *String);
if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index, if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index,
TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))) TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight,
pmxWorldToDevice)))
{ {
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error) if (error)
@ -2209,8 +2225,12 @@ TextIntGetTextExtentPoint(PDC dc,
} }
glyph = face->glyph; glyph = face->glyph;
realglyph = ftGdiGlyphCacheSet(face, glyph_index, realglyph = ftGdiGlyphCacheSet(face,
TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, glyph, RenderMode); glyph_index,
TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight,
pmxWorldToDevice,
glyph,
RenderMode);
if (!realglyph) if (!realglyph)
{ {
DPRINT1("Failed to render glyph! [index: %u]\n", glyph_index); DPRINT1("Failed to render glyph! [index: %u]\n", glyph_index);
@ -2470,7 +2490,7 @@ ftGdiGetTextMetricsW(
/* FIXME: Should set character height if neg */ /* FIXME: Should set character height if neg */
(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ?
dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)));
FtSetCoordinateTransform(Face, dc); FtSetCoordinateTransform(Face, DC_pmxWorldToDevice(dc));
IntUnLockFreeType; IntUnLockFreeType;
if (0 != Error) if (0 != Error)
{ {
@ -3119,6 +3139,24 @@ NtGdiGetFontFamilyInfo(HDC Dc,
return Count; return Count;
} }
LONG
FORCEINLINE
ScaleLong(LONG lValue, PFLOATOBJ pef)
{
FLOATOBJ efTemp;
/* Check if we have scaling different from 1 */
if (!FLOATOBJ_Equal(pef, (PFLOATOBJ)&gef1))
{
/* Need to multiply */
FLOATOBJ_SetLong(&efTemp, lValue);
FLOATOBJ_Mul(&efTemp, pef);
lValue = FLOATOBJ_GetLong(&efTemp);
}
return lValue;
}
BOOL BOOL
APIENTRY APIENTRY
GreExtTextOutW( GreExtTextOutW(
@ -3165,6 +3203,8 @@ GreExtTextOutW(
POINT Start; POINT Start;
BOOL DoBreak = FALSE; BOOL DoBreak = FALSE;
USHORT DxShift; USHORT DxShift;
PMATRIX pmxWorldToDevice;
LONG fixAscender, fixDescender;
// TODO: Write test-cases to exactly match real Windows in different // TODO: Write test-cases to exactly match real Windows in different
// bad parameters (e.g. does Windows check the DC or the RECT first?). // bad parameters (e.g. does Windows check the DC or the RECT first?).
@ -3328,18 +3368,22 @@ GreExtTextOutW(
goto fail; goto fail;
} }
FtSetCoordinateTransform(face, dc); pmxWorldToDevice = DC_pmxWorldToDevice(dc);
FtSetCoordinateTransform(face, pmxWorldToDevice);
/* /*
* Process the vertical alignment and determine the yoff. * Process the vertical alignment and determine the yoff.
*/ */
fixAscender = ScaleLong(face->size->metrics.ascender, &pmxWorldToDevice->efM22);
fixDescender = ScaleLong(face->size->metrics.descender, &pmxWorldToDevice->efM22);
if (pdcattr->lTextAlign & TA_BASELINE) if (pdcattr->lTextAlign & TA_BASELINE)
yoff = 0; yoff = 0;
else if (pdcattr->lTextAlign & TA_BOTTOM) else if (pdcattr->lTextAlign & TA_BOTTOM)
yoff = -face->size->metrics.descender >> 6; yoff = -fixDescender >> 6;
else /* TA_TOP */ else /* TA_TOP */
yoff = face->size->metrics.ascender >> 6; yoff = fixAscender >> 6;
use_kerning = FT_HAS_KERNING(face); use_kerning = FT_HAS_KERNING(face);
previous = 0; previous = 0;
@ -3377,7 +3421,8 @@ GreExtTextOutW(
glyph_index = FT_Get_Char_Index(face, *TempText); glyph_index = FT_Get_Char_Index(face, *TempText);
if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index, if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index,
TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))) TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight,
pmxWorldToDevice)))
{ {
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error) if (error)
@ -3386,8 +3431,12 @@ GreExtTextOutW(
} }
glyph = face->glyph; glyph = face->glyph;
realglyph = ftGdiGlyphCacheSet(face, glyph_index, realglyph = ftGdiGlyphCacheSet(face,
TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, glyph, RenderMode); glyph_index,
TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight,
pmxWorldToDevice,
glyph,
RenderMode);
if (!realglyph) if (!realglyph)
{ {
DPRINT1("Failed to render glyph! [index: %u]\n", glyph_index); DPRINT1("Failed to render glyph! [index: %u]\n", glyph_index);
@ -3453,7 +3502,8 @@ GreExtTextOutW(
glyph_index = FT_Get_Char_Index(face, *String); glyph_index = FT_Get_Char_Index(face, *String);
if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index, if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index,
TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))) TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight,
pmxWorldToDevice)))
{ {
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error) if (error)
@ -3466,6 +3516,7 @@ GreExtTextOutW(
realglyph = ftGdiGlyphCacheSet(face, realglyph = ftGdiGlyphCacheSet(face,
glyph_index, glyph_index,
TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight,
pmxWorldToDevice,
glyph, glyph,
RenderMode); RenderMode);
if (!realglyph) if (!realglyph)
@ -3491,8 +3542,8 @@ GreExtTextOutW(
{ {
DestRect.left = BackgroundLeft; DestRect.left = BackgroundLeft;
DestRect.right = (TextLeft + (realglyph->root.advance.x >> 10) + 32) >> 6; DestRect.right = (TextLeft + (realglyph->root.advance.x >> 10) + 32) >> 6;
DestRect.top = TextTop + yoff - ((face->size->metrics.ascender + 32) >> 6); DestRect.top = TextTop + yoff - ((fixAscender + 32) >> 6);
DestRect.bottom = TextTop + yoff + ((32 - face->size->metrics.descender) >> 6); DestRect.bottom = TextTop + yoff + ((32 - fixDescender) >> 6);
MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom); MouseSafetyOnDrawStart(dc->ppdev, DestRect.left, DestRect.top, DestRect.right, DestRect.bottom);
IntEngBitBlt( IntEngBitBlt(
&psurf->SurfObj, &psurf->SurfObj,
@ -3777,6 +3828,7 @@ NtGdiGetCharABCWidthsW(
UINT i, glyph_index, BufferSize; UINT i, glyph_index, BufferSize;
HFONT hFont = 0; HFONT hFont = 0;
NTSTATUS Status = STATUS_SUCCESS; NTSTATUS Status = STATUS_SUCCESS;
PMATRIX pmxWorldToDevice;
if (pwch) if (pwch)
{ {
@ -3823,6 +3875,9 @@ NtGdiGetCharABCWidthsW(
pdcattr = dc->pdcattr; pdcattr = dc->pdcattr;
hFont = pdcattr->hlfntNew; hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont); TextObj = RealizeFontInit(hFont);
/* Get the DC's world-to-device transformation matrix */
pmxWorldToDevice = DC_pmxWorldToDevice(dc);
DC_UnlockDc(dc); DC_UnlockDc(dc);
if (TextObj == NULL) if (TextObj == NULL)
@ -3866,7 +3921,7 @@ NtGdiGetCharABCWidthsW(
/* FIXME: Should set character height if neg */ /* FIXME: Should set character height if neg */
(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ?
dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)));
FtSetCoordinateTransform(face, dc); FtSetCoordinateTransform(face, pmxWorldToDevice);
for (i = FirstChar; i < FirstChar+Count; i++) for (i = FirstChar; i < FirstChar+Count; i++)
{ {
@ -3951,6 +4006,7 @@ NtGdiGetCharWidthW(
FT_CharMap charmap, found = NULL; FT_CharMap charmap, found = NULL;
UINT i, glyph_index, BufferSize; UINT i, glyph_index, BufferSize;
HFONT hFont = 0; HFONT hFont = 0;
PMATRIX pmxWorldToDevice;
if (pwc) if (pwc)
{ {
@ -3991,6 +4047,8 @@ NtGdiGetCharWidthW(
pdcattr = dc->pdcattr; pdcattr = dc->pdcattr;
hFont = pdcattr->hlfntNew; hFont = pdcattr->hlfntNew;
TextObj = RealizeFontInit(hFont); TextObj = RealizeFontInit(hFont);
/* Get the DC's world-to-device transformation matrix */
pmxWorldToDevice = DC_pmxWorldToDevice(dc);
DC_UnlockDc(dc); DC_UnlockDc(dc);
if (TextObj == NULL) if (TextObj == NULL)
@ -4034,7 +4092,7 @@ NtGdiGetCharWidthW(
/* FIXME: Should set character height if neg */ /* FIXME: Should set character height if neg */
(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ? (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ?
dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight))); dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)));
FtSetCoordinateTransform(face, dc); FtSetCoordinateTransform(face, pmxWorldToDevice);
for (i = FirstChar; i < FirstChar+Count; i++) for (i = FirstChar; i < FirstChar+Count; i++)
{ {