From 31e0c1379e6d0ce48196183e295b929af51fa74e Mon Sep 17 00:00:00 2001 From: bungeman Date: Fri, 15 Jul 2016 14:55:35 -0700 Subject: Rotate bitmap strikes with FreeType. BUG=skia:3490 GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2139703002 Review-Url: https://codereview.chromium.org/2139703002 --- src/ports/SkFontHost_FreeType.cpp | 212 +++++++++++++++++-------------- src/ports/SkFontHost_FreeType_common.cpp | 24 ++-- src/ports/SkFontHost_FreeType_common.h | 3 +- 3 files changed, 132 insertions(+), 107 deletions(-) diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp index 2e0b8b6ee3..d0f86d99a7 100644 --- a/src/ports/SkFontHost_FreeType.cpp +++ b/src/ports/SkFontHost_FreeType.cpp @@ -197,18 +197,23 @@ protected: SkUnichar generateGlyphToChar(uint16_t glyph) override; private: - FT_Face fFace; // reference to shared face in gFaceRecHead - FT_Size fFTSize; // our own copy - FT_Int fStrikeIndex; - FT_F26Dot6 fScaleX, fScaleY; - FT_Matrix fMatrix22; - uint32_t fLoadGlyphFlags; - bool fDoLinearMetrics; - bool fLCDIsVert; - - // Need scalar versions for generateFontMetrics - SkVector fScale; - SkMatrix fMatrix22Scalar; + FT_Face fFace; // Shared face from gFaceRecHead. + FT_Size fFTSize; // The size on the fFace for this scaler. + FT_Int fStrikeIndex; + + /** The rest of the matrix after FreeType handles the size. + * With outline font rasterization this is handled by FreeType with FT_Set_Transform. + * With bitmap only fonts this matrix must be applied to scale the bitmap. + */ + SkMatrix fMatrix22Scalar; + /** Same as fMatrix22Scalar, but in FreeType units and space. */ + FT_Matrix fMatrix22; + /** The actual size requested. */ + SkVector fScale; + + uint32_t fLoadGlyphFlags; + bool fDoLinearMetrics; + bool fLCDIsVert; FT_Error setupSize(); void getBBoxForCurrentGlyph(SkGlyph* glyph, FT_BBox* bbox, @@ -219,6 +224,7 @@ private: // Caller must lock gFTMutex before calling this function. // update FreeType2 glyph slot with glyph emboldened void emboldenIfNeeded(FT_Face face, FT_GlyphSlot glyph); + bool shouldSubpixelBitmap(const SkGlyph&, const SkMatrix&); }; /////////////////////////////////////////////////////////////////////////// @@ -747,47 +753,35 @@ bool SkTypeface_FreeType::onGetKerningPairAdjustments(const uint16_t glyphs[], return true; } +/** Returns the bitmap strike equal to or just larger than the requested size. */ static FT_Int chooseBitmapStrike(FT_Face face, FT_F26Dot6 scaleY) { - // early out if face is bad if (face == nullptr) { - SkDEBUGF(("chooseBitmapStrike aborted due to nullptr face\n")); + SkDEBUGF(("chooseBitmapStrike aborted due to nullptr face.\n")); return -1; } - // determine target ppem - FT_Pos targetPPEM = scaleY; - // find a bitmap strike equal to or just larger than the requested size + + FT_Pos requestedPPEM = scaleY; // FT_Bitmap_Size::y_ppem is in 26.6 format. 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) { + FT_Pos strikePPEM = face->available_sizes[strikeIndex].y_ppem; + if (strikePPEM == requestedPPEM) { // exact match - our search stops here - chosenPPEM = thisPPEM; - chosenStrikeIndex = strikeIndex; - break; - } else if (chosenPPEM < targetPPEM) { + return strikeIndex; + } else if (chosenPPEM < requestedPPEM) { // attempt to increase chosenPPEM - if (thisPPEM > chosenPPEM) { - chosenPPEM = thisPPEM; + if (chosenPPEM < strikePPEM) { + chosenPPEM = strikePPEM; chosenStrikeIndex = strikeIndex; } } else { - // attempt to decrease chosenPPEM, but not below targetPPEM - if (thisPPEM < chosenPPEM && thisPPEM > targetPPEM) { - chosenPPEM = thisPPEM; + // attempt to decrease chosenPPEM, but not below requestedPPEM + if (requestedPPEM < strikePPEM && strikePPEM < chosenPPEM) { + chosenPPEM = strikePPEM; chosenStrikeIndex = strikeIndex; } } } - if (chosenStrikeIndex != -1) { - // use the chosen strike - 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; } @@ -814,14 +808,12 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, } fRec.computeMatrices(SkScalerContextRec::kFull_PreMatrixScale, &fScale, &fMatrix22Scalar); - fMatrix22Scalar.setSkewX(-fMatrix22Scalar.getSkewX()); - fMatrix22Scalar.setSkewY(-fMatrix22Scalar.getSkewY()); - fScaleX = SkScalarToFDot6(fScale.fX); - fScaleY = SkScalarToFDot6(fScale.fY); + FT_F26Dot6 scaleX = SkScalarToFDot6(fScale.fX); + FT_F26Dot6 scaleY = SkScalarToFDot6(fScale.fY); fMatrix22.xx = SkScalarToFixed(fMatrix22Scalar.getScaleX()); - fMatrix22.xy = SkScalarToFixed(fMatrix22Scalar.getSkewX()); - fMatrix22.yx = SkScalarToFixed(fMatrix22Scalar.getSkewY()); + fMatrix22.xy = SkScalarToFixed(-fMatrix22Scalar.getSkewX()); + fMatrix22.yx = SkScalarToFixed(-fMatrix22Scalar.getSkewY()); fMatrix22.yy = SkScalarToFixed(fMatrix22Scalar.getScaleY()); fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); @@ -900,7 +892,7 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, FT_Size size; FT_Error err = FT_New_Size(ftFace.get(), &size); if (err != 0) { - SkDEBUGF(("FT_New_Size returned %x for face %s\n", err, ftFace->family_name)); + SkDEBUGF(("FT_New_Size(%s) returned 0x%x.\n", ftFace->family_name, err)); return nullptr; } return size; @@ -912,39 +904,54 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface, FT_Error err = FT_Activate_Size(ftSize.get()); if (err != 0) { - SkDEBUGF(("FT_Activate_Size(%08x, 0x%x, 0x%x) returned 0x%x\n", - ftFace.get(), fScaleX, fScaleY, err)); + SkDEBUGF(("FT_Activate_Size(%s) returned 0x%x.\n", ftFace->family_name, err)); return; } if (FT_IS_SCALABLE(ftFace)) { - err = FT_Set_Char_Size(ftFace.get(), fScaleX, fScaleY, 72, 72); + err = FT_Set_Char_Size(ftFace.get(), scaleX, scaleY, 72, 72); if (err != 0) { - SkDEBUGF(("FT_Set_CharSize(%08x, 0x%x, 0x%x) returned 0x%x\n", - ftFace.get(), fScaleX, fScaleY, err)); + SkDEBUGF(("FT_Set_CharSize(%s, %f, %f) returned 0x%x.\n", + ftFace->family_name, fScale.fX, fScale.fY, err)); return; } - FT_Set_Transform(ftFace.get(), &fMatrix22, nullptr); } else if (FT_HAS_FIXED_SIZES(ftFace)) { - fStrikeIndex = chooseBitmapStrike(ftFace.get(), fScaleY); + fStrikeIndex = chooseBitmapStrike(ftFace.get(), scaleY); if (fStrikeIndex == -1) { - SkDEBUGF(("no glyphs for font \"%s\" size %f?\n", - ftFace->family_name, SkFDot6ToScalar(fScaleY))); - } else { - // FreeType does no provide linear metrics for bitmap fonts. - linearMetrics = false; - - // FreeType documentation says: - // FT_LOAD_NO_BITMAP -- Ignore bitmap strikes when loading. - // Bitmap-only fonts ignore this flag. - // - // However, in FreeType 2.5.1 color bitmap only fonts do not ignore this flag. - // Force this flag off for bitmap only fonts. - fLoadGlyphFlags &= ~FT_LOAD_NO_BITMAP; + SkDEBUGF(("No glyphs for font \"%s\" size %f.\n", ftFace->family_name, fScale.fY)); + return; } + + err = FT_Select_Size(ftFace.get(), fStrikeIndex); + if (err != 0) { + SkDEBUGF(("FT_Select_Size(%s, %d) returned 0x%x.\n", + ftFace->family_name, fStrikeIndex, err)); + fStrikeIndex = -1; + return; + } + + // A non-ideal size was picked, so recompute the matrix. + // This adjusts for the difference between FT_Set_Char_Size and FT_Select_Size. + fMatrix22Scalar.preScale(fScale.x() / ftFace->size->metrics.x_ppem, + fScale.y() / ftFace->size->metrics.y_ppem); + fMatrix22.xx = SkScalarToFixed(fMatrix22Scalar.getScaleX()); + fMatrix22.xy = SkScalarToFixed(-fMatrix22Scalar.getSkewX()); + fMatrix22.yx = SkScalarToFixed(-fMatrix22Scalar.getSkewY()); + fMatrix22.yy = SkScalarToFixed(fMatrix22Scalar.getScaleY()); + + // FreeType does not provide linear metrics for bitmap fonts. + linearMetrics = false; + + // FreeType documentation says: + // FT_LOAD_NO_BITMAP -- Ignore bitmap strikes when loading. + // Bitmap-only fonts ignore this flag. + // + // However, in FreeType 2.5.1 color bitmap only fonts do not ignore this flag. + // Force this flag off for bitmap only fonts. + fLoadGlyphFlags &= ~FT_LOAD_NO_BITMAP; } else { - SkDEBUGF(("unknown kind of font \"%s\" size %f?\n", - fFace->family_name, SkFDot6ToScalar(fScaleY))); + SkDEBUGF(("Unknown kind of font \"%s\" size %f.\n", fFace->family_name, fScale.fY)); + return; } fFTSize = ftSize.release(); @@ -973,14 +980,8 @@ FT_Error SkScalerContext_FreeType::setupSize() { gFTMutex.assertHeld(); FT_Error err = FT_Activate_Size(fFTSize); if (err != 0) { - SkDEBUGF(("SkScalerContext_FreeType::FT_Activate_Size(%s %s, 0x%x, 0x%x) returned 0x%x\n", - fFace->family_name, fFace->style_name, fScaleX, fScaleY, err)); - fFTSize = nullptr; 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, nullptr); return 0; } @@ -1037,7 +1038,7 @@ void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) { glyph->fLsbDelta = 0; const SkScalar advanceScalar = SkFT_FixedToScalar(advance); glyph->fAdvanceX = SkScalarToFloat(fMatrix22Scalar.getScaleX() * advanceScalar); - glyph->fAdvanceY = -SkScalarToFloat(fMatrix22Scalar.getSkewY() * advanceScalar); + glyph->fAdvanceY = SkScalarToFloat(fMatrix22Scalar.getSkewY() * advanceScalar); return; } } @@ -1111,15 +1112,20 @@ void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) { } } -inline void scaleGlyphMetrics(SkGlyph& glyph, SkScalar scale) { - glyph.fWidth *= scale; - glyph.fHeight *= scale; - glyph.fTop *= scale; - glyph.fLeft *= scale; +bool SkScalerContext_FreeType::shouldSubpixelBitmap(const SkGlyph& glyph, const SkMatrix& matrix) { + // If subpixel rendering of a bitmap *can* be done. + bool mechanism = fFace->glyph->format == FT_GLYPH_FORMAT_BITMAP && + fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag && + (glyph.getSubXFixed() || glyph.getSubYFixed()); + + // If subpixel rendering of a bitmap *should* be done. + // 1. If the face is not scalable then always allow subpixel rendering. + // Otherwise, if the font has an 8ppem strike 7 will subpixel render but 8 won't. + // 2. If the matrix is already not identity the bitmap will already be resampled, + // so resampling slightly differently shouldn't make much difference. + bool policy = !FT_IS_SCALABLE(fFace) || !matrix.isIdentity(); - float floatScale = SkScalarToFloat(scale); - glyph.fAdvanceX *= floatScale; - glyph.fAdvanceY *= floatScale; + return mechanism && policy; } void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { @@ -1176,10 +1182,22 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { 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); - glyph->fLeft = SkToS16(fFace->glyph->bitmap_left); + { + SkRect rect = SkRect::MakeXYWH(SkIntToScalar(fFace->glyph->bitmap_left), + -SkIntToScalar(fFace->glyph->bitmap_top), + SkIntToScalar(fFace->glyph->bitmap.width), + SkIntToScalar(fFace->glyph->bitmap.rows)); + fMatrix22Scalar.mapRect(&rect); + if (this->shouldSubpixelBitmap(*glyph, fMatrix22Scalar)) { + rect.offset(SkFixedToScalar(glyph->getSubXFixed()), + SkFixedToScalar(glyph->getSubYFixed())); + } + SkIRect irect = rect.roundOut(); + glyph->fWidth = SkToU16(irect.width()); + glyph->fHeight = SkToU16(irect.height()); + glyph->fTop = SkToS16(irect.top()); + glyph->fLeft = SkToS16(irect.left()); + } break; default: @@ -1191,7 +1209,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { if (fRec.fFlags & SkScalerContext::kVertical_Flag) { if (fDoLinearMetrics) { const SkScalar advanceScalar = SkFT_FixedToScalar(fFace->glyph->linearVertAdvance); - glyph->fAdvanceX = -SkScalarToFloat(fMatrix22Scalar.getSkewX() * advanceScalar); + glyph->fAdvanceX = SkScalarToFloat(fMatrix22Scalar.getSkewX() * advanceScalar); glyph->fAdvanceY = SkScalarToFloat(fMatrix22Scalar.getScaleY() * advanceScalar); } else { glyph->fAdvanceX = -SkFDot6ToFloat(fFace->glyph->advance.x); @@ -1201,7 +1219,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { if (fDoLinearMetrics) { const SkScalar advanceScalar = SkFT_FixedToScalar(fFace->glyph->linearHoriAdvance); glyph->fAdvanceX = SkScalarToFloat(fMatrix22Scalar.getScaleX() * advanceScalar); - glyph->fAdvanceY = -SkScalarToFloat(fMatrix22Scalar.getSkewY() * advanceScalar); + glyph->fAdvanceY = SkScalarToFloat(fMatrix22Scalar.getSkewY() * advanceScalar); } else { glyph->fAdvanceX = SkFDot6ToFloat(fFace->glyph->advance.x); glyph->fAdvanceY = -SkFDot6ToFloat(fFace->glyph->advance.y); @@ -1213,15 +1231,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) { } } - // If the font isn't scalable, scale the metrics from the non-scalable strike. - // This means do not try to scale embedded bitmaps; only scale bitmaps in bitmap only fonts. - if (!FT_IS_SCALABLE(fFace) && !SkScalarNearlyZero(fScale.fY) && fFace->size->metrics.y_ppem) { - // NOTE: both dimensions are scaled by y_ppem. this is WAI. - scaleGlyphMetrics(*glyph, fScale.fY / fFace->size->metrics.y_ppem); - } - #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(), fLoadGlyphFlags, glyph->fWidth)); #endif } @@ -1247,7 +1257,15 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) { } emboldenIfNeeded(fFace, fFace->glyph); - generateGlyphImage(fFace, glyph); + SkMatrix* bitmapMatrix = &fMatrix22Scalar; + SkMatrix subpixelBitmapMatrix; + if (this->shouldSubpixelBitmap(glyph, *bitmapMatrix)) { + subpixelBitmapMatrix = fMatrix22Scalar; + subpixelBitmapMatrix.postTranslate(SkFixedToScalar(glyph.getSubXFixed()), + SkFixedToScalar(glyph.getSubYFixed())); + bitmapMatrix = &subpixelBitmapMatrix; + } + generateGlyphImage(fFace, glyph, *bitmapMatrix); } @@ -1303,7 +1321,7 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* metrics FT_Face face = fFace; SkScalar scaleX = fScale.x(); SkScalar scaleY = fScale.y(); - SkScalar mxy = fMatrix22Scalar.getSkewX() * scaleY; + SkScalar mxy = -fMatrix22Scalar.getSkewX() * scaleY; SkScalar myy = fMatrix22Scalar.getScaleY() * scaleY; // fetch units/EM from "head" table if needed (ie for bitmap fonts) diff --git a/src/ports/SkFontHost_FreeType_common.cpp b/src/ports/SkFontHost_FreeType_common.cpp index 245ded1283..96a7134fd8 100644 --- a/src/ports/SkFontHost_FreeType_common.cpp +++ b/src/ports/SkFontHost_FreeType_common.cpp @@ -334,7 +334,11 @@ inline SkColorType SkColorType_for_SkMaskFormat(SkMask::Format format) { } } -void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) { +void SkScalerContext_FreeType_Base::generateGlyphImage( + FT_Face face, + const SkGlyph& glyph, + const SkMatrix& bitmapTransform) +{ const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag); const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag); @@ -411,11 +415,7 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly } // If no scaling needed, directly copy glyph bitmap. - if (glyph.fWidth == face->glyph->bitmap.width && - glyph.fHeight == face->glyph->bitmap.rows && - glyph.fTop == -face->glyph->bitmap_top && - glyph.fLeft == face->glyph->bitmap_left) - { + if (bitmapTransform.isIdentity()) { SkMask dstMask; glyph.toMask(&dstMask); copyFTBitmap(face->glyph->bitmap, dstMask); @@ -426,6 +426,7 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly // Copy the FT_Bitmap into an SkBitmap (either A8 or ARGB) SkBitmap unscaledBitmap; + // TODO: mark this as sRGB when the blits will be sRGB. unscaledBitmap.allocPixels(SkImageInfo::Make(face->glyph->bitmap.width, face->glyph->bitmap.rows, SkColorType_for_FTPixelMode(pixel_mode), @@ -447,6 +448,7 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly bitmapRowBytes = glyph.rowBytes(); } SkBitmap dstBitmap; + // TODO: mark this as sRGB when the blits will be sRGB. dstBitmap.setInfo(SkImageInfo::Make(glyph.fWidth, glyph.fHeight, SkColorType_for_SkMaskFormat(maskFormat), kPremul_SkAlphaType), @@ -459,9 +461,15 @@ void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGly // Scale unscaledBitmap into dstBitmap. SkCanvas canvas(dstBitmap); +#ifdef SK_SHOW_TEXT_BLIT_COVERAGE + canvas.clear(0x33FF0000); +#else canvas.clear(SK_ColorTRANSPARENT); - canvas.scale(SkIntToScalar(glyph.fWidth) / SkIntToScalar(face->glyph->bitmap.width), - SkIntToScalar(glyph.fHeight) / SkIntToScalar(face->glyph->bitmap.rows)); +#endif + canvas.translate(-glyph.fLeft, -glyph.fTop); + canvas.concat(bitmapTransform); + canvas.translate(face->glyph->bitmap_left, -face->glyph->bitmap_top); + SkPaint paint; paint.setFilterQuality(kMedium_SkFilterQuality); canvas.drawBitmap(unscaledBitmap, 0, 0, &paint); diff --git a/src/ports/SkFontHost_FreeType_common.h b/src/ports/SkFontHost_FreeType_common.h index 3801453e22..6b50af27ab 100644 --- a/src/ports/SkFontHost_FreeType_common.h +++ b/src/ports/SkFontHost_FreeType_common.h @@ -31,9 +31,8 @@ protected: : INHERITED(typeface, effects, desc) {} - void generateGlyphImage(FT_Face face, const SkGlyph& glyph); + void generateGlyphImage(FT_Face face, const SkGlyph& glyph, const SkMatrix& bitmapTransform); void generateGlyphPath(FT_Face face, SkPath* path); - private: typedef SkScalerContext INHERITED; }; -- cgit v1.2.3