void SkScalerContext_CairoFT::generateImage(const SkGlyph& glyph) { SkASSERT(fScaledFont != nullptr); CairoLockedFTFace faceLock(fScaledFont); FT_Face face = faceLock.getFace(); FT_Error err = FT_Load_Glyph(face, glyph.getGlyphID(), fLoadGlyphFlags); if (err != 0) { memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight); return; } prepareGlyph(face->glyph); bool useLcdFilter = face->glyph->format == FT_GLYPH_FORMAT_OUTLINE && isLCD(glyph) && gSetLcdFilter; if (useLcdFilter) { gSetLcdFilter(face->glyph->library, fLcdFilter); } generateGlyphImage(face, glyph); if (useLcdFilter) { gSetLcdFilter(face->glyph->library, FT_LCD_FILTER_NONE); } }
virtual void onFilterRec(SkScalerContextRec* rec) const override { // No subpixel AA unless enabled in Fontconfig. if (!fPattern && isLCD(*rec)) { rec->fMaskFormat = SkMask::kA8_Format; } // rotated text looks bad with hinting, so we disable it as needed if (!gFontHintingEnabled || !isAxisAligned(*rec)) { rec->setHinting(SkPaint::kNo_Hinting); } }
SkScalerContext_CairoFT::SkScalerContext_CairoFT(SkTypeface* typeface, const SkDescriptor* desc) : SkScalerContext_FreeType_Base(typeface, desc) { SkMatrix matrix; fRec.getSingleMatrix(&matrix); cairo_font_face_t* fontFace = static_cast<SkCairoFTTypeface*>(typeface)->getFontFace(); cairo_matrix_t fontMatrix, ctMatrix; cairo_matrix_init(&fontMatrix, matrix.getScaleX(), matrix.getSkewY(), matrix.getSkewX(), matrix.getScaleY(), 0.0, 0.0); cairo_matrix_init_scale(&ctMatrix, 1.0, 1.0); // We need to ensure that the font options match for hinting, as generateMetrics() // uses the fScaledFont which uses these font options cairo_font_options_t *fontOptions = cairo_font_options_create(); FT_Int32 loadFlags = FT_LOAD_DEFAULT; if (SkMask::kBW_Format == fRec.fMaskFormat) { // See http://code.google.com/p/chromium/issues/detail?id=43252#c24 loadFlags = FT_LOAD_TARGET_MONO; if (fRec.getHinting() == SkPaint::kNo_Hinting) { cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_NONE); loadFlags = FT_LOAD_NO_HINTING; } } else { switch (fRec.getHinting()) { case SkPaint::kNo_Hinting: loadFlags = FT_LOAD_NO_HINTING; cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_NONE); break; case SkPaint::kSlight_Hinting: loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_SLIGHT); break; case SkPaint::kNormal_Hinting: cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_MEDIUM); if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) { loadFlags = FT_LOAD_FORCE_AUTOHINT; } break; case SkPaint::kFull_Hinting: cairo_font_options_set_hint_style(fontOptions, CAIRO_HINT_STYLE_FULL); if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) { loadFlags = FT_LOAD_FORCE_AUTOHINT; } if (isLCD(fRec)) { if (SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag)) { loadFlags = FT_LOAD_TARGET_LCD_V; } else { loadFlags = FT_LOAD_TARGET_LCD; } } break; default: SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting()); break; } } fScaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctMatrix, fontOptions); cairo_font_options_destroy(fontOptions); if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) { loadFlags |= FT_LOAD_NO_BITMAP; } // Always using FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH to get correct // advances, as fontconfig and cairo do. // See http://code.google.com/p/skia/issues/detail?id=222. loadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; #ifdef FT_LOAD_COLOR loadFlags |= FT_LOAD_COLOR; #endif fLoadGlyphFlags = loadFlags; }
void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph) { SkASSERT(fScaledFont != nullptr); glyph->zeroMetrics(); CairoLockedFTFace faceLock(fScaledFont); FT_Face face = faceLock.getFace(); FT_Error err = FT_Load_Glyph( face, glyph->getGlyphID(), fLoadGlyphFlags ); if (err != 0) { return; } prepareGlyph(face->glyph); switch (face->glyph->format) { case FT_GLYPH_FORMAT_OUTLINE: if (!face->glyph->outline.n_contours) { break; } FT_BBox bbox; FT_Outline_Get_CBox(&face->glyph->outline, &bbox); bbox.xMin &= ~63; bbox.yMin &= ~63; bbox.xMax = (bbox.xMax + 63) & ~63; bbox.yMax = (bbox.yMax + 63) & ~63; 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)); if (isLCD(fRec) && gSetLcdFilter && (fLcdFilter == FT_LCD_FILTER_DEFAULT || fLcdFilter == FT_LCD_FILTER_LIGHT)) { if (fRec.fFlags & kLCD_Vertical_Flag) { glyph->fTop -= 1; glyph->fHeight += 2; } else { glyph->fLeft -= 1; glyph->fWidth += 2; } } break; case FT_GLYPH_FORMAT_BITMAP: #ifdef FT_LOAD_COLOR if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { glyph->fMaskFormat = SkMask::kARGB32_Format; } #endif if (isLCD(fRec)) { fRec.fMaskFormat = SkMask::kA8_Format; } if (fHaveShape) { // Apply the shape matrix to the glyph's bounding box. SkMatrix matrix; fRec.getSingleMatrix(&matrix); matrix.preScale(SkScalarInvert(fScaleX), SkScalarInvert(fScaleY)); SkRect srcRect = SkRect::MakeXYWH( SkIntToScalar(face->glyph->bitmap_left), -SkIntToScalar(face->glyph->bitmap_top), SkIntToScalar(face->glyph->bitmap.width), SkIntToScalar(face->glyph->bitmap.rows)); SkRect destRect; matrix.mapRect(&destRect, srcRect); SkIRect glyphRect = destRect.roundOut(); glyph->fWidth = SkToU16(glyphRect.width()); glyph->fHeight = SkToU16(glyphRect.height()); glyph->fTop = SkToS16(SkScalarRoundToInt(destRect.fTop)); glyph->fLeft = SkToS16(SkScalarRoundToInt(destRect.fLeft)); } else { glyph->fWidth = SkToU16(face->glyph->bitmap.width); glyph->fHeight = SkToU16(face->glyph->bitmap.rows); glyph->fTop = -SkToS16(face->glyph->bitmap_top); glyph->fLeft = SkToS16(face->glyph->bitmap_left); } break; default: SkDEBUGFAIL("unknown glyph format"); return; } if (fRec.fFlags & SkScalerContext::kVertical_Flag) { glyph->fAdvanceX = -SkFDot6ToFloat(face->glyph->advance.x); glyph->fAdvanceY = SkFDot6ToFloat(face->glyph->advance.y); } else { glyph->fAdvanceX = SkFDot6ToFloat(face->glyph->advance.x); glyph->fAdvanceY = -SkFDot6ToFloat(face->glyph->advance.y); } }
void SkScalerContext_CairoFT::parsePattern(FcPattern* pattern) { FcBool antialias, autohint, bitmap, embolden, hinting, vertical; if (FcPatternGetBool(pattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch && autohint) { fRec.fFlags |= SkScalerContext::kForceAutohinting_Flag; } if (FcPatternGetBool(pattern, FC_EMBEDDED_BITMAP, 0, &bitmap) == FcResultMatch && bitmap) { fRec.fFlags |= SkScalerContext::kEmbeddedBitmapText_Flag; } if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch && embolden) { fRec.fFlags |= SkScalerContext::kEmbolden_Flag; } if (FcPatternGetBool(pattern, FC_VERTICAL_LAYOUT, 0, &vertical) == FcResultMatch && vertical) { fRec.fFlags |= SkScalerContext::kVertical_Flag; } if (fRec.fMaskFormat != SkMask::kBW_Format && (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &antialias) != FcResultMatch || antialias)) { int rgba; if (!isLCD(fRec) || FcPatternGetInteger(pattern, FC_RGBA, 0, &rgba) != FcResultMatch) { rgba = FC_RGBA_UNKNOWN; } switch (rgba) { case FC_RGBA_RGB: break; case FC_RGBA_BGR: fRec.fFlags |= SkScalerContext::kLCD_BGROrder_Flag; break; case FC_RGBA_VRGB: fRec.fFlags |= SkScalerContext::kLCD_Vertical_Flag; break; case FC_RGBA_VBGR: fRec.fFlags |= SkScalerContext::kLCD_Vertical_Flag | SkScalerContext::kLCD_BGROrder_Flag; break; default: fRec.fMaskFormat = SkMask::kA8_Format; break; } int filter; if (isLCD(fRec)) { if (FcPatternGetInteger(pattern, FC_LCD_FILTER, 0, &filter) != FcResultMatch) { filter = FC_LCD_LEGACY; } switch (filter) { case FC_LCD_NONE: fLcdFilter = FT_LCD_FILTER_NONE; break; case FC_LCD_DEFAULT: fLcdFilter = FT_LCD_FILTER_DEFAULT; break; case FC_LCD_LIGHT: fLcdFilter = FT_LCD_FILTER_LIGHT; break; case FC_LCD_LEGACY: default: fLcdFilter = FT_LCD_FILTER_LEGACY; break; } } } else { fRec.fMaskFormat = SkMask::kBW_Format; } if (fRec.getHinting() != SkPaint::kNo_Hinting && (FcPatternGetBool(pattern, FC_HINTING, 0, &hinting) != FcResultMatch || hinting)) { int hintstyle; if (FcPatternGetInteger(pattern, FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) { hintstyle = FC_HINT_FULL; } switch (hintstyle) { case FC_HINT_NONE: fRec.setHinting(SkPaint::kNo_Hinting); break; case FC_HINT_SLIGHT: fRec.setHinting(SkPaint::kSlight_Hinting); break; case FC_HINT_MEDIUM: default: fRec.setHinting(SkPaint::kNormal_Hinting); break; case FC_HINT_FULL: fRec.setHinting(SkPaint::kFull_Hinting); break; } } }
SkScalerContext_CairoFT::SkScalerContext_CairoFT(SkTypeface* typeface, const SkDescriptor* desc, cairo_font_face_t* fontFace, FcPattern* pattern) : SkScalerContext_FreeType_Base(typeface, desc) , fLcdFilter(FT_LCD_FILTER_NONE) { SkMatrix matrix; fRec.getSingleMatrix(&matrix); cairo_matrix_t fontMatrix, ctMatrix; cairo_matrix_init(&fontMatrix, matrix.getScaleX(), matrix.getSkewY(), matrix.getSkewX(), matrix.getScaleY(), 0.0, 0.0); cairo_matrix_init_identity(&ctMatrix); cairo_font_options_t *fontOptions = cairo_font_options_create(); fScaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctMatrix, fontOptions); cairo_font_options_destroy(fontOptions); computeShapeMatrix(matrix); #ifdef CAIRO_HAS_FC_FONT resolvePattern(pattern); #endif FT_Int32 loadFlags = FT_LOAD_DEFAULT; if (SkMask::kBW_Format == fRec.fMaskFormat) { if (fRec.getHinting() == SkPaint::kNo_Hinting) { loadFlags |= FT_LOAD_NO_HINTING; } else { loadFlags = FT_LOAD_TARGET_MONO; } loadFlags |= FT_LOAD_MONOCHROME; } else { switch (fRec.getHinting()) { case SkPaint::kNo_Hinting: loadFlags |= FT_LOAD_NO_HINTING; break; case SkPaint::kSlight_Hinting: loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT break; case SkPaint::kNormal_Hinting: if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) { loadFlags |= FT_LOAD_FORCE_AUTOHINT; } break; case SkPaint::kFull_Hinting: if (isLCD(fRec)) { if (fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag) { loadFlags = FT_LOAD_TARGET_LCD_V; } else { loadFlags = FT_LOAD_TARGET_LCD; } } if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) { loadFlags |= FT_LOAD_FORCE_AUTOHINT; } break; default: SkDebugf("---------- UNKNOWN hinting %d\n", fRec.getHinting()); break; } } // Disable autohinting to disable hinting even for "tricky" fonts. if (!gFontHintingEnabled) { loadFlags |= FT_LOAD_NO_AUTOHINT; } if ((fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag) == 0) { loadFlags |= FT_LOAD_NO_BITMAP; } // Always using FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH to get correct // advances, as fontconfig and cairo do. // See http://code.google.com/p/skia/issues/detail?id=222. loadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; if (fRec.fFlags & SkScalerContext::kVertical_Flag) { loadFlags |= FT_LOAD_VERTICAL_LAYOUT; } #ifdef FT_LOAD_COLOR loadFlags |= FT_LOAD_COLOR; #endif fLoadGlyphFlags = loadFlags; }