Work around changes in OSX 10.10.

In 10.10.1 CTFontGetAdvancesForGlyphs applies the font
transform to the width of the advance but always sets the
height of the advance to 0. Work around this by measuring the
advance with an unrotated font and apply the transform
manually.

In 10.7 through 10.9 CTFontDrawGlyphs appears to take
'positions' in text space and acts like
CGContextShowGlyphsAtPosition. In 10.10.1, these 'positions' now appear to be in glyph space, and
CTFontDrawGlyphs maps the glyph positions internally.

BUG=chromium:442574

Review URL: https://codereview.chromium.org/841843002
This commit is contained in:
bungeman 2015-01-08 08:33:44 -08:00 committed by Commit bot
parent 9558f65387
commit 3b4b66c04a

View File

@ -46,6 +46,9 @@
#include <dlfcn.h>
// Set to make glyph bounding boxes visible.
#define SK_SHOW_TEXT_BLIT_COVERAGE 0
class SkScalerContext_Mac;
// CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
@ -681,14 +684,18 @@ private:
AutoCFRelease<CTFontRef> fCTFont;
CGAffineTransform fInvTransform;
/** Vertical variant of fCTFont.
/** Unrotated variant of fCTFont.
*
* In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
* advances, but always sets the height to 0. This font is used to get the advances of the
* unrotated glyph, and then the rotation is applied separately.
*
* 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.
* With kCTFontVerticalOrientation the advances must be unrotated.
*/
AutoCFRelease<CTFontRef> fCTVerticalFont;
AutoCFRelease<CTFontRef> fCTUnrotatedFont;
AutoCFRelease<CGFontRef> fCGFont;
SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
@ -729,6 +736,7 @@ SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
if (fVertical) {
// Setting the vertical orientation here affects the character to glyph mapping.
AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
@ -748,11 +756,8 @@ SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
fCTFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize, &transform, ctFontDesc));
fCGFont.reset(CTFontCopyGraphicsFont(fCTFont, NULL));
if (fVertical) {
CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
transform = CGAffineTransformConcat(rotateLeft, transform);
fCTVerticalFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize, &transform, NULL));
}
fCTUnrotatedFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize,
&CGAffineTransformIdentity, NULL));
// The fUnitMatrix includes the text size (and em) as it is used to scale the raw font data.
SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
@ -896,11 +901,14 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph&
subY += offset.fY;
}
// CTFontDrawGlyphs and CGContextShowGlyphsAtPositions take 'positions' which are in text space.
// The glyph location (in device space) must be mapped into text space, so that CG can convert
// it back into device space.
CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
point = CGPointApplyAffineTransform(point, context.fInvTransform);
if (darwinVersion() < 14) {
// Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
// 'positions' which are in text space. The glyph location (in device space) must be
// mapped into text space, so that CG can convert it back into device space.
// In 10.10 and later, this is handled directly in CTFontDrawGlyphs.
point = CGPointApplyAffineTransform(point, context.fInvTransform);
}
ctFontDrawGlyphs(context.fCTFont, &glyphID, &point, 1, fCG);
SkASSERT(rowBytesPtr);
@ -1007,12 +1015,16 @@ void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
// The following block produces cgAdvance in CG units (pixels, y up).
CGSize cgAdvance;
if (fVertical) {
CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
CTFontGetAdvancesForGlyphs(fCTUnrotatedFont, kCTFontVerticalOrientation,
&cgGlyph, &cgAdvance, 1);
// Vertical advances are returned as widths instead of heights.
SkTSwap(cgAdvance.height, cgAdvance.width);
cgAdvance.height = -cgAdvance.height;
} else {
CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
CTFontGetAdvancesForGlyphs(fCTUnrotatedFont, kCTFontHorizontalOrientation,
&cgGlyph, &cgAdvance, 1);
}
cgAdvance = CGSizeApplyAffineTransform(cgAdvance, CTFontGetMatrix(fCTFont));
glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
@ -1153,7 +1165,11 @@ static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
U8CPU r = (rgb >> 16) & 0xFF;
U8CPU g = (rgb >> 8) & 0xFF;
U8CPU b = (rgb >> 0) & 0xFF;
return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
#if SK_SHOW_TEXT_BLIT_COVERAGE
lum = SkTMax(lum, (U8CPU)0x30);
#endif
return lum;
}
template<bool APPLY_PREBLEND>
static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
@ -1178,6 +1194,11 @@ static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
#if SK_SHOW_TEXT_BLIT_COVERAGE
r = SkTMax(r, (U8CPU)0x30);
g = SkTMax(g, (U8CPU)0x30);
b = SkTMax(b, (U8CPU)0x30);
#endif
return SkPack888ToRGB16(r, g, b);
}
template<bool APPLY_PREBLEND>
@ -1201,7 +1222,9 @@ static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
U8CPU r = (rgb >> 16) & 0xFF;
U8CPU g = (rgb >> 8) & 0xFF;
U8CPU b = (rgb >> 0) & 0xFF;
#if SK_SHOW_TEXT_BLIT_COVERAGE
a = SkTMax(a, (U8CPU)0x30);
#endif
return SkPackARGB32(a, r, g, b);
}