summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/skia/src/ports/SkFontHost_FreeType.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/skia/src/ports/SkFontHost_FreeType.cpp')
-rw-r--r--chromium/third_party/skia/src/ports/SkFontHost_FreeType.cpp287
1 files changed, 199 insertions, 88 deletions
diff --git a/chromium/third_party/skia/src/ports/SkFontHost_FreeType.cpp b/chromium/third_party/skia/src/ports/SkFontHost_FreeType.cpp
index 745a58a9963..12edc49d9e8 100644
--- a/chromium/third_party/skia/src/ports/SkFontHost_FreeType.cpp
+++ b/chromium/third_party/skia/src/ports/SkFontHost_FreeType.cpp
@@ -6,6 +6,7 @@
* found in the LICENSE file.
*/
+#include "SkAdvancedTypefaceMetrics.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkColorPriv.h"
@@ -17,8 +18,9 @@
#include "SkGlyph.h"
#include "SkMask.h"
#include "SkMaskGamma.h"
+#include "SkMatrix22.h"
#include "SkOTUtils.h"
-#include "SkAdvancedTypefaceMetrics.h"
+#include "SkOnce.h"
#include "SkScalerContext.h"
#include "SkStream.h"
#include "SkString.h"
@@ -42,6 +44,8 @@
#include FT_LCD_FILTER_H
#endif
+// Defined in FreeType 2.3.8 and later.
+// This is a silly build time check, we would need a runtime check if we really cared.
#ifdef FT_ADVANCES_H
#include FT_ADVANCES_H
#endif
@@ -160,17 +164,22 @@ static bool InitFreetype() {
return true;
}
-// Lazy, once, wrapper to ask the FreeType Library if it can support LCD text
-static bool is_lcd_supported() {
+// Called while holding gFTMutex.
+static void determine_lcd_support(bool* lcdSupported) {
if (!gLCDSupportValid) {
- SkAutoMutexAcquire ac(gFTMutex);
-
- if (!gLCDSupportValid) {
- InitFreetype();
- FT_Done_FreeType(gFTLibrary);
- }
+ // This will determine LCD support as a side effect.
+ InitFreetype();
+ FT_Done_FreeType(gFTLibrary);
}
- return gLCDSupport;
+ SkASSERT(gLCDSupportValid);
+ *lcdSupported = gLCDSupport;
+}
+
+// Lazy, once, wrapper to ask the FreeType Library if it can support LCD text
+static bool is_lcd_supported() {
+ static bool lcdSupported = false;
+ SkOnce(&gLCDSupportValid, &gFTMutex, determine_lcd_support, &lcdSupported);
+ return lcdSupported;
}
class SkScalerContext_FreeType : public SkScalerContext_FreeType_Base {
@@ -213,8 +222,12 @@ private:
FT_Error setupSize();
void getBBoxForCurrentGlyph(SkGlyph* glyph, FT_BBox* bbox,
bool snapToPixelBoundary = false);
+ bool getCBoxForLetter(char letter, FT_BBox* bbox);
// Caller must lock gFTMutex before calling this function.
void updateGlyphIfLCD(SkGlyph* glyph);
+ // Caller must lock gFTMutex before calling this function.
+ // update FreeType2 glyph slot with glyph emboldened
+ void emboldenIfNeeded(FT_Face face, FT_GlyphSlot glyph);
};
///////////////////////////////////////////////////////////////////////////
@@ -427,11 +440,26 @@ static bool canEmbed(FT_Face face) {
#endif
}
+static bool canSubset(FT_Face face) {
+#ifdef FT_FSTYPE_NO_SUBSETTING
+ FT_UShort fsType = FT_Get_FSType_Flags(face);
+ return (fsType & FT_FSTYPE_NO_SUBSETTING) == 0;
+#else
+ // No subset is 0x100.
+ TT_OS2* os2_table;
+ if ((os2_table = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) {
+ return (os2_table->fsType & 0x100) == 0;
+ }
+ return false; // We tried, fail safe.
+#endif
+}
+
static bool GetLetterCBox(FT_Face face, char letter, FT_BBox* bbox) {
const FT_UInt glyph_id = FT_Get_Char_Index(face, letter);
if (!glyph_id)
return false;
- FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_SCALE);
+ if (FT_Load_Glyph(face, glyph_id, FT_LOAD_NO_SCALE) != 0)
+ return false;
FT_Outline_Get_CBox(&face->glyph->outline, bbox);
return true;
}
@@ -511,7 +539,21 @@ SkAdvancedTypefaceMetrics* SkTypeface_FreeType::onGetAdvancedTypefaceMetrics(
SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
info->fFontName.set(FT_Get_Postscript_Name(face));
- info->fMultiMaster = FT_HAS_MULTIPLE_MASTERS(face);
+ info->fFlags = SkAdvancedTypefaceMetrics::kEmpty_FontFlag;
+ if (FT_HAS_MULTIPLE_MASTERS(face)) {
+ info->fFlags = SkTBitOr<SkAdvancedTypefaceMetrics::FontFlags>(
+ info->fFlags, SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag);
+ }
+ if (!canEmbed(face)) {
+ info->fFlags = SkTBitOr<SkAdvancedTypefaceMetrics::FontFlags>(
+ info->fFlags,
+ SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag);
+ }
+ if (!canSubset(face)) {
+ info->fFlags = SkTBitOr<SkAdvancedTypefaceMetrics::FontFlags>(
+ info->fFlags,
+ SkAdvancedTypefaceMetrics::kNotSubsettable_FontFlag);
+ }
info->fLastGlyphID = face->num_glyphs - 1;
info->fEmSize = 1000;
@@ -582,8 +624,10 @@ SkAdvancedTypefaceMetrics* SkTypeface_FreeType::onGetAdvancedTypefaceMetrics(
info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
else if (serif_style >= 9 && serif_style <= 12)
info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
- } else if ((os2_table =
- (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) {
+ } else if (((os2_table = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2)) != NULL) &&
+ // sCapHeight is available only when version 2 or later.
+ os2_table->version != 0xFFFF &&
+ os2_table->version >= 2) {
info->fCapHeight = os2_table->sCapHeight;
} else {
// Figure out a good guess for CapHeight: average the height of M and X.
@@ -598,14 +642,16 @@ SkAdvancedTypefaceMetrics* SkTypeface_FreeType::onGetAdvancedTypefaceMetrics(
info->fCapHeight = m_bbox.yMax - m_bbox.yMin;
} else if (!got_m && got_x) {
info->fCapHeight = x_bbox.yMax - x_bbox.yMin;
+ } else {
+ // Last resort, use the ascent.
+ info->fCapHeight = info->fAscent;
}
}
info->fBBox = SkIRect::MakeLTRB(face->bbox.xMin, face->bbox.yMax,
face->bbox.xMax, face->bbox.yMin);
- if (!canEmbed(face) || !FT_IS_SCALABLE(face) ||
- info->fType == SkAdvancedTypefaceMetrics::kOther_Font) {
+ if (!FT_IS_SCALABLE(face)) {
perGlyphInfo = SkAdvancedTypefaceMetrics::kNo_PerGlyphInfo;
}
@@ -668,18 +714,12 @@ SkAdvancedTypefaceMetrics* SkTypeface_FreeType::onGetAdvancedTypefaceMetrics(
populate_glyph_to_unicode(face, &(info->fGlyphToUnicode));
}
- if (!canEmbed(face))
- info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
-
return info;
#endif
}
///////////////////////////////////////////////////////////////////////////
-#define BLACK_LUMINANCE_LIMIT 0x40
-#define WHITE_LUMINANCE_LIMIT 0xA0
-
static bool bothZero(SkScalar a, SkScalar b) {
return 0 == a && 0 == b;
}
@@ -833,53 +873,60 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface,
}
fFace = fFaceRec->fFace;
- // compute our factors from the record
-
- SkMatrix m;
-
- fRec.getSingleMatrix(&m);
-
-#ifdef DUMP_STRIKE_CREATION
- SkString keyString;
- SkFontHost::GetDescriptorKeyString(desc, &keyString);
- printf("========== strike [%g %g %g] [%g %g %g %g] hints %d format %d %s\n", SkScalarToFloat(fRec.fTextSize),
- SkScalarToFloat(fRec.fPreScaleX), SkScalarToFloat(fRec.fPreSkewX),
- SkScalarToFloat(fRec.fPost2x2[0][0]), SkScalarToFloat(fRec.fPost2x2[0][1]),
- SkScalarToFloat(fRec.fPost2x2[1][0]), SkScalarToFloat(fRec.fPost2x2[1][1]),
- fRec.getHinting(), fRec.fMaskFormat, keyString.c_str());
-#endif
-
- // now compute our scale factors
- SkScalar sx = m.getScaleX();
- SkScalar sy = m.getScaleY();
+ // A is the total matrix.
+ SkMatrix A;
+ fRec.getSingleMatrix(&A);
+ SkScalar sx = A.getScaleX();
+ SkScalar sy = A.getScaleY();
fMatrix22Scalar.reset();
- if (m.getSkewX() || m.getSkewY() || sx < 0 || sy < 0) {
- // sort of give up on hinting
- sx = SkMaxScalar(SkScalarAbs(sx), SkScalarAbs(m.getSkewX()));
- sy = SkMaxScalar(SkScalarAbs(m.getSkewY()), SkScalarAbs(sy));
- sx = sy = SkScalarAve(sx, sy);
-
- SkScalar inv = SkScalarInvert(sx);
-
- // flip the skew elements to go from our Y-down system to FreeType's
- fMatrix22.xx = SkScalarToFixed(SkScalarMul(m.getScaleX(), inv));
- fMatrix22.xy = -SkScalarToFixed(SkScalarMul(m.getSkewX(), inv));
- fMatrix22.yx = -SkScalarToFixed(SkScalarMul(m.getSkewY(), inv));
- fMatrix22.yy = SkScalarToFixed(SkScalarMul(m.getScaleY(), inv));
-
- fMatrix22Scalar.setScaleX(SkScalarMul(m.getScaleX(), inv));
- fMatrix22Scalar.setSkewX(-SkScalarMul(m.getSkewX(), inv));
- fMatrix22Scalar.setSkewY(-SkScalarMul(m.getSkewY(), inv));
- fMatrix22Scalar.setScaleY(SkScalarMul(m.getScaleY(), inv));
- } else {
- fMatrix22.xx = fMatrix22.yy = SK_Fixed1;
- fMatrix22.xy = fMatrix22.yx = 0;
+ // In GDI, the hinter is aware of the current transformation
+ // (the transform is in some sense applied before/with the hinting).
+ // The bytecode can then test if it is rotated or stretched and decide
+ // to apply instructions or not.
+ //
+ // FreeType, however, always does the transformation strictly after hinting.
+ // It just sets 'rotated' and 'stretched' to false and only applies the
+ // size before hinting.
+ //
+ // Also, FreeType respects the head::flags::IntegerScaling flag,
+ // (although this is patched out on most major distros)
+ // so it is critical to get the size correct on the request.
+ //
+ // This also gets us the actual closest size on bitmap fonts as well.
+ if (A.getSkewX() || A.getSkewY() || sx < 0 || sy < 0) {
+ // h is where A maps the horizontal baseline.
+ SkPoint h = SkPoint::Make(SK_Scalar1, 0);
+ A.mapPoints(&h, 1);
+
+ // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
+ SkMatrix G;
+ SkComputeGivensRotation(h, &G);
+
+ // GA is the matrix A with rotation removed.
+ SkMatrix GA(G);
+ GA.preConcat(A);
+
+ sx = SkScalarAbs(GA.get(SkMatrix::kMScaleX));
+ sy = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
+
+ // sA is the total matrix A without the text scale.
+ SkMatrix sA(A);
+ sA.preScale(SkScalarInvert(sx), SkScalarInvert(sy)); //remove text size
+
+ fMatrix22Scalar.setScaleX(sA.getScaleX());
+ fMatrix22Scalar.setSkewX(-sA.getSkewX());
+ fMatrix22Scalar.setSkewY(-sA.getSkewY());
+ fMatrix22Scalar.setScaleY(sA.getScaleY());
}
fScale.set(sx, sy);
fScaleX = SkScalarToFixed(sx);
fScaleY = SkScalarToFixed(sy);
+ fMatrix22.xx = SkScalarToFixed(fMatrix22Scalar.getScaleX());
+ fMatrix22.xy = SkScalarToFixed(fMatrix22Scalar.getSkewX());
+ fMatrix22.yx = SkScalarToFixed(fMatrix22Scalar.getSkewY());
+ fMatrix22.yy = SkScalarToFixed(fMatrix22Scalar.getScaleY());
fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
@@ -905,13 +952,12 @@ SkScalerContext_FreeType::SkScalerContext_FreeType(SkTypeface* typeface,
loadFlags = FT_LOAD_TARGET_LIGHT; // This implies FORCE_AUTOHINT
break;
case SkPaint::kNormal_Hinting:
- if (fRec.fFlags & SkScalerContext::kAutohinting_Flag)
+ if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) {
loadFlags = FT_LOAD_FORCE_AUTOHINT;
- else
- loadFlags = FT_LOAD_NO_AUTOHINT;
+ }
break;
case SkPaint::kFull_Hinting:
- if (fRec.fFlags & SkScalerContext::kAutohinting_Flag) {
+ if (fRec.fFlags & SkScalerContext::kForceAutohinting_Flag) {
loadFlags = FT_LOAD_FORCE_AUTOHINT;
break;
}
@@ -1126,6 +1172,17 @@ void SkScalerContext_FreeType::getBBoxForCurrentGlyph(SkGlyph* glyph,
}
}
+bool SkScalerContext_FreeType::getCBoxForLetter(char letter, FT_BBox* bbox) {
+ const FT_UInt glyph_id = FT_Get_Char_Index(fFace, letter);
+ if (!glyph_id)
+ return false;
+ if (FT_Load_Glyph(fFace, glyph_id, fLoadGlyphFlags) != 0)
+ return false;
+ emboldenIfNeeded(fFace, fFace->glyph);
+ FT_Outline_Get_CBox(&fFace->glyph->outline, bbox);
+ return true;
+}
+
void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) {
if (isLCD(fRec)) {
if (fLCDIsVert) {
@@ -1171,6 +1228,7 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
glyph->zeroMetrics();
return;
}
+ emboldenIfNeeded(fFace, fFace->glyph);
switch ( fFace->glyph->format ) {
case FT_GLYPH_FORMAT_OUTLINE:
@@ -1180,10 +1238,6 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
glyph->fTop = 0;
glyph->fLeft = 0;
} else {
- if (fRec.fFlags & kEmbolden_Flag && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
- emboldenOutline(fFace, &fFace->glyph->outline);
- }
-
FT_BBox bbox;
getBBoxForCurrentGlyph(glyph, &bbox, true);
@@ -1197,11 +1251,6 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
break;
case FT_GLYPH_FORMAT_BITMAP:
- if (fRec.fFlags & kEmbolden_Flag) {
- FT_GlyphSlot_Own_Bitmap(fFace->glyph);
- FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0);
- }
-
if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
FT_Vector vector;
vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX;
@@ -1280,6 +1329,7 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
return;
}
+ emboldenIfNeeded(fFace, fFace->glyph);
generateGlyphImage(fFace, glyph);
}
@@ -1307,6 +1357,7 @@ void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph,
path->reset();
return;
}
+ emboldenIfNeeded(fFace, fFace->glyph);
generateGlyphPath(fFace, path);
@@ -1358,14 +1409,19 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
// use the os/2 table as a source of reasonable defaults.
SkScalar x_height = 0.0f;
SkScalar avgCharWidth = 0.0f;
+ SkScalar cap_height = 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;
+ if (os2->version != 0xFFFF && os2->version >= 2) {
+ cap_height = scaleX * SkIntToScalar(os2->sCapHeight) / upem;
+ }
}
// pull from format-specific metrics as needed
SkScalar ascent, descent, leading, xmin, xmax, ymin, ymax;
+ SkScalar underlineThickness, underlinePosition;
if (face->face_flags & FT_FACE_FLAG_SCALABLE) { // scalable outline font
ascent = -SkIntToScalar(face->ascender) / upem;
descent = -SkIntToScalar(face->descender) / upem;
@@ -1374,19 +1430,31 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
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
+ underlineThickness = SkIntToScalar(face->underline_thickness) / upem;
+ underlinePosition = -SkIntToScalar(face->underline_position +
+ face->underline_thickness / 2) / upem;
+
+ if(mx) {
+ mx->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
+ mx->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
+ }
+ if(my){
+ my->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
+ my->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
+ }
+ // we may be able to synthesize x_height and cap_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);
+ FT_BBox bbox;
+ if (getCBoxForLetter('x', &bbox)) {
x_height = SkIntToScalar(bbox.yMax) / 64.0f;
}
}
+ if (!cap_height) {
+ FT_BBox bbox;
+ if (getCBoxForLetter('H', &bbox)) {
+ cap_height = SkIntToScalar(bbox.yMax) / 64.0f;
+ }
+ }
} else if (fStrikeIndex != -1) { // bitmap strike metrics
SkScalar xppem = SkIntToScalar(face->size->metrics.x_ppem);
SkScalar yppem = SkIntToScalar(face->size->metrics.y_ppem);
@@ -1398,11 +1466,16 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
xmax = SkIntToScalar(face->available_sizes[fStrikeIndex].width) / xppem;
ymin = descent + leading;
ymax = ascent - descent;
- if (!x_height) {
- x_height = -ascent;
+ underlineThickness = 0;
+ underlinePosition = 0;
+
+ if(mx) {
+ mx->fFlags &= ~SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
+ mx->fFlags &= ~SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
}
- if (!avgCharWidth) {
- avgCharWidth = xmax - xmin;
+ if(my){
+ my->fFlags &= ~SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
+ my->fFlags &= ~SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
}
} else {
goto ERROR;
@@ -1415,6 +1488,9 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
if (!avgCharWidth) {
avgCharWidth = xmax - xmin;
}
+ if (!cap_height) {
+ cap_height = -ascent;
+ }
// disallow negative linespacing
if (leading < 0.0f) {
@@ -1431,6 +1507,9 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
mx->fXMin = xmin;
mx->fXMax = xmax;
mx->fXHeight = x_height;
+ mx->fCapHeight = cap_height;
+ mx->fUnderlineThickness = underlineThickness * mxy;
+ mx->fUnderlinePosition = underlinePosition * mxy;
}
if (my) {
my->fTop = ymax * myy;
@@ -1442,6 +1521,38 @@ void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
my->fXMin = xmin;
my->fXMax = xmax;
my->fXHeight = x_height;
+ my->fCapHeight = cap_height;
+ my->fUnderlineThickness = underlineThickness * myy;
+ my->fUnderlinePosition = underlinePosition * myy;
+ }
+}
+
+void SkScalerContext_FreeType::emboldenIfNeeded(FT_Face face, FT_GlyphSlot glyph)
+{
+ // check to see if the embolden bit is set
+ if (0 == (fRec.fFlags & SkScalerContext::kEmbolden_Flag)) {
+ return;
+ }
+
+#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
+ // Android doesn't want to embolden a font that is already bold.
+ if ((fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
+ return;
+ }
+#endif
+
+ switch (glyph->format) {
+ case FT_GLYPH_FORMAT_OUTLINE:
+ FT_Pos strength;
+ strength = FT_MulFix(face->units_per_EM, face->size->metrics.y_scale) / 24;
+ FT_Outline_Embolden(&glyph->outline, strength);
+ break;
+ case FT_GLYPH_FORMAT_BITMAP:
+ FT_GlyphSlot_Own_Bitmap(glyph);
+ FT_Bitmap_Embolden(glyph->library, &glyph->bitmap, kBitmapEmboldenStrength, 0);
+ break;
+ default:
+ SkDEBUGFAIL("unknown glyph format");
}
}