summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/ports/SkFontHost_cairo.cpp')
-rw-r--r--gfx/skia/skia/src/ports/SkFontHost_cairo.cpp681
1 files changed, 681 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
new file mode 100644
index 0000000000..f276117c46
--- /dev/null
+++ b/gfx/skia/skia/src/ports/SkFontHost_cairo.cpp
@@ -0,0 +1,681 @@
+
+/*
+ * Copyright 2012 Mozilla Foundation
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/ports/SkFontHost_FreeType_common.h"
+
+#include "src/core/SkAdvancedTypefaceMetrics.h"
+#include "src/core/SkFDot6.h"
+#include "include/core/SkFontMetrics.h"
+#include "include/core/SkPath.h"
+#include "src/core/SkScalerContext.h"
+#include "src/core/SkTypefaceCache.h"
+
+#include <cmath>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+
+// for FT_GlyphSlot_Embolden
+#ifdef FT_SYNTHESIS_H
+#include FT_SYNTHESIS_H
+#endif
+
+// for FT_Library_SetLcdFilter
+#ifdef FT_LCD_FILTER_H
+#include FT_LCD_FILTER_H
+#else
+typedef enum FT_LcdFilter_
+{
+ FT_LCD_FILTER_NONE = 0,
+ FT_LCD_FILTER_DEFAULT = 1,
+ FT_LCD_FILTER_LIGHT = 2,
+ FT_LCD_FILTER_LEGACY = 16,
+} FT_LcdFilter;
+#endif
+
+// If compiling with FreeType before 2.5.0
+#ifndef FT_LOAD_COLOR
+# define FT_LOAD_COLOR ( 1L << 20 )
+# define FT_PIXEL_MODE_BGRA 7
+#endif
+
+// If compiling with FreeType before 2.12.0
+#ifndef FT_FACE_FLAG_SVG
+// We need the format tag so that we can switch on it and handle a possibly-
+// newer version of the library at runtime.
+static constexpr FT_UInt32 FT_IMAGE_TAG(FT_GLYPH_FORMAT_SVG, 'S', 'V', 'G', ' ');
+#endif
+
+#ifndef SK_CAN_USE_DLOPEN
+#define SK_CAN_USE_DLOPEN 1
+#endif
+#if SK_CAN_USE_DLOPEN
+#include <dlfcn.h>
+#endif
+
+#ifndef SK_FONTHOST_CAIRO_STANDALONE
+#define SK_FONTHOST_CAIRO_STANDALONE 1
+#endif
+
+static bool gFontHintingEnabled = true;
+static FT_Error (*gSetLcdFilter)(FT_Library, FT_LcdFilter) = nullptr;
+
+extern "C"
+{
+ void mozilla_LockFTLibrary(FT_Library aLibrary);
+ void mozilla_UnlockFTLibrary(FT_Library aLibrary);
+ void mozilla_AddRefSharedFTFace(void* aContext);
+ void mozilla_ReleaseSharedFTFace(void* aContext, void* aOwner);
+ void mozilla_ForgetSharedFTFaceLockOwner(void* aContext, void* aOwner);
+ int mozilla_LockSharedFTFace(void* aContext, void* aOwner);
+ void mozilla_UnlockSharedFTFace(void* aContext);
+ FT_Error mozilla_LoadFTGlyph(FT_Face aFace, uint32_t aGlyphIndex, int32_t aFlags);
+ void mozilla_glyphslot_embolden_less(FT_GlyphSlot slot);
+}
+
+void SkInitCairoFT(bool fontHintingEnabled)
+{
+ gFontHintingEnabled = fontHintingEnabled;
+#if SK_CAN_USE_DLOPEN
+ gSetLcdFilter = (FT_Error (*)(FT_Library, FT_LcdFilter))dlsym(RTLD_DEFAULT, "FT_Library_SetLcdFilter");
+#else
+ gSetLcdFilter = &FT_Library_SetLcdFilter;
+#endif
+ // FT_Library_SetLcdFilter may be provided but have no effect if FreeType
+ // is built without FT_CONFIG_OPTION_SUBPIXEL_RENDERING.
+ if (gSetLcdFilter &&
+ gSetLcdFilter(nullptr, FT_LCD_FILTER_NONE) == FT_Err_Unimplemented_Feature) {
+ gSetLcdFilter = nullptr;
+ }
+}
+
+class SkScalerContext_CairoFT : public SkScalerContext_FreeType_Base {
+public:
+ SkScalerContext_CairoFT(sk_sp<SkTypeface> typeface,
+ const SkScalerContextEffects& effects,
+ const SkDescriptor* desc, FT_Face face,
+ void* faceContext, SkPixelGeometry pixelGeometry,
+ FT_LcdFilter lcdFilter);
+
+ virtual ~SkScalerContext_CairoFT() {
+ mozilla_ForgetSharedFTFaceLockOwner(fFTFaceContext, this);
+ }
+
+ bool isValid() const { return fFTFaceContext != nullptr; }
+
+ void Lock() {
+ if (!mozilla_LockSharedFTFace(fFTFaceContext, this)) {
+ FT_Set_Transform(fFTFace, fHaveShape ? &fShapeMatrixFT : nullptr, nullptr);
+ FT_Set_Char_Size(fFTFace, FT_F26Dot6(fScaleX * 64.0f + 0.5f),
+ FT_F26Dot6(fScaleY * 64.0f + 0.5f), 0, 0);
+ }
+ }
+
+ void Unlock() { mozilla_UnlockSharedFTFace(fFTFaceContext); }
+
+protected:
+ bool generateAdvance(SkGlyph* glyph) override;
+ void generateMetrics(SkGlyph* glyph, SkArenaAlloc* arena) override;
+ void generateImage(const SkGlyph& glyph) override;
+ bool generatePath(const SkGlyph& glyph, SkPath* path) override;
+ void generateFontMetrics(SkFontMetrics* metrics) override;
+
+private:
+ bool computeShapeMatrix(const SkMatrix& m);
+ void prepareGlyph(FT_GlyphSlot glyph);
+
+ FT_Face fFTFace;
+ void* fFTFaceContext;
+ FT_Int32 fLoadGlyphFlags;
+ FT_LcdFilter fLcdFilter;
+ SkScalar fScaleX;
+ SkScalar fScaleY;
+ SkMatrix fShapeMatrix;
+ FT_Matrix fShapeMatrixFT;
+ bool fHaveShape;
+};
+
+class AutoLockFTFace {
+public:
+ AutoLockFTFace(SkScalerContext_CairoFT* scalerContext)
+ : fScalerContext(scalerContext) {
+ fScalerContext->Lock();
+ }
+
+ ~AutoLockFTFace() { fScalerContext->Unlock(); }
+
+private:
+ SkScalerContext_CairoFT* fScalerContext;
+};
+
+static bool isLCD(const SkScalerContextRec& rec) {
+ return SkMask::kLCD16_Format == rec.fMaskFormat;
+}
+
+static bool bothZero(SkScalar a, SkScalar b) {
+ return 0 == a && 0 == b;
+}
+
+// returns false if there is any non-90-rotation or skew
+static bool isAxisAligned(const SkScalerContextRec& rec) {
+ return 0 == rec.fPreSkewX &&
+ (bothZero(rec.fPost2x2[0][1], rec.fPost2x2[1][0]) ||
+ bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
+}
+
+class SkCairoFTTypeface : public SkTypeface {
+public:
+ std::unique_ptr<SkStreamAsset> onOpenStream(int*) const override { return nullptr; }
+
+ std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override
+ {
+ SkDEBUGCODE(SkDebugf("SkCairoFTTypeface::onGetAdvancedMetrics unimplemented\n"));
+ return nullptr;
+ }
+
+ std::unique_ptr<SkScalerContext> onCreateScalerContext(const SkScalerContextEffects& effects, const SkDescriptor* desc) const override
+ {
+ SkScalerContext_CairoFT* ctx = new SkScalerContext_CairoFT(
+ sk_ref_sp(const_cast<SkCairoFTTypeface*>(this)), effects, desc,
+ fFTFace, fFTFaceContext, fPixelGeometry, fLcdFilter);
+ std::unique_ptr<SkScalerContext> result(ctx);
+ if (!ctx->isValid()) {
+ return nullptr;
+ }
+ return result;
+ }
+
+ void onFilterRec(SkScalerContextRec* rec) const override
+ {
+ // rotated text looks bad with hinting, so we disable it as needed
+ if (!gFontHintingEnabled || !isAxisAligned(*rec)) {
+ rec->setHinting(SkFontHinting::kNone);
+ }
+
+ // Don't apply any gamma so that we match cairo-ft's results.
+ rec->ignorePreBlend();
+ }
+
+ void onGetFontDescriptor(SkFontDescriptor*, bool*) const override
+ {
+ SkDEBUGCODE(SkDebugf("SkCairoFTTypeface::onGetFontDescriptor unimplemented\n"));
+ }
+
+ void onCharsToGlyphs(const SkUnichar* chars, int count, SkGlyphID glyphs[]) const override
+ {
+ mozilla_LockSharedFTFace(fFTFaceContext, nullptr);
+ for (int i = 0; i < count; ++i) {
+ glyphs[i] = SkToU16(FT_Get_Char_Index(fFTFace, chars[i]));
+ }
+ mozilla_UnlockSharedFTFace(fFTFaceContext);
+ }
+
+ int onCountGlyphs() const override
+ {
+ return fFTFace->num_glyphs;
+ }
+
+ int onGetUPEM() const override
+ {
+ return 0;
+ }
+
+ SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override
+ {
+ return nullptr;
+ }
+
+ void onGetFamilyName(SkString* familyName) const override
+ {
+ familyName->reset();
+ }
+
+ bool onGetPostScriptName(SkString*) const override {
+ return false;
+ }
+
+ bool onGlyphMaskNeedsCurrentColor() const override {
+ return false;
+ }
+
+ int onGetTableTags(SkFontTableTag*) const override
+ {
+ return 0;
+ }
+
+ size_t onGetTableData(SkFontTableTag, size_t, size_t, void*) const override
+ {
+ return 0;
+ }
+
+ void getPostScriptGlyphNames(SkString*) const override {}
+
+ void getGlyphToUnicodeMap(SkUnichar*) const override {}
+
+ int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
+ int coordinateCount) const override
+ {
+ return 0;
+ }
+
+ int onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
+ int parameterCount) const override
+ {
+ return 0;
+ }
+
+ sk_sp<SkTypeface> onMakeClone(const SkFontArguments& args) const override {
+ return sk_ref_sp(this);
+ }
+
+ SkCairoFTTypeface(FT_Face face, void* faceContext,
+ SkPixelGeometry pixelGeometry, FT_LcdFilter lcdFilter)
+ : SkTypeface(SkFontStyle::Normal())
+ , fFTFace(face)
+ , fFTFaceContext(faceContext)
+ , fPixelGeometry(pixelGeometry)
+ , fLcdFilter(lcdFilter)
+ {
+ mozilla_AddRefSharedFTFace(fFTFaceContext);
+ }
+
+ void* GetFTFaceContext() const { return fFTFaceContext; }
+
+ bool hasColorGlyphs() const override
+ {
+ // Check if the font has scalable outlines. If not, then avoid trying
+ // to render it as a path.
+ if (fFTFace) {
+ return !FT_IS_SCALABLE(fFTFace);
+ }
+ return false;
+ }
+
+private:
+ ~SkCairoFTTypeface()
+ {
+ mozilla_ReleaseSharedFTFace(fFTFaceContext, nullptr);
+ }
+
+ FT_Face fFTFace;
+ void* fFTFaceContext;
+ SkPixelGeometry fPixelGeometry;
+ FT_LcdFilter fLcdFilter;
+};
+
+static bool FindByFTFaceContext(SkTypeface* typeface, void* context) {
+ return static_cast<SkCairoFTTypeface*>(typeface)->GetFTFaceContext() == context;
+}
+
+SkTypeface* SkCreateTypefaceFromCairoFTFont(FT_Face face, void* faceContext,
+ SkPixelGeometry pixelGeometry,
+ uint8_t lcdFilter)
+{
+ sk_sp<SkTypeface> typeface =
+ SkTypefaceCache::FindByProcAndRef(FindByFTFaceContext, faceContext);
+ if (!typeface) {
+ typeface = sk_make_sp<SkCairoFTTypeface>(face, faceContext, pixelGeometry,
+ (FT_LcdFilter)lcdFilter);
+ SkTypefaceCache::Add(typeface);
+ }
+
+ return typeface.release();
+}
+
+SkScalerContext_CairoFT::SkScalerContext_CairoFT(
+ sk_sp<SkTypeface> typeface, const SkScalerContextEffects& effects,
+ const SkDescriptor* desc, FT_Face face, void* faceContext,
+ SkPixelGeometry pixelGeometry, FT_LcdFilter lcdFilter)
+ : SkScalerContext_FreeType_Base(std::move(typeface), effects, desc)
+ , fFTFace(face)
+ , fFTFaceContext(faceContext)
+ , fLcdFilter(lcdFilter)
+{
+ SkMatrix matrix;
+ fRec.getSingleMatrix(&matrix);
+
+ computeShapeMatrix(matrix);
+
+ FT_Int32 loadFlags = FT_LOAD_DEFAULT;
+
+ if (SkMask::kBW_Format == fRec.fMaskFormat) {
+ if (fRec.getHinting() == SkFontHinting::kNone) {
+ loadFlags |= FT_LOAD_NO_HINTING;
+ } else {
+ loadFlags = FT_LOAD_TARGET_MONO;
+ }
+ loadFlags |= FT_LOAD_MONOCHROME;
+ } else {
+ if (isLCD(fRec)) {
+ switch (pixelGeometry) {
+ case kRGB_H_SkPixelGeometry:
+ default:
+ break;
+ case kRGB_V_SkPixelGeometry:
+ fRec.fFlags |= SkScalerContext::kLCD_Vertical_Flag;
+ break;
+ case kBGR_H_SkPixelGeometry:
+ fRec.fFlags |= SkScalerContext::kLCD_BGROrder_Flag;
+ break;
+ case kBGR_V_SkPixelGeometry:
+ fRec.fFlags |= SkScalerContext::kLCD_Vertical_Flag |
+ SkScalerContext::kLCD_BGROrder_Flag;
+ break;
+ }
+ }
+
+ switch (fRec.getHinting()) {
+ case SkFontHinting::kNone:
+ loadFlags |= FT_LOAD_NO_HINTING;
+ break;
+ case SkFontHinting::kSlight:
+ loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT
+ break;
+ case SkFontHinting::kNormal:
+ if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) {
+ loadFlags |= FT_LOAD_FORCE_AUTOHINT;
+ }
+ break;
+ case SkFontHinting::kFull:
+ 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 when asked to disable hinting, except for "tricky" fonts.
+ if (!gFontHintingEnabled) {
+ if (fFTFace && !(fFTFace->face_flags & FT_FACE_FLAG_TRICKY)) {
+ 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;
+
+ loadFlags |= FT_LOAD_COLOR;
+
+ fLoadGlyphFlags = loadFlags;
+}
+
+bool SkScalerContext_CairoFT::computeShapeMatrix(const SkMatrix& m)
+{
+ // Compute a shape matrix compatible with Cairo's _compute_transform.
+ // Finds major/minor scales and uses them to normalize the transform.
+ double scaleX = m.getScaleX();
+ double skewX = m.getSkewX();
+ double skewY = m.getSkewY();
+ double scaleY = m.getScaleY();
+ double det = scaleX * scaleY - skewY * skewX;
+ if (!std::isfinite(det)) {
+ fScaleX = fRec.fTextSize * fRec.fPreScaleX;
+ fScaleY = fRec.fTextSize;
+ fHaveShape = false;
+ return false;
+ }
+ double major = det != 0.0 ? hypot(scaleX, skewY) : 0.0;
+ double minor = major != 0.0 ? fabs(det) / major : 0.0;
+ // Limit scales to be above 1pt.
+ major = std::max(major, 1.0);
+ minor = std::max(minor, 1.0);
+
+ // If the font is not scalable, then choose the best available size.
+ if (fFTFace && !FT_IS_SCALABLE(fFTFace)) {
+ double bestDist = DBL_MAX;
+ FT_Int bestSize = -1;
+ for (FT_Int i = 0; i < fFTFace->num_fixed_sizes; i++) {
+ // Distance is positive if strike is larger than desired size,
+ // or negative if smaller. If previously a found smaller strike,
+ // then prefer a larger strike. Otherwise, minimize distance.
+ double dist = fFTFace->available_sizes[i].y_ppem / 64.0 - minor;
+ if (bestDist < 0 ? dist >= bestDist : fabs(dist) <= bestDist) {
+ bestDist = dist;
+ bestSize = i;
+ }
+ }
+ if (bestSize < 0) {
+ fScaleX = fRec.fTextSize * fRec.fPreScaleX;
+ fScaleY = fRec.fTextSize;
+ fHaveShape = false;
+ return false;
+ }
+ major = fFTFace->available_sizes[bestSize].x_ppem / 64.0;
+ minor = fFTFace->available_sizes[bestSize].y_ppem / 64.0;
+ fHaveShape = true;
+ } else {
+ fHaveShape = !m.isScaleTranslate() || scaleX < 0.0 || scaleY < 0.0;
+ }
+
+ fScaleX = SkDoubleToScalar(major);
+ fScaleY = SkDoubleToScalar(minor);
+
+ if (fHaveShape) {
+ // Normalize the transform and convert to fixed-point.
+ fShapeMatrix = m;
+ fShapeMatrix.preScale(SkDoubleToScalar(1.0 / major), SkDoubleToScalar(1.0 / minor));
+
+ fShapeMatrixFT.xx = SkScalarToFixed(fShapeMatrix.getScaleX());
+ fShapeMatrixFT.yx = SkScalarToFixed(-fShapeMatrix.getSkewY());
+ fShapeMatrixFT.xy = SkScalarToFixed(-fShapeMatrix.getSkewX());
+ fShapeMatrixFT.yy = SkScalarToFixed(fShapeMatrix.getScaleY());
+ }
+ return true;
+}
+
+bool SkScalerContext_CairoFT::generateAdvance(SkGlyph* glyph)
+{
+ generateMetrics(glyph, nullptr);
+ return !glyph->isEmpty();
+}
+
+void SkScalerContext_CairoFT::prepareGlyph(FT_GlyphSlot glyph)
+{
+ if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) {
+ // Not FT_GlyphSlot_Embolden because we want a less extreme effect.
+ mozilla_glyphslot_embolden_less(glyph);
+ }
+}
+
+void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph, SkArenaAlloc* arena)
+{
+ glyph->fMaskFormat = fRec.fMaskFormat;
+
+ glyph->zeroMetrics();
+
+ AutoLockFTFace faceLock(this);
+
+ FT_Error err = mozilla_LoadFTGlyph(fFTFace, glyph->getGlyphID(), fLoadGlyphFlags);
+ if (err != 0) {
+ return;
+ }
+
+ prepareGlyph(fFTFace->glyph);
+
+ glyph->fAdvanceX = SkFDot6ToFloat(fFTFace->glyph->advance.x);
+ glyph->fAdvanceY = -SkFDot6ToFloat(fFTFace->glyph->advance.y);
+
+ SkIRect bounds;
+ switch (fFTFace->glyph->format) {
+ case FT_GLYPH_FORMAT_OUTLINE:
+ if (!fFTFace->glyph->outline.n_contours) {
+ return;
+ }
+
+ FT_BBox bbox;
+ FT_Outline_Get_CBox(&fFTFace->glyph->outline, &bbox);
+ if (this->isSubpixel()) {
+ int dx = SkFixedToFDot6(glyph->getSubXFixed());
+ int dy = SkFixedToFDot6(glyph->getSubYFixed());
+ bbox.xMin += dx;
+ bbox.yMin -= dy;
+ bbox.xMax += dx;
+ bbox.yMax -= dy;
+ }
+ bbox.xMin &= ~63;
+ bbox.yMin &= ~63;
+ bbox.xMax = (bbox.xMax + 63) & ~63;
+ bbox.yMax = (bbox.yMax + 63) & ~63;
+ bounds = SkIRect::MakeLTRB(SkFDot6Floor(bbox.xMin),
+ -SkFDot6Floor(bbox.yMax),
+ SkFDot6Floor(bbox.xMax),
+ -SkFDot6Floor(bbox.yMin));
+
+ if (isLCD(fRec)) {
+ // In FreeType < 2.8.1, LCD filtering, if explicitly used, may
+ // add padding to the glyph. When not used, there is no padding.
+ // As of 2.8.1, LCD filtering is now always supported and may
+ // add padding even if an LCD filter is not explicitly set.
+ // Regardless, if no LCD filtering is used, or if LCD filtering
+ // doesn't add padding, it is safe to modify the glyph's bounds
+ // here. generateGlyphImage will detect if the mask is smaller
+ // than the bounds and clip things appropriately.
+ if (fRec.fFlags & kLCD_Vertical_Flag) {
+ bounds.outset(0, 1);
+ } else {
+ bounds.outset(1, 0);
+ }
+ }
+ break;
+ case FT_GLYPH_FORMAT_BITMAP:
+ if (fFTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
+ glyph->fMaskFormat = SkMask::kARGB32_Format;
+ }
+
+ if (isLCD(fRec)) {
+ fRec.fMaskFormat = SkMask::kA8_Format;
+ }
+
+ if (fHaveShape) {
+ // Ensure filtering is preserved when the bitmap is transformed.
+ // Otherwise, the result will look horrifically aliased.
+ if (fRec.fMaskFormat == SkMask::kBW_Format) {
+ fRec.fMaskFormat = SkMask::kA8_Format;
+ }
+
+ // Apply the shape matrix to the glyph's bounding box.
+ SkRect srcRect = SkRect::MakeXYWH(
+ SkIntToScalar(fFTFace->glyph->bitmap_left),
+ -SkIntToScalar(fFTFace->glyph->bitmap_top),
+ SkIntToScalar(fFTFace->glyph->bitmap.width),
+ SkIntToScalar(fFTFace->glyph->bitmap.rows));
+ SkRect destRect;
+ fShapeMatrix.mapRect(&destRect, srcRect);
+ SkIRect glyphRect = destRect.roundOut();
+ bounds = SkIRect::MakeXYWH(SkScalarRoundToInt(destRect.fLeft),
+ SkScalarRoundToInt(destRect.fTop),
+ glyphRect.width(),
+ glyphRect.height());
+ } else {
+ bounds = SkIRect::MakeXYWH(fFTFace->glyph->bitmap_left,
+ -fFTFace->glyph->bitmap_top,
+ fFTFace->glyph->bitmap.width,
+ fFTFace->glyph->bitmap.rows);
+ }
+ break;
+ case FT_GLYPH_FORMAT_SVG:
+ // We don't support getting glyph bounds for SVG, but at least the advance
+ // should be correctly returned, and we don't want to fire an assertion.
+ break;
+ default:
+ SkDEBUGFAIL("unknown glyph format");
+ return;
+ }
+
+ if (SkIRect::MakeXYWH(SHRT_MIN, SHRT_MIN, USHRT_MAX, USHRT_MAX).contains(bounds)) {
+ glyph->fWidth = SkToU16(bounds.width());
+ glyph->fHeight = SkToU16(bounds.height());
+ glyph->fLeft = SkToS16(bounds.left());
+ glyph->fTop = SkToS16(bounds.top());
+ }
+}
+
+void SkScalerContext_CairoFT::generateImage(const SkGlyph& glyph)
+{
+ AutoLockFTFace faceLock(this);
+
+ FT_Error err = mozilla_LoadFTGlyph(fFTFace, glyph.getGlyphID(), fLoadGlyphFlags);
+
+ if (err != 0) {
+ memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
+ return;
+ }
+
+ prepareGlyph(fFTFace->glyph);
+
+ bool useLcdFilter =
+ fFTFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE &&
+ glyph.maskFormat() == SkMask::kLCD16_Format &&
+ gSetLcdFilter;
+ if (useLcdFilter) {
+ mozilla_LockFTLibrary(fFTFace->glyph->library);
+ gSetLcdFilter(fFTFace->glyph->library, fLcdFilter);
+ }
+
+ SkMatrix matrix;
+ if (fFTFace->glyph->format == FT_GLYPH_FORMAT_BITMAP &&
+ fHaveShape) {
+ matrix = fShapeMatrix;
+ } else {
+ matrix.setIdentity();
+ }
+ generateGlyphImage(fFTFace, glyph, matrix);
+
+ if (useLcdFilter) {
+ gSetLcdFilter(fFTFace->glyph->library, FT_LCD_FILTER_NONE);
+ mozilla_UnlockFTLibrary(fFTFace->glyph->library);
+ }
+}
+
+bool SkScalerContext_CairoFT::generatePath(const SkGlyph& glyph, SkPath* path)
+{
+ AutoLockFTFace faceLock(this);
+
+ SkASSERT(path);
+
+ SkGlyphID glyphID = glyph.getGlyphID();
+
+ uint32_t flags = fLoadGlyphFlags;
+ flags |= FT_LOAD_NO_BITMAP; // ignore embedded bitmaps so we're sure to get the outline
+ flags &= ~FT_LOAD_RENDER; // don't scan convert (we just want the outline)
+
+ FT_Error err = mozilla_LoadFTGlyph(fFTFace, glyphID, flags);
+
+ if (err != 0) {
+ path->reset();
+ return false;
+ }
+
+ prepareGlyph(fFTFace->glyph);
+
+ return generateGlyphPath(fFTFace, path);
+}
+
+void SkScalerContext_CairoFT::generateFontMetrics(SkFontMetrics* metrics)
+{
+ if (metrics) {
+ memset(metrics, 0, sizeof(SkFontMetrics));
+ }
+}