Chromium Code Reviews| Index: src/ports/SkFontHost_FreeType.cpp |
| =================================================================== |
| --- src/ports/SkFontHost_FreeType.cpp (revision 12380) |
| +++ src/ports/SkFontHost_FreeType.cpp (working copy) |
| @@ -58,11 +58,21 @@ |
| #include <freetype/ftsynth.h> |
| #endif |
| +// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA |
| +// were introduced in FreeType 2.5.0. |
| +// The following may be removed once FreeType 2.5.0 is required to build. |
| +#ifndef FT_LOAD_COLOR |
| +# define FT_LOAD_COLOR ( 1L << 20 ) |
| +# define FT_PIXEL_MODE_BGRA 7 |
| +#endif |
| + |
| //#define ENABLE_GLYPH_SPEW // for tracing calls |
| //#define DUMP_STRIKE_CREATION |
| //#define SK_GAMMA_APPLY_TO_A8 |
| +//#define DEBUG_METRICS |
| + |
| using namespace skia_advanced_typeface_metrics_utils; |
| static bool isLCD(const SkScalerContext::Rec& rec) { |
| @@ -184,6 +194,7 @@ |
| SkFaceRec* fFaceRec; |
| FT_Face fFace; // reference to shared face in gFaceRecHead |
| FT_Size fFTSize; // our own copy |
| + FT_Int fStrikeIndex; |
| SkFixed fScaleX, fScaleY; |
| FT_Matrix fMatrix22; |
| uint32_t fLoadGlyphFlags; |
| @@ -749,6 +760,110 @@ |
| return true; |
| } |
| +#ifdef DEBUG_METRICS |
| +static void dumpFTFaceMetrics(const FT_Face face) { |
| + SkASSERT(face); |
| + SkDebugf(" units_per_EM: %hu\n", face->units_per_EM); |
| + SkDebugf(" ascender: %hd\n", face->ascender); |
| + SkDebugf(" descender: %hd\n", face->descender); |
| + SkDebugf(" height: %hd\n", face->height); |
| + SkDebugf(" max_advance_width: %hd\n", face->max_advance_width); |
| + SkDebugf(" max_advance_height: %hd\n", face->max_advance_height); |
| +} |
| + |
| +static void dumpFTSize(const FT_Size size) { |
| + SkASSERT(size); |
| + SkDebugf(" x_ppem: %hu\n", size->metrics.x_ppem); |
| + SkDebugf(" y_ppem: %hu\n", size->metrics.y_ppem); |
| + SkDebugf(" x_scale: %f\n", size->metrics.x_scale / 65536.0f); |
| + SkDebugf(" y_scale: %f\n", size->metrics.y_scale / 65536.0f); |
| + SkDebugf(" ascender: %f\n", size->metrics.ascender / 64.0f); |
| + SkDebugf(" descender: %f\n", size->metrics.descender / 64.0f); |
| + SkDebugf(" height: %f\n", size->metrics.height / 64.0f); |
| + SkDebugf(" max_advance: %f\n", size->metrics.max_advance / 64.0f); |
| +} |
| + |
| +static void dumpBitmapStrikeMetrics(const FT_Face face, int strikeIndex) { |
| + SkASSERT(face); |
| + SkASSERT(strikeIndex >= 0 && strikeIndex < face->num_fixed_sizes); |
| + SkDebugf(" height: %hd\n", face->available_sizes[strikeIndex].height); |
| + SkDebugf(" width: %hd\n", face->available_sizes[strikeIndex].width); |
| + SkDebugf(" size: %f\n", face->available_sizes[strikeIndex].size / 64.0f); |
| + SkDebugf(" x_ppem: %f\n", face->available_sizes[strikeIndex].x_ppem / 64.0f); |
| + SkDebugf(" y_ppem: %f\n", face->available_sizes[strikeIndex].y_ppem / 64.0f); |
| +} |
| + |
| +static void dumpSkFontMetrics(const SkPaint::FontMetrics& metrics) { |
| + SkDebugf(" fTop: %f\n", metrics.fTop); |
| + SkDebugf(" fAscent: %f\n", metrics.fAscent); |
| + SkDebugf(" fDescent: %f\n", metrics.fDescent); |
| + SkDebugf(" fBottom: %f\n", metrics.fBottom); |
| + SkDebugf(" fLeading: %f\n", metrics.fLeading); |
| + SkDebugf(" fAvgCharWidth: %f\n", metrics.fAvgCharWidth); |
| + SkDebugf(" fXMin: %f\n", metrics.fXMin); |
| + SkDebugf(" fXMax: %f\n", metrics.fXMax); |
| + SkDebugf(" fXHeight: %f\n", metrics.fXHeight); |
| +} |
| + |
| +static void dumpSkGlyphMetrics(const SkGlyph& glyph) { |
| + SkDebugf(" fAdvanceX: %f\n", SkFixedToScalar(glyph.fAdvanceX)); |
| + SkDebugf(" fAdvanceY: %f\n", SkFixedToScalar(glyph.fAdvanceY)); |
| + SkDebugf(" fWidth: %hu\n", glyph.fWidth); |
| + SkDebugf(" fHeight: %hu\n", glyph.fHeight); |
| + SkDebugf(" fTop: %hd\n", glyph.fTop); |
| + SkDebugf(" fLeft: %hd\n", glyph.fLeft); |
| +} |
| +#endif |
| + |
| +static FT_Int chooseBitmapStrike(FT_Face face, SkFixed scaleY) { |
| + // early out if face is bad |
| + if (face == NULL) { |
| + SkDEBUGF(("chooseBitmapStrike aborted due to NULL face\n")); |
| + return -1; |
| + } |
| + // determine target ppem |
| + FT_Pos targetPPEM = SkFixedToFDot6(scaleY); |
| + // find a bitmap strike equal to or just larger than the requested size |
| + FT_Int chosenStrikeIndex = -1; |
| + FT_Pos chosenPPEM = 0; |
| + for (FT_Int strikeIndex = 0; strikeIndex < face->num_fixed_sizes; ++strikeIndex) { |
| + FT_Pos thisPPEM = face->available_sizes[strikeIndex].y_ppem; |
| + if (thisPPEM == targetPPEM) { |
| + // exact match - our search stops here |
| + chosenPPEM = thisPPEM; |
| + chosenStrikeIndex = strikeIndex; |
| + break; |
| + } else if (chosenPPEM < targetPPEM) { |
| + // attempt to increase chosenPPEM |
| + if (thisPPEM > chosenPPEM) { |
| + chosenPPEM = thisPPEM; |
| + chosenStrikeIndex = strikeIndex; |
| + } |
| + } else { |
| + // attempt to decrease chosenPPEM, but not below targetPPEM |
| + if (thisPPEM < chosenPPEM && thisPPEM > targetPPEM) { |
| + chosenPPEM = thisPPEM; |
| + chosenStrikeIndex = strikeIndex; |
| + } |
| + } |
| + } |
| + if (chosenStrikeIndex != -1) { |
| + // use the chosen strike |
| +#ifdef DEBUG_METRICS |
| + SkDebugf("chooseBitmapStrike: chose strike %d for face \"%s\" at size %f\n", |
| + chosenStrikeIndex, face->family_name, SkFixedToScalar(scaleY)); |
| + dumpBitmapStrikeMetrics(face, chosenStrikeIndex); |
| +#endif |
| + FT_Error err = FT_Select_Size(face, chosenStrikeIndex); |
| + if (err != 0) { |
| + SkDEBUGF(("FT_Select_Size(%s, %d) returned 0x%x\n", face->family_name, |
| + chosenStrikeIndex, err)); |
| + chosenStrikeIndex = -1; |
| + } |
| + } |
| + return chosenStrikeIndex; |
| +} |
| + |
| SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, |
| const SkDescriptor* desc) |
| : SkScalerContext_FreeType_Base(typeface, desc) { |
| @@ -762,6 +877,7 @@ |
| ++gFTCount; |
| // load the font file |
| + fStrikeIndex = -1; |
| fFTSize = NULL; |
| fFace = NULL; |
| fFaceRec = ref_ft_face(typeface); |
| @@ -821,9 +937,9 @@ |
| fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); |
| // compute the flags we send to Load_Glyph |
| + bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag); |
| { |
| FT_Int32 loadFlags = FT_LOAD_DEFAULT; |
| - bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag); |
| if (SkMask::kBW_Format == fRec.fMaskFormat) { |
| // See http://code.google.com/p/chromium/issues/detail?id=43252#c24 |
| @@ -881,42 +997,49 @@ |
| loadFlags |= FT_LOAD_VERTICAL_LAYOUT; |
| } |
| + loadFlags |= FT_LOAD_COLOR; |
|
bungeman-skia
2013/11/27 00:04:49
We should actually check FT_HAS_COLOR(face) and se
|
| + |
| fLoadGlyphFlags = loadFlags; |
| - fDoLinearMetrics = linearMetrics; |
| } |
| - // now create the FT_Size |
| + FT_Error err = FT_New_Size(fFace, &fFTSize); |
| + if (err != 0) { |
| + SkDEBUGF(("FT_New_Size returned %x for face %s\n", err, fFace->family_name)); |
| + fFace = NULL; |
| + return; |
| + } |
| + err = FT_Activate_Size(fFTSize); |
| + if (err != 0) { |
| + SkDEBUGF(("FT_Activate_Size(%08x, 0x%x, 0x%x) returned 0x%x\n", fFace, fScaleX, fScaleY, |
| + err)); |
| + fFTSize = NULL; |
| + return; |
| + } |
| - { |
| - FT_Error err; |
| + if (fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) { |
| + fStrikeIndex = chooseBitmapStrike(fFace, fScaleY); |
| + } |
| - err = FT_New_Size(fFace, &fFTSize); |
| - if (err != 0) { |
| - SkDEBUGF(("SkScalerContext_FreeType::FT_New_Size(%x): FT_Set_Char_Size(0x%x, 0x%x) returned 0x%x\n", |
| - fFaceRec->fFontID, fScaleX, fScaleY, err)); |
| - fFace = NULL; |
| - return; |
| + if (fStrikeIndex == -1) { |
| + if (fFace->face_flags & FT_FACE_FLAG_SCALABLE) { |
| + err = FT_Set_Char_Size(fFace, |
| + SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), |
| + 72, 72); |
| + if (err != 0) { |
| + SkDEBUGF(("FT_Set_CharSize(%08x, 0x%x, 0x%x) returned 0x%x\n", fFace, fScaleX, |
| + fScaleY, err)); |
| + fFace = NULL; |
| + return; |
| + } |
| + FT_Set_Transform(fFace, &fMatrix22, NULL); |
| + } else { |
| + SkDEBUGF(("no glyphs for font \"%s\" size %f?\n", fFace->family_name, |
| + SkFixedToScalar(fScaleY))); |
| } |
| - |
| - err = FT_Activate_Size(fFTSize); |
| - if (err != 0) { |
| - SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", |
| - fFaceRec->fFontID, fScaleX, fScaleY, err)); |
| - fFTSize = NULL; |
| - } |
| - |
| - err = FT_Set_Char_Size( fFace, |
| - SkFixedToFDot6(fScaleX), SkFixedToFDot6(fScaleY), |
| - 72, 72); |
| - if (err != 0) { |
| - SkDEBUGF(("SkScalerContext_FreeType::FT_Set_Char_Size(%x, 0x%x, 0x%x) returned 0x%x\n", |
| - fFaceRec->fFontID, fScaleX, fScaleY, err)); |
| - fFace = NULL; |
| - return; |
| - } |
| - |
| - FT_Set_Transform( fFace, &fMatrix22, NULL); |
| + } else { |
| + linearMetrics = false; // no linear metrics for bitmap fonts |
| } |
| + fDoLinearMetrics = linearMetrics; |
| } |
| SkScalerContext_FreeType::~SkScalerContext_FreeType() { |
| @@ -940,18 +1063,18 @@ |
| this face with other context (at different sizes). |
| */ |
| FT_Error SkScalerContext_FreeType::setupSize() { |
| - FT_Error err = FT_Activate_Size(fFTSize); |
| - |
| + FT_Error err = FT_Activate_Size(fFTSize); |
| if (err != 0) { |
| SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%x, 0x%x, 0x%x) returned 0x%x\n", |
| - fFaceRec->fFontID, fScaleX, fScaleY, err)); |
| + fFaceRec->fFontID, fScaleX, fScaleY, err)); |
| fFTSize = NULL; |
| - } else { |
| - // seems we need to reset this every time (not sure why, but without it |
| - // I get random italics from some other fFTSize) |
| - FT_Set_Transform( fFace, &fMatrix22, NULL); |
| + return err; |
| } |
| - return err; |
| + |
| + // seems we need to reset this every time (not sure why, but without it |
| + // I get random italics from some other fFTSize) |
| + FT_Set_Transform(fFace, &fMatrix22, NULL); |
| + return 0; |
| } |
| unsigned SkScalerContext_FreeType::generateGlyphCount() { |
| @@ -1061,6 +1184,17 @@ |
| } |
| } |
| +inline void scaleGlyphMetrics(SkGlyph& glyph, SkScalar scale) { |
| + glyph.fWidth *= scale; |
| + glyph.fHeight *= scale; |
| + glyph.fTop *= scale; |
| + glyph.fLeft *= scale; |
| + |
| + SkFixed fixedScale = SkScalarToFixed(scale); |
| + glyph.fAdvanceX = SkFixedMul(glyph.fAdvanceX, fixedScale); |
| + glyph.fAdvanceY = SkFixedMul(glyph.fAdvanceY, fixedScale); |
| +} |
| + |
| void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { |
| SkAutoMutexAcquire ac(gFTMutex); |
| @@ -1085,32 +1219,28 @@ |
| } |
| switch ( fFace->glyph->format ) { |
| - case FT_GLYPH_FORMAT_OUTLINE: { |
| - FT_BBox bbox; |
| - |
| + case FT_GLYPH_FORMAT_OUTLINE: |
| if (0 == fFace->glyph->outline.n_contours) { |
| glyph->fWidth = 0; |
| glyph->fHeight = 0; |
| glyph->fTop = 0; |
| glyph->fLeft = 0; |
| - break; |
| - } |
| + } else { |
| + if (fRec.fFlags & kEmbolden_Flag && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) { |
| + emboldenOutline(fFace, &fFace->glyph->outline); |
| + } |
| - if (fRec.fFlags & kEmbolden_Flag) { |
| - emboldenOutline(fFace, &fFace->glyph->outline); |
| - } |
| + FT_BBox bbox; |
| + getBBoxForCurrentGlyph(glyph, &bbox, true); |
| - getBBoxForCurrentGlyph(glyph, &bbox, true); |
| + glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin)); |
| + glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin)); |
| + glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax)); |
| + glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin)); |
| - glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin)); |
| - glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin)); |
| - glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax)); |
| - glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin)); |
| - |
| - updateGlyphIfLCD(glyph); |
| - |
| + updateGlyphIfLCD(glyph); |
| + } |
| break; |
| - } |
| case FT_GLYPH_FORMAT_BITMAP: |
| if (fRec.fFlags & kEmbolden_Flag) { |
| @@ -1127,6 +1257,10 @@ |
| fFace->glyph->bitmap_top += SkFDot6Floor(vector.y); |
| } |
| + if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { |
| + glyph->fMaskFormat = SkMask::kARGB32_Format; |
| + } |
| + |
| glyph->fWidth = SkToU16(fFace->glyph->bitmap.width); |
| glyph->fHeight = SkToU16(fFace->glyph->bitmap.rows); |
| glyph->fTop = -SkToS16(fFace->glyph->bitmap_top); |
| @@ -1161,7 +1295,21 @@ |
| } |
| } |
| + if (fFace->glyph->format == FT_GLYPH_FORMAT_BITMAP && fScaleY && fFace->size->metrics.y_ppem) { |
| +#ifdef DEBUG_METRICS |
| + SkDebugf("pre-scale glyph metrics:\n"); |
| + dumpSkGlyphMetrics(*glyph); |
| +#endif |
| + // NOTE: both dimensions are scaled by y_ppem. this is WAI. |
| + scaleGlyphMetrics(*glyph, SkScalarDiv(SkFixedToScalar(fScaleY), |
| + SkIntToScalar(fFace->size->metrics.y_ppem))); |
| + } |
| +#ifdef DEBUG_METRICS |
| + SkDebugf("post-scale glyph metrics:\n"); |
| + dumpSkGlyphMetrics(*glyph); |
| +#endif |
| + |
| #ifdef ENABLE_GLYPH_SPEW |
| SkDEBUGF(("FT_Set_Char_Size(this:%p sx:%x sy:%x ", this, fScaleX, fScaleY)); |
| SkDEBUGF(("Metrics(glyph:%d flags:0x%x) w:%d\n", glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, glyph->fWidth)); |
| @@ -1228,16 +1376,16 @@ |
| } |
| } |
| -void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, |
| - SkPaint::FontMetrics* my) { |
| +#ifdef DEBUG_METRICS |
| +void generateFontMetricsOLD(FT_Face face, SkScalar scaleX, SkScalar scaleY, |
| + uint32_t loadGlyphFlags, uint16_t recFlags, SkMatrix matrix22Scalar, |
| + SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) { |
| if (NULL == mx && NULL == my) { |
| return; |
| } |
| - SkAutoMutexAcquire ac(gFTMutex); |
| - |
| - if (this->setupSize()) { |
| - ERROR: |
| + if (false) { |
| + ERROR: |
| if (mx) { |
| sk_bzero(mx, sizeof(SkPaint::FontMetrics)); |
| } |
| @@ -1247,7 +1395,6 @@ |
| return; |
| } |
| - FT_Face face = fFace; |
| int upem = face->units_per_EM; |
| if (upem <= 0) { |
| goto ERROR; |
| @@ -1255,9 +1402,8 @@ |
| SkPoint pts[6]; |
| SkFixed ys[6]; |
| - SkScalar scaleY = fScale.y(); |
| - SkScalar mxy = fMatrix22Scalar.getSkewX(); |
| - SkScalar myy = fMatrix22Scalar.getScaleY(); |
| + SkScalar mxy = matrix22Scalar.getSkewX(); |
| + SkScalar myy = matrix22Scalar.getScaleY(); |
| SkScalar xmin = SkIntToScalar(face->bbox.xMin) / upem; |
| SkScalar xmax = SkIntToScalar(face->bbox.xMax) / upem; |
| @@ -1266,8 +1412,6 @@ |
| leading = 0; |
| } |
| - // Try to get the OS/2 table from the font. This contains the specific |
| - // average font width metrics which Windows uses. |
| TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2); |
| ys[0] = -face->bbox.yMax; |
| @@ -1279,23 +1423,22 @@ |
| SkScalar x_height; |
| if (os2 && os2->sxHeight) { |
| - x_height = fScale.x() * os2->sxHeight / upem; |
| + x_height = scaleX * os2->sxHeight / upem; |
| } else { |
| - const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x'); |
| + const FT_UInt x_glyph = FT_Get_Char_Index(face, 'x'); |
| if (x_glyph) { |
| FT_BBox bbox; |
| - FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags); |
| - if (fRec.fFlags & kEmbolden_Flag) { |
| - emboldenOutline(fFace, &fFace->glyph->outline); |
| + FT_Load_Glyph(face, x_glyph, loadGlyphFlags); |
| + if (recFlags & SkScalerContext::kEmbolden_Flag) { |
| + SkDebugf("x_height is incorrect (skipped embolden step)\n"); |
| } |
| - FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); |
| + FT_Outline_Get_CBox(&face->glyph->outline, &bbox); |
| x_height = bbox.yMax / 64.0f; |
| - } else { |
| + } else { |
| x_height = 0; |
| - } |
| + } |
| } |
| - // convert upem-y values into scalar points |
| for (int i = 0; i < 6; i++) { |
| SkScalar y = scaleY * ys[i] / upem; |
| pts[i].set(y * mxy, y * myy); |
| @@ -1311,8 +1454,11 @@ |
| mx->fXMin = xmin; |
| mx->fXMax = xmax; |
| mx->fXHeight = x_height; |
| - } |
| - if (my) { |
| + SkDebugf("generateFontMetricsOLD(\"%s\"): mx is:\n", face->family_name); |
| + dumpSkFontMetrics(*mx); |
| + } |
| + |
| + if (my) { |
| my->fTop = pts[0].fY; |
| my->fAscent = pts[1].fY; |
| my->fDescent = pts[2].fY; |
| @@ -1322,7 +1468,155 @@ |
| my->fXMin = xmin; |
| my->fXMax = xmax; |
| my->fXHeight = x_height; |
| + SkDebugf("generateFontMetricsOLD(\"%s\"): my is:\n", face->family_name); |
| + dumpSkFontMetrics(*my); |
| + } |
| +} |
| +#endif |
| + |
| +void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx, |
| + SkPaint::FontMetrics* my) { |
| + if (NULL == mx && NULL == my) { |
| + return; |
| } |
| + |
| + SkAutoMutexAcquire ac(gFTMutex); |
| + |
| + if (this->setupSize()) { |
| + ERROR: |
| + if (mx) { |
| + sk_bzero(mx, sizeof(SkPaint::FontMetrics)); |
| + } |
| + if (my) { |
| + sk_bzero(my, sizeof(SkPaint::FontMetrics)); |
| + } |
| + return; |
| + } |
| + |
| + FT_Face face = fFace; |
| + SkScalar scaleX = fScale.x(); |
| + SkScalar scaleY = fScale.y(); |
| + SkScalar mxy = fMatrix22Scalar.getSkewX() * scaleY; |
| + SkScalar myy = fMatrix22Scalar.getScaleY() * scaleY; |
| + |
| +#ifdef DEBUG_METRICS |
| + SkDebugf("generateFontMetrics(\"%s\"):\n", face->family_name); |
| + dumpFTFaceMetrics(face); |
| + dumpFTSize(face->size); |
| +#endif |
| + |
| + // fetch units/EM from "head" table if needed (ie for bitmap fonts) |
| + SkScalar upem = SkIntToScalar(face->units_per_EM); |
| + if (!upem) { |
| + TT_Header* ttHeader = (TT_Header*)FT_Get_Sfnt_Table(face, ft_sfnt_head); |
| + if (ttHeader) { |
| + upem = SkIntToScalar(ttHeader->Units_Per_EM); |
| + } |
| + } |
| + |
| + // use the os/2 table as a source of reasonable defaults. |
| + SkScalar x_height = 0.0f; |
| + SkScalar avgCharWidth = 0.0f; |
| + TT_OS2* os2 = (TT_OS2*) FT_Get_Sfnt_Table(face, ft_sfnt_os2); |
| + if (os2) { |
| + x_height = scaleX * SkIntToScalar(os2->sxHeight) / upem; |
| + avgCharWidth = SkIntToScalar(os2->xAvgCharWidth) / upem; |
| + } |
| + |
| + // pull from format-specific metrics as needed |
| + SkScalar ascent, descent, leading, xmin, xmax, ymin, ymax; |
| + if (face->face_flags & FT_FACE_FLAG_SCALABLE) { // scalable outline font |
| + ascent = -SkIntToScalar(face->ascender) / upem; |
| + descent = -SkIntToScalar(face->descender) / upem; |
| + leading = SkIntToScalar(face->height + (face->descender - face->ascender)) / upem; |
| + xmin = SkIntToScalar(face->bbox.xMin) / upem; |
| + xmax = SkIntToScalar(face->bbox.xMax) / upem; |
| + ymin = -SkIntToScalar(face->bbox.yMin) / upem; |
| + ymax = -SkIntToScalar(face->bbox.yMax) / upem; |
| + // we may be able to synthesize x_height from outline |
| + if (!x_height) { |
| + const FT_UInt x_glyph = FT_Get_Char_Index(fFace, 'x'); |
| + if (x_glyph) { |
| + FT_BBox bbox; |
| + FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags); |
| + if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) { |
| + emboldenOutline(fFace, &fFace->glyph->outline); |
| + } |
| + FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox); |
| + x_height = SkIntToScalar(bbox.yMax) / 64.0f; |
| + } |
| + } |
| + } else if (fStrikeIndex != -1) { // bitmap strike metrics |
| +#ifdef DEBUG_METRICS |
| + dumpBitmapStrikeMetrics(fFace, fStrikeIndex); |
| +#endif |
| + SkScalar xppem = SkIntToScalar(face->size->metrics.x_ppem); |
| + SkScalar yppem = SkIntToScalar(face->size->metrics.y_ppem); |
| + ascent = -SkIntToScalar(face->size->metrics.ascender) / (yppem * 64.0f); |
| + descent = -SkIntToScalar(face->size->metrics.descender) / (yppem * 64.0f); |
| + leading = (SkIntToScalar(face->size->metrics.height) / (yppem * 64.0f)) |
| + + ascent - descent; |
| + xmin = 0.0f; |
| + xmax = SkIntToScalar(face->available_sizes[fStrikeIndex].width) / xppem; |
| + ymin = descent + leading; |
| + ymax = ascent - descent; |
| + if (!x_height) { |
| + x_height = -ascent; |
| + } |
| + if (!avgCharWidth) { |
| + avgCharWidth = xmax - xmin; |
| + } |
| + } else { |
| + goto ERROR; |
| + } |
| + |
| + // synthesize elements that were not provided by the os/2 table or format-specific metrics |
| + if (!x_height) { |
| + x_height = -ascent; |
| + } |
| + if (!avgCharWidth) { |
| + avgCharWidth = xmax - xmin; |
| + } |
| + |
| + // disallow negative linespacing |
| + if (leading < 0.0f) { |
| + leading = 0.0f; |
| + } |
| + |
| +#ifdef DEBUG_METRICS |
| + generateFontMetricsOLD(fFace, fScale.x(), fScale.y(), fLoadGlyphFlags, fRec.fFlags, |
| + fMatrix22Scalar, mx, my); |
| +#endif |
| + if (mx) { |
| + mx->fTop = ymax * mxy; |
| + mx->fAscent = ascent * mxy; |
| + mx->fDescent = descent * mxy; |
| + mx->fBottom = ymin * mxy; |
| + mx->fLeading = leading * mxy; |
| + mx->fAvgCharWidth = avgCharWidth * mxy; |
| + mx->fXMin = xmin; |
| + mx->fXMax = xmax; |
| + mx->fXHeight = x_height; |
| +#ifdef DEBUG_METRICS |
| + SkDebugf("generateFontMetrics(\"%s\"): mx is:\n", face->family_name); |
| + dumpSkFontMetrics(*mx); |
| +#endif |
| + } |
| + if (my) { |
| + my->fTop = ymax * myy; |
| + my->fAscent = ascent * myy; |
| + my->fDescent = descent * myy; |
| + my->fBottom = ymin * myy; |
| + my->fLeading = leading * myy; |
| + my->fAvgCharWidth = avgCharWidth * myy; |
| + my->fXMin = xmin; |
| + my->fXMax = xmax; |
| + my->fXHeight = x_height; |
| +#ifdef DEBUG_METRICS |
| + SkDebugf("generateFontMetrics(\"%s\"): my is:\n", face->family_name); |
| + dumpSkFontMetrics(*my); |
| +#endif |
| + } |
| } |
| /////////////////////////////////////////////////////////////////////////////// |