Add basic support for vertical text into freetype.

Review URL: https://codereview.appspot.com/5794081

git-svn-id: http://skia.googlecode.com/svn/trunk@3433 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
djsollen@google.com 2012-03-19 19:44:19 +00:00
parent 0b53d59a24
commit d8b599cb26

View File

@ -107,6 +107,11 @@ static const uint8_t* gGammaTables[2];
// This value was chosen by eyeballing the result in Firefox and trying to match it.
static const FT_Pos kBitmapEmboldenStrength = 1 << 6;
// convert from Skia's fixed (16.16) to FreeType's fixed (26.6) representation
static inline int FixedToDot6(SkFixed x) { return x >> 10; }
// convert from FreeType's fixed (26.6) to Skia's fixed (16.16) representation
static inline SkFixed Dot6ToFixed(int x) { return x << 10; }
static bool
InitFreetype() {
FT_Error err = FT_Init_FreeType(&gFTLibrary);
@ -165,6 +170,9 @@ private:
FT_Error setupSize();
void emboldenOutline(FT_Outline* outline);
void getBBoxForCurrentGlyph(SkGlyph* glyph, FT_BBox* bbox,
bool snapToPixelBoundary = false);
void updateGlyphIfLCD(SkGlyph* glyph);
};
///////////////////////////////////////////////////////////////////////////
@ -959,6 +967,43 @@ void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) {
return;
}
void SkScalerContext_FreeType::getBBoxForCurrentGlyph(SkGlyph* glyph,
FT_BBox* bbox,
bool snapToPixelBoundary) {
FT_Outline_Get_CBox(&fFace->glyph->outline, bbox);
if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
int dx = FixedToDot6(glyph->getSubXFixed());
int dy = FixedToDot6(glyph->getSubYFixed());
// negate dy since freetype-y-goes-up and skia-y-goes-down
bbox->xMin += dx;
bbox->yMin -= dy;
bbox->xMax += dx;
bbox->yMax -= dy;
}
// outset the box to integral boundaries
if (snapToPixelBoundary) {
bbox->xMin &= ~63;
bbox->yMin &= ~63;
bbox->xMax = (bbox->xMax + 63) & ~63;
bbox->yMax = (bbox->yMax + 63) & ~63;
}
}
void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) {
if (isLCD(fRec)) {
if (fLCDIsVert) {
glyph->fHeight += gLCDExtra;
glyph->fTop -= gLCDExtra >> 1;
} else {
glyph->fWidth += gLCDExtra;
glyph->fLeft -= gLCDExtra >> 1;
}
}
}
void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
SkAutoMutexAcquire ac(gFTMutex);
@ -980,6 +1025,8 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
return;
}
SkFixed vLeft, vTop;
switch ( fFace->glyph->format ) {
case FT_GLYPH_FORMAT_OUTLINE: {
FT_BBox bbox;
@ -995,37 +1042,21 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
if (fRec.fFlags & kEmbolden_Flag) {
emboldenOutline(&fFace->glyph->outline);
}
FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
int dx = glyph->getSubXFixed() >> 10;
int dy = glyph->getSubYFixed() >> 10;
// negate dy since freetype-y-goes-up and skia-y-goes-down
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;
getBBoxForCurrentGlyph(glyph, &bbox, true);
glyph->fWidth = SkToU16((bbox.xMax - bbox.xMin) >> 6);
glyph->fHeight = SkToU16((bbox.yMax - bbox.yMin) >> 6);
glyph->fTop = -SkToS16(bbox.yMax >> 6);
glyph->fLeft = SkToS16(bbox.xMin >> 6);
if (isLCD(fRec)) {
if (fLCDIsVert) {
glyph->fHeight += gLCDExtra;
glyph->fTop -= gLCDExtra >> 1;
} else {
glyph->fWidth += gLCDExtra;
glyph->fLeft -= gLCDExtra >> 1;
}
if ((fRec.fFlags & SkScalerContext::kVertical_Flag)) {
vLeft = Dot6ToFixed(bbox.xMin);
vTop = Dot6ToFixed(bbox.yMax);
}
updateGlyphIfLCD(glyph);
break;
}
@ -1057,6 +1088,62 @@ void SkScalerContext_FreeType::generateMetrics(SkGlyph* glyph) {
glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, fFace->glyph->linearHoriAdvance);
}
if ((fRec.fFlags & SkScalerContext::kVertical_Flag)
&& fFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
//TODO: do we need to specially handle SubpixelPositioning and Kerning?
FT_Matrix identityMatrix;
identityMatrix.xx = identityMatrix.yy = SK_Fixed1;
identityMatrix.xy = identityMatrix.yx = 0;
// if the matrix is not the identity matrix then we need to re-load the
// glyph with the identity matrix to get the necessary bounding box
if (memcmp(&fMatrix22, &identityMatrix, sizeof(FT_Matrix)) != 0) {
FT_Set_Transform(fFace, &identityMatrix, NULL);
err = FT_Load_Glyph( fFace, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags );
if (err != 0) {
SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
fFaceRec->fFontID, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, err));
goto ERROR;
}
if (fRec.fFlags & kEmbolden_Flag) {
emboldenOutline(&fFace->glyph->outline);
}
}
// bounding box of the unskewed and unscaled glyph
FT_BBox bbox;
getBBoxForCurrentGlyph(glyph, &bbox);
// compute the vertical gap above and below the glyph if the glyph were
// centered within the linearVertAdvance
SkFixed vGap = (fFace->glyph->linearVertAdvance - Dot6ToFixed(bbox.yMax - bbox.yMin)) / 2;
// the origin point of the glyph when rendered vertically
FT_Vector vOrigin;
vOrigin.x = fFace->glyph->linearHoriAdvance / 2;
vOrigin.y = vGap + Dot6ToFixed(bbox.yMax);
// transform the vertical origin based on the matrix of the actual glyph
FT_Vector_Transform(&vOrigin, &fMatrix22);
// compute a new offset vector for the glyph by subtracting the vertical
// origin from the original horizontal offset vector
glyph->fLeft = SkFixedRoundToInt(vLeft - vOrigin.x);
glyph->fTop = -SkFixedRoundToInt(vTop - vOrigin.y);
updateGlyphIfLCD(glyph);
// use the vertical advance values computed by freetype
glyph->fAdvanceX = -SkFixedMul(fMatrix22.xy, fFace->glyph->linearVertAdvance);
glyph->fAdvanceY = SkFixedMul(fMatrix22.yy, fFace->glyph->linearVertAdvance);
}
#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));
@ -1304,8 +1391,8 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
int dx = 0, dy = 0;
if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
dx = glyph.getSubXFixed() >> 10;
dy = glyph.getSubYFixed() >> 10;
dx = FixedToDot6(glyph.getSubXFixed());
dy = FixedToDot6(glyph.getSubYFixed());
// negate dy since freetype-y-goes-up and skia-y-goes-down
dy = -dy;
}
@ -1422,7 +1509,7 @@ void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
///////////////////////////////////////////////////////////////////////////////
#define ft2sk(x) SkFixedToScalar((x) << 10)
#define ft2sk(x) SkFixedToScalar(Dot6ToFixed(x))
#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 2
#define CONST_PARAM const