ChangeLog: Improved font glyph transformation support in GetGlyphOutline. Description: Added support for font width scaling. Needed to store the font average width in the GdiFont structure in order to calculate the scaling factor. Unified the scaling, the rotation and the optional user-defined transformations into a single transformation. Makes the code a lot easier to read. Added some sanity checks here and there. Fixed some arithmetic issues. Look at this line in particular: lpgm->gmCellIncY = -((vec.y+63) >> 6); Dave Belanger: dave.belanger@cimmetry.com --- wine-20030911/dlls/gdi/freetype.c Fri Sep 5 19:08:38 2003 +++ wine/dlls/gdi/freetype.c Fri Oct 3 10:53:01 2003 @@ -90,12 +90,13 @@ static void *ft_handle = NULL; #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL -MAKE_FUNCPTR(FT_Cos); +MAKE_FUNCPTR(FT_Vector_Unit); MAKE_FUNCPTR(FT_Done_Face); MAKE_FUNCPTR(FT_Get_Char_Index); MAKE_FUNCPTR(FT_Get_Sfnt_Table); MAKE_FUNCPTR(FT_Init_FreeType); MAKE_FUNCPTR(FT_Load_Glyph); +MAKE_FUNCPTR(FT_Matrix_Multiply); MAKE_FUNCPTR(FT_MulFix); MAKE_FUNCPTR(FT_New_Face); MAKE_FUNCPTR(FT_Outline_Get_Bitmap); @@ -103,8 +104,7 @@ MAKE_FUNCPTR(FT_Outline_Translate); MAKE_FUNCPTR(FT_Select_Charmap); MAKE_FUNCPTR(FT_Set_Pixel_Sizes); -MAKE_FUNCPTR(FT_Sin); -MAKE_FUNCPTR(FT_Vector_Rotate); +MAKE_FUNCPTR(FT_Vector_Transform); #undef MAKE_FUNCPTR static void (*pFT_Library_Version)(FT_Library,FT_Int*,FT_Int*,FT_Int*); static FT_Error (*pFT_Load_Sfnt_Table)(FT_Face,FT_ULong,FT_Long,FT_Byte*,FT_ULong*); @@ -148,6 +148,7 @@ GM *gm; DWORD gmsize; HFONT hfont; + LONG aveWidth; SHORT yMax; SHORT yMin; OUTLINETEXTMETRICW *potm; @@ -226,6 +227,27 @@ static FontSubst *substlist = NULL; static BOOL have_installed_roman_font = FALSE; /* CreateFontInstance will fail if this is still FALSE */ +/* + This function builds an FT_Fixed from a float. It puts the integer part + in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed. + It fails if the integer part of the float number is greater than SHORT_MAX. +*/ +static inline FT_Fixed FT_FixedFromFloat(float f) +{ + short value = f; + unsigned short fract = (f - value) * 0xFFFF; + return (FT_Fixed)((long)value << 16 | (unsigned long)fract); +} + +/* + This function builds an FT_Fixed from a FIXED. It simply put f.value + in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed. +*/ +static inline FT_Fixed FT_FixedFromFIXED(FIXED f) +{ + return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract); +} + static BOOL AddFontFileToList(char *file, char *fake_family) { FT_Face ft_face; @@ -661,12 +683,13 @@ #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_Cos) + LOAD_FUNCPTR(FT_Vector_Unit) LOAD_FUNCPTR(FT_Done_Face) LOAD_FUNCPTR(FT_Get_Char_Index) LOAD_FUNCPTR(FT_Get_Sfnt_Table) LOAD_FUNCPTR(FT_Init_FreeType) LOAD_FUNCPTR(FT_Load_Glyph) + LOAD_FUNCPTR(FT_Matrix_Multiply) LOAD_FUNCPTR(FT_MulFix) LOAD_FUNCPTR(FT_New_Face) LOAD_FUNCPTR(FT_Outline_Get_Bitmap) @@ -674,8 +697,7 @@ LOAD_FUNCPTR(FT_Outline_Translate) LOAD_FUNCPTR(FT_Select_Charmap) LOAD_FUNCPTR(FT_Set_Pixel_Sizes) - LOAD_FUNCPTR(FT_Sin) - LOAD_FUNCPTR(FT_Vector_Rotate) + LOAD_FUNCPTR(FT_Vector_Transform) #undef LOAD_FUNCPTR /* Don't warn if this one is missing */ @@ -1231,6 +1253,7 @@ TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont); ret->hfont = hfont; + ret->aveWidth= lf.lfWidth; ret->next = GdiFontList; GdiFontList = ret; @@ -1513,6 +1536,7 @@ LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf, const MAT2* lpmat) { + static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)}; FT_Face ft_face = font->ft_face; FT_UInt glyph_index; DWORD width, height, pitch, needed = 0; @@ -1521,6 +1545,10 @@ INT left, right, top = 0, bottom = 0; FT_Angle angle = 0; FT_Int load_flags = FT_LOAD_DEFAULT; + float widthRatio = 1.0; + FT_Matrix transMat = identityMat; + BOOL needsTransform = FALSE; + TRACE("%p, %04x, %08x, %p, %08lx, %p, %p\n", font, glyph, format, lpgm, buflen, buf, lpmat); @@ -1542,7 +1570,7 @@ } } - if(font->orientation || (format != GGO_METRICS && format != GGO_BITMAP)) + if(font->orientation || (format != GGO_METRICS && format != GGO_BITMAP) || font->aveWidth || lpmat) load_flags |= FT_LOAD_NO_BITMAP; err = pFT_Load_Glyph(ft_face, glyph_index, load_flags); @@ -1551,16 +1579,58 @@ FIXME("FT_Load_Glyph on index %x returns %d\n", glyph_index, err); return GDI_ERROR; } + + /* Scaling factor */ + if (font->aveWidth && font->potm) { + widthRatio = (float)font->aveWidth * font->xform.eM11 / (float) font->potm->otmTextMetrics.tmAveCharWidth; + } - left = ft_face->glyph->metrics.horiBearingX & -64; - right = ((ft_face->glyph->metrics.horiBearingX + - ft_face->glyph->metrics.width) + 63) & -64; + left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64; + right = (INT)((ft_face->glyph->metrics.horiBearingX + ft_face->glyph->metrics.width) * widthRatio + 63) & -64; - font->gm[glyph_index].adv = (ft_face->glyph->metrics.horiAdvance + 63) >> 6; + font->gm[glyph_index].adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6; font->gm[glyph_index].lsb = left >> 6; font->gm[glyph_index].bbx = (right - left) >> 6; - if(font->orientation == 0) { + /* Scaling transform */ + if(font->aveWidth) { + FT_Matrix scaleMat; + scaleMat.xx = FT_FixedFromFloat(widthRatio); + scaleMat.xy = 0; + scaleMat.yx = 0; + scaleMat.yy = (1 << 16); + + pFT_Matrix_Multiply(&scaleMat, &transMat); + needsTransform = TRUE; + } + + /* Rotation transform */ + if(font->orientation) { + FT_Matrix rotationMat; + FT_Vector vecAngle; + angle = FT_FixedFromFloat((float)font->orientation / 10.0); + pFT_Vector_Unit(&vecAngle, angle); + rotationMat.xx = vecAngle.x; + rotationMat.xy = -vecAngle.y; + rotationMat.yx = -rotationMat.xy; + rotationMat.yy = rotationMat.xx; + + pFT_Matrix_Multiply(&rotationMat, &transMat); + needsTransform = TRUE; + } + + /* Extra transformation specified by caller */ + if (lpmat) { + FT_Matrix extraMat; + extraMat.xx = FT_FixedFromFIXED(lpmat->eM11); + extraMat.xy = FT_FixedFromFIXED(lpmat->eM21); + extraMat.yx = FT_FixedFromFIXED(lpmat->eM12); + extraMat.yy = FT_FixedFromFIXED(lpmat->eM22); + pFT_Matrix_Multiply(&extraMat, &transMat); + needsTransform = TRUE; + } + + if(!needsTransform) { top = (ft_face->glyph->metrics.horiBearingY + 63) & -64; bottom = (ft_face->glyph->metrics.horiBearingY - ft_face->glyph->metrics.height) & -64; @@ -1569,17 +1639,14 @@ } else { INT xc, yc; FT_Vector vec; - angle = font->orientation / 10 << 16; - angle |= ((font->orientation % 10) * (1 << 16)) / 10; - TRACE("angle %ld\n", angle >> 16); for(xc = 0; xc < 2; xc++) { for(yc = 0; yc < 2; yc++) { - vec.x = ft_face->glyph->metrics.horiBearingX + - xc * ft_face->glyph->metrics.width; + vec.x = (ft_face->glyph->metrics.horiBearingX + + xc * ft_face->glyph->metrics.width); vec.y = ft_face->glyph->metrics.horiBearingY - yc * ft_face->glyph->metrics.height; TRACE("Vec %ld,%ld\n", vec.x, vec.y); - pFT_Vector_Rotate(&vec, angle); + pFT_Vector_Transform(&vec, &transMat); if(xc == 0 && yc == 0) { left = right = vec.x; top = bottom = vec.y; @@ -1599,9 +1666,9 @@ TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom); vec.x = ft_face->glyph->metrics.horiAdvance; vec.y = 0; - pFT_Vector_Rotate(&vec, angle); + pFT_Vector_Transform(&vec, &transMat); lpgm->gmCellIncX = (vec.x+63) >> 6; - lpgm->gmCellIncY = -(vec.y+63) >> 6; + lpgm->gmCellIncY = -((vec.y+63) >> 6); } lpgm->gmBlackBoxX = (right - left) >> 6; lpgm->gmBlackBoxY = (top - bottom) >> 6; @@ -1614,6 +1681,10 @@ if(format == GGO_METRICS) return 1; /* FIXME */ + if (buf && !buflen){ + return GDI_ERROR; + } + if(ft_face->glyph->format != ft_glyph_format_outline && format != GGO_BITMAP) { FIXME("loaded a bitmap\n"); return GDI_ERROR; @@ -1623,7 +1694,7 @@ case GGO_BITMAP: width = lpgm->gmBlackBoxX; height = lpgm->gmBlackBoxY; - pitch = (width + 31) / 32 * 4; + pitch = ((width + 31) >> 5) << 2; needed = pitch * height; if(!buf || !buflen) break; @@ -1649,16 +1720,10 @@ ft_bitmap.pixel_mode = ft_pixel_mode_mono; ft_bitmap.buffer = buf; - if(font->orientation) { - FT_Matrix matrix; - matrix.xx = matrix.yy = pFT_Cos(angle); - matrix.xy = -pFT_Sin(angle); - matrix.yx = -matrix.xy; - - pFT_Outline_Transform(&ft_face->glyph->outline, &matrix); + if(needsTransform) { + pFT_Outline_Transform(&ft_face->glyph->outline, &transMat); } - if (lpmat) pFT_Outline_Transform(&ft_face->glyph->outline, (FT_Matrix *)lpmat); pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom ); /* Note: FreeType will only set 'black' bits for us. */ @@ -1692,15 +1757,10 @@ ft_bitmap.pixel_mode = ft_pixel_mode_grays; ft_bitmap.buffer = buf; - if(font->orientation) { - FT_Matrix matrix; - matrix.xx = matrix.yy = pFT_Cos(angle); - matrix.xy = -pFT_Sin(angle); - matrix.yx = -matrix.xy; - pFT_Outline_Transform(&ft_face->glyph->outline, &matrix); + if(needsTransform) { + pFT_Outline_Transform(&ft_face->glyph->outline, &transMat); } - if (lpmat) pFT_Outline_Transform(&ft_face->glyph->outline, (FT_Matrix *)lpmat); pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom ); pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap); @@ -1739,7 +1799,9 @@ if(buflen == 0) buf = NULL; - if (lpmat) pFT_Outline_Transform(outline, (FT_Matrix *)lpmat); + if (needsTransform && buf) { + pFT_Outline_Transform(outline, &transMat); + } for(contour = 0; contour < outline->n_contours; contour++) { pph_start = needed; @@ -1817,7 +1879,9 @@ FT_Vector cubic_control[4]; if(buflen == 0) buf = NULL; - if (lpmat) pFT_Outline_Transform(outline, (FT_Matrix *)lpmat); + if (needsTransform && buf) { + pFT_Outline_Transform(outline, &transMat); + } for(contour = 0; contour < outline->n_contours; contour++) { pph_start = needed; @@ -1924,6 +1988,10 @@ } if(!font->potm) return FALSE; memcpy(ptm, &font->potm->otmTextMetrics, sizeof(*ptm)); + + if (font->aveWidth) { + ptm->tmAveCharWidth = font->aveWidth * font->xform.eM11; + } return TRUE; } @@ -2035,6 +2103,9 @@ (pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6); TM.tmAveCharWidth = (pFT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6; + if (TM.tmAveCharWidth == 0) { + TM.tmAveCharWidth = 1; + } TM.tmMaxCharWidth = (pFT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6; TM.tmWeight = font->fake_bold ? FW_BOLD : pOS2->usWeightClass; TM.tmOverhang = 0;