Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(579)

Unified Diff: src/ports/SkFontHost_mac.cpp

Issue 15064003: Fix vertical text scaling on Mac. (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: Make vertical text look better. Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« src/core/SkDraw.cpp ('K') | « src/core/SkDraw.cpp ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/ports/SkFontHost_mac.cpp
===================================================================
--- src/ports/SkFontHost_mac.cpp (revision 9039)
+++ src/ports/SkFontHost_mac.cpp (working copy)
@@ -124,13 +124,6 @@
return rect.size.width <= 0 || rect.size.height <= 0;
}
-static void CGRectInset_inline(CGRect* rect, CGFloat dx, CGFloat dy) {
- rect->origin.x += dx;
- rect->origin.y += dy;
- rect->size.width -= dx * 2;
- rect->size.height -= dy * 2;
-}
-
static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
return rect.origin.x;
}
@@ -251,10 +244,6 @@
return darwin_version;
}
-static bool isLeopard() {
- return darwinVersion() == 9;
-}
-
static bool isSnowLeopard() {
return darwinVersion() == 10;
}
@@ -300,11 +289,6 @@
ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
}
-static SkScalar getFontScale(CGFontRef cgFont) {
- int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
- return SkScalarInvert(SkIntToScalar(unitsPerEm));
-}
-
///////////////////////////////////////////////////////////////////////////////
#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
@@ -530,13 +514,7 @@
CTFontDescriptorCreateWithAttributes(cfAttributes));
if (ctFontDesc != NULL) {
- if (isLeopard()) {
- // CTFontCreateWithFontDescriptor on Leopard ignores the name
- AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFontName, 1, NULL));
- ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, ctFontDesc);
- } else {
- ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
- }
+ ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
}
}
@@ -648,13 +626,9 @@
return face;
}
-static void flip(SkMatrix* matrix) {
- matrix->setSkewX(-matrix->getSkewX());
- matrix->setSkewY(-matrix->getSkewY());
-}
-
///////////////////////////////////////////////////////////////////////////////
+/** GlyphRect is in FUnits (em space, y up). */
struct GlyphRect {
int16_t fMinX;
int16_t fMinY;
@@ -665,9 +639,8 @@
class SkScalerContext_Mac : public SkScalerContext {
public:
SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
- virtual ~SkScalerContext_Mac();
+ virtual ~SkScalerContext_Mac() { };
-
protected:
unsigned generateGlyphCount(void) SK_OVERRIDE;
uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
@@ -680,24 +653,32 @@
private:
static void CTPathElement(void *info, const CGPathElement *element);
uint16_t getFBoundingBoxesGlyphOffset();
- void getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const;
+ /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
+ void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bool generateBBoxes();
- CGAffineTransform fTransform;
- SkMatrix fUnitMatrix; // without font size
- SkMatrix fVerticalMatrix; // unit rotated
- SkMatrix fMatrix; // with font size
- SkMatrix fFBoundingBoxesMatrix; // lion-specific fix
+ /**
+ * Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
+ *
+ * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
+ * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
+ */
+ SkMatrix fFUnitMatrix;
Offscreen fOffscreen;
AutoCFRelease<CTFontRef> fCTFont;
- AutoCFRelease<CTFontRef> fCTVerticalFont; // for vertical advance
+ /** CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
+ * This makes kCTFontDefaultOrientation dangerous, because the metrics from
+ * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
+ * Use fCTVerticalFont with kCTFontVerticalOrientation to get metrics in the same space.
+ */
+ AutoCFRelease<CTFontRef> fCTVerticalFont;
AutoCFRelease<CGFontRef> fCGFont;
- GlyphRect* fFBoundingBoxes;
+ SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
uint16_t fFBoundingBoxesGlyphOffset;
uint16_t fGlyphCount;
bool fGeneratedFBoundingBoxes;
- bool fDoSubPosition;
- bool fVertical;
+ const bool fDoSubPosition;
+ const bool fVertical;
friend class Offscreen;
@@ -707,39 +688,22 @@
SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
const SkDescriptor* desc)
: INHERITED(typeface, desc)
- , fFBoundingBoxes(NULL)
+ , fFBoundingBoxes()
, fFBoundingBoxesGlyphOffset(0)
, fGeneratedFBoundingBoxes(false)
+ , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
+ , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
+
{
+ //this->forceGenerateImageFromPath();
CTFontRef ctFont = typeface->fFontRef.get();
CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
-
- // Get the state we need
- fRec.getSingleMatrix(&fMatrix);
- fUnitMatrix = fMatrix;
-
- // extract the font size out of the matrix, but leave the skewing for italic
- SkScalar reciprocal = SkScalarInvert(fRec.fTextSize);
- fUnitMatrix.preScale(reciprocal, reciprocal);
-
SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
-
- fTransform = MatrixToCGAffineTransform(fMatrix);
-
- CGAffineTransform transform;
- CGFloat unitFontSize;
- if (isLeopard()) {
- // passing 1 for pointSize to Leopard sets the font size to 1 pt.
- // pass the CoreText size explicitly
- transform = MatrixToCGAffineTransform(fUnitMatrix);
- unitFontSize = SkScalarToFloat(fRec.fTextSize);
- } else {
- // since our matrix includes everything, we pass 1 for pointSize
- transform = fTransform;
- unitFontSize = 1;
- }
- flip(&fUnitMatrix); // flip to fix up bounds later
- fVertical = SkToBool(fRec.fFlags & kVertical_Flag);
+ fGlyphCount = SkToU16(numGlyphs);
+
+ fRec.getSingleMatrix(&fFUnitMatrix);
+ CGAffineTransform transform = MatrixToCGAffineTransform(fFUnitMatrix);
+
AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
if (fVertical) {
AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
@@ -754,29 +718,18 @@
ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
}
}
- fCTFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform, ctFontDesc);
+ // Since our matrix includes everything, we pass 1 for size.
+ fCTFont = CTFontCreateCopyWithAttributes(ctFont, 1.f, &transform, ctFontDesc);
fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL);
if (fVertical) {
CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
transform = CGAffineTransformConcat(rotateLeft, transform);
- fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform, NULL);
- fVerticalMatrix = fUnitMatrix;
- if (isSnowLeopard()) {
- SkScalar scale = SkScalarMul(fRec.fTextSize, getFontScale(fCGFont));
- fVerticalMatrix.preScale(scale, scale);
- } else {
- fVerticalMatrix.preRotate(SkIntToScalar(90));
- }
- fVerticalMatrix.postScale(SK_Scalar1, -SK_Scalar1);
+ fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, 1.f, &transform, NULL);
}
- fGlyphCount = SkToU16(numGlyphs);
- fDoSubPosition = SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
+ SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
+ fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
}
-SkScalerContext_Mac::~SkScalerContext_Mac() {
- delete[] fFBoundingBoxes;
-}
-
CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
CGGlyph glyphID, size_t* rowBytesPtr,
bool generateA8FromLCD) {
@@ -823,11 +776,14 @@
CGContextSetTextDrawingMode(fCG, kCGTextFill);
CGContextSetFont(fCG, context.fCGFont);
- CGContextSetFontSize(fCG, 1);
- CGContextSetTextMatrix(fCG, context.fTransform);
+ CGContextSetFontSize(fCG, 1.0f /*CTFontGetSize(context.fCTFont)*/);
+ CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont));
- CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition);
- CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition);
+ // Because CG always draws from the horizontal baseline,
+ // if there is a non-integral translation from the horizontal origin to the vertical origin,
+ // then CG cannot draw the glyph in the correct location without subpixel positioning.
+ CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical);
+ CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || context.fVertical);
// Draw white on black to create mask.
// TODO: Draw black on white and invert, CG has a special case codepath.
@@ -860,12 +816,15 @@
subX = SkFixedToFloat(glyph.getSubXFixed());
subY = SkFixedToFloat(glyph.getSubYFixed());
}
+
+ // CGContextShowGlyphsAtPoint always draws using the horizontal baseline origin.
if (context.fVertical) {
- SkIPoint offset;
+ SkPoint offset;
context.getVerticalOffset(glyphID, &offset);
subX += offset.fX;
subY += offset.fY;
}
+
CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
glyph.fTop + glyph.fHeight - subY,
&glyphID, 1);
@@ -875,22 +834,21 @@
return image;
}
-void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const {
- CGSize vertOffset;
- CTFontGetVerticalTranslationsForGlyphs(fCTVerticalFont, &glyphID, &vertOffset, 1);
- const SkPoint trans = {CGToScalar(vertOffset.width),
- CGToScalar(vertOffset.height)};
- SkPoint floatOffset;
- fVerticalMatrix.mapPoints(&floatOffset, &trans, 1);
- if (!isSnowLeopard()) {
- // SnowLeopard fails to apply the font's matrix to the vertical metrics,
- // but Lion and Leopard do. The unit matrix describes the font's matrix at
- // point size 1. There may be some way to avoid mapping here by setting up
- // fVerticalMatrix differently, but this works for now.
- fUnitMatrix.mapPoints(&floatOffset, 1);
+void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
+ // SnowLeopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
+ // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
+ CGSize cgVertOffset;
+ CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
+
+ SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
+ if (isSnowLeopard()) {
+ // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
+ fFUnitMatrix.mapPoints(&skVertOffset, 1);
+ } else {
+ skVertOffset.fY = -skVertOffset.fY;
}
- offset->fX = SkScalarRound(floatOffset.fX);
- offset->fY = SkScalarRound(floatOffset.fY);
+
+ *offset = skVertOffset;
}
uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
@@ -938,7 +896,7 @@
}
uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
- fFBoundingBoxes = new GlyphRect[entries];
+ fFBoundingBoxes.reset(entries);
SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
@@ -951,10 +909,6 @@
rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
}
- fFBoundingBoxesMatrix = fMatrix;
- flip(&fFBoundingBoxesMatrix);
- SkScalar fontScale = getFontScale(fCGFont);
- fFBoundingBoxesMatrix.preScale(fontScale, fontScale);
return true;
}
@@ -985,105 +939,87 @@
}
void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
- CGSize advance;
- CGRect bounds;
- CGGlyph cgGlyph;
-
- // Get the state we need
- cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
-
+ const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
+ glyph->zeroMetrics();
+
+ bool wantedVerticalBoundsButGotHorizontalBounds = false;
+
+ // The following block produces gcAdvance and cgBounds in CG units (pixels, y up).
+ CGSize cgAdvance;
+ CGRect cgBounds;
if (fVertical) {
if (!isSnowLeopard()) {
- // Lion and Leopard respect the vertical font metrics.
+ // Lion and Mountain Lion respect requests for vertical bounds.
CTFontGetBoundingRectsForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
- &cgGlyph, &bounds, 1);
+ &cgGlyph, &cgBounds, 1);
} else {
- // Snow Leopard and earlier respect the vertical font metrics for
- // advances, but not bounds, so use the default box and adjust it below.
- CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
- &cgGlyph, &bounds, 1);
+ // Snow Leopard does not respect requests for vertical bounds.
+ // Request the horizontal bounds and adjust it below.
+ CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
+ &cgGlyph, &cgBounds, 1);
+ wantedVerticalBoundsButGotHorizontalBounds = true;
}
+ // All versions respect requests for vertical advances.
CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
- &cgGlyph, &advance, 1);
+ &cgGlyph, &cgAdvance, 1);
} else {
- CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
- &cgGlyph, &bounds, 1);
- CTFontGetAdvancesForGlyphs(fCTFont, kCTFontDefaultOrientation,
- &cgGlyph, &advance, 1);
+ CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
+ &cgGlyph, &cgBounds, 1);
+ CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
+ &cgGlyph, &cgAdvance, 1);
}
+ glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
+ glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
+
+ // Now work around all the bounds bugs.
// BUG?
// 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
// it should be empty. So, if we see a zero-advance, we check if it has an
// empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
// is rare, so we won't incur a big performance cost for this extra check.
- if (0 == advance.width && 0 == advance.height) {
+ if (0 == cgAdvance.width && 0 == cgAdvance.height) {
AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
if (NULL == path || CGPathIsEmpty(path)) {
- bounds = CGRectMake(0, 0, 0, 0);
+ return;
}
}
- glyph->zeroMetrics();
- glyph->fAdvanceX = SkFloatToFixed_Check(advance.width);
- glyph->fAdvanceY = -SkFloatToFixed_Check(advance.height);
-
- if (CGRectIsEmpty_inline(bounds)) {
+ if (CGRectIsEmpty_inline(cgBounds)) {
return;
}
- if (isLeopard() && !fVertical) {
- // Leopard does not consider the matrix skew in its bounds.
- // Run the bounding rectangle through the skew matrix to determine
- // the true bounds. However, this doesn't work if the font is vertical.
- // FIXME (Leopard): If the font has synthetic italic (e.g., matrix skew)
- // and the font is vertical, the bounds need to be recomputed.
- SkRect glyphBounds = SkRect::MakeXYWH(
- bounds.origin.x, bounds.origin.y,
- bounds.size.width, bounds.size.height);
- fUnitMatrix.mapRect(&glyphBounds);
- bounds.origin.x = glyphBounds.fLeft;
- bounds.origin.y = glyphBounds.fTop;
- bounds.size.width = glyphBounds.width();
- bounds.size.height = glyphBounds.height();
- }
- // Adjust the bounds
- //
- // CTFontGetBoundingRectsForGlyphs ignores the font transform, so we need
- // to transform the bounding box ourselves.
- //
- // The bounds are also expanded by 1 pixel, to give CG room for anti-aliasing.
- CGRectInset_inline(&bounds, -1, -1);
-
- // Get the metrics
- bool lionAdjustedMetrics = false;
+ // Convert bounds to SkGlyph units (pixels, y down).
+ SkRect skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
+ cgBounds.size.width, cgBounds.size.height);
if (isLion() || isMountainLion()) {
if (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()){
- lionAdjustedMetrics = true;
- SkRect adjust;
const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
- adjust.set(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
- fFBoundingBoxesMatrix.mapRect(&adjust);
- bounds.origin.x = SkScalarToFloat(adjust.fLeft) - 1;
- bounds.origin.y = SkScalarToFloat(adjust.fTop) - 1;
+ skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
+ // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
+ fFUnitMatrix.mapRect(&skBounds);
+ wantedVerticalBoundsButGotHorizontalBounds = fVertical;
}
- // Lion returns fractions in the bounds
- glyph->fWidth = SkToU16(sk_float_ceil2int(bounds.size.width));
- glyph->fHeight = SkToU16(sk_float_ceil2int(bounds.size.height));
- } else {
- glyph->fWidth = SkToU16(sk_float_round2int(bounds.size.width));
- glyph->fHeight = SkToU16(sk_float_round2int(bounds.size.height));
}
- glyph->fTop = SkToS16(-sk_float_round2int(CGRectGetMaxY_inline(bounds)));
- glyph->fLeft = SkToS16(sk_float_round2int(CGRectGetMinX_inline(bounds)));
- SkIPoint offset;
- if (fVertical && (isSnowLeopard() || lionAdjustedMetrics)) {
- // SnowLeopard doesn't respect vertical metrics, so compute them manually.
- // Also compute them for Lion when the metrics were computed by hand.
+ if (wantedVerticalBoundsButGotHorizontalBounds) {
+ // Snow Leopard does not respect requests for vertical bounds.
+ // Our own fFBoundingBoxes are for horizontal bounds.
+ // Convert these horizontal bounds into vertical bounds.
+ SkPoint offset;
getVerticalOffset(cgGlyph, &offset);
- glyph->fLeft += offset.fX;
- glyph->fTop += offset.fY;
+ skBounds.offset(offset);
}
+ SkIRect skIBounds;
+ skBounds.roundOut(&skIBounds);
+ // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
+ // Note that we currently don't know how much room to give subpixel smoothed glyphs
+ // as CG dilates the outlines by some amount.
+ skIBounds.outset(1, 1);
+ glyph->fTop = SkToS16(skIBounds.fTop);
+ glyph->fLeft = SkToS16(skIBounds.fLeft);
+ glyph->fWidth = SkToU16(skIBounds.width());
+ glyph->fHeight = SkToU16(skIBounds.height());
+
#ifdef HACK_COLORGLYPHS
glyph->fMaskFormat = SkMask::kARGB32_Format;
#endif
@@ -1371,17 +1307,17 @@
CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
}
- if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
+ if (fDoSubPosition) {
SkMatrix m;
m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
path->transform(m);
// balance the call to CTFontCreateCopyWithAttributes
CFSafeRelease(font);
}
- if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
- SkIPoint offset;
+ if (fVertical) {
+ SkPoint offset;
getVerticalOffset(cgGlyph, &offset);
- path->offset(SkIntToScalar(offset.fX), SkIntToScalar(offset.fY));
+ path->offset(offset.fX, offset.fY);
}
}
« src/core/SkDraw.cpp ('K') | « src/core/SkDraw.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698