Index: src/ports/SkFontHost_mac.cpp |
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp |
index c19df196b3a9a97b1ff4ad667ff550f44f27d56e..0d4bbb73d97a10437547f540091ba017ad006396 100755 |
--- a/src/ports/SkFontHost_mac.cpp |
+++ b/src/ports/SkFontHost_mac.cpp |
@@ -25,6 +25,7 @@ |
#include "SkFontDescriptor.h" |
#include "SkFloatingPoint.h" |
#include "SkGlyph.h" |
+#include "SkLazyFnPtr.h" |
#include "SkMaskGamma.h" |
#include "SkSFNTHeader.h" |
#include "SkOTTable_glyf.h" |
@@ -43,7 +44,7 @@ |
#include "SkFontMgr.h" |
#include "SkUtils.h" |
-//#define HACK_COLORGLYPHS |
+#include <dlfcn.h> |
class SkScalerContext_Mac; |
@@ -273,7 +274,6 @@ static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix, |
/////////////////////////////////////////////////////////////////////////////// |
#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) |
-#define BITMAP_INFO_GRAY (kCGImageAlphaNone) |
/** |
* There does not appear to be a publicly accessable API for determining if lcd |
@@ -304,7 +304,14 @@ static bool supports_LCD() { |
class Offscreen { |
public: |
- Offscreen(); |
+ Offscreen() |
+ : fRGBSpace(NULL) |
+ , fCG(NULL) |
+ , fDoAA(false) |
+ , fDoLCD(false) |
+ { |
+ fSize.set(0, 0); |
+ } |
CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph, |
CGGlyph glyphID, size_t* rowBytesPtr, |
@@ -328,11 +335,6 @@ private: |
} |
}; |
-Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL), |
- fDoAA(false), fDoLCD(false) { |
- fSize.set(0, 0); |
-} |
- |
/////////////////////////////////////////////////////////////////////////////// |
static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) { |
@@ -440,8 +442,21 @@ public: |
, fRequestedName(requestedName) |
, fFontRef(fontRef) // caller has already called CFRetain for us |
, fIsLocalStream(isLocalStream) |
+ , fHasSbixTable(false) |
{ |
SkASSERT(fontRef); |
+ |
+ AutoCFRelease<CFArrayRef> tags(CTFontCopyAvailableTables(fFontRef,kCTFontTableOptionNoOptions)); |
+ if (tags) { |
+ int count = SkToInt(CFArrayGetCount(tags)); |
+ for (int i = 0; i < count; ++i) { |
+ uintptr_t tag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(tags, i)); |
+ if ('sbix' == tag) { |
+ fHasSbixTable = true; |
+ break; |
+ } |
+ } |
+ } |
} |
SkString fRequestedName; |
@@ -469,6 +484,7 @@ protected: |
private: |
bool fIsLocalStream; |
+ bool fHasSbixTable; |
typedef SkTypeface INHERITED; |
}; |
@@ -663,6 +679,7 @@ private: |
Offscreen fOffscreen; |
AutoCFRelease<CTFontRef> fCTFont; |
+ CGAffineTransform fInvTransform; |
/** Vertical variant of fCTFont. |
* |
@@ -701,9 +718,14 @@ SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface, |
SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF); |
fGlyphCount = SkToU16(numGlyphs); |
+ // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up. |
+ // As a result, it is necessary to know the actual device size and request that. |
+ SkVector scale; |
SkMatrix skTransform; |
- fRec.getSingleMatrixWithoutTextSize(&skTransform); |
+ fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, &skTransform, |
+ NULL, NULL, &fFUnitMatrix); |
CGAffineTransform transform = MatrixToCGAffineTransform(skTransform); |
+ fInvTransform = CGAffineTransformInvert(transform); |
AutoCFRelease<CTFontDescriptorRef> ctFontDesc; |
if (fVertical) { |
@@ -722,13 +744,7 @@ SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface, |
// The transform contains everything except the requested text size. |
// Some properties, like 'trak', are based on the text size (before applying the matrix). |
- CGFloat textSize = ScalarToCG(fRec.fTextSize); |
- |
- // If a text size of 0 is requested, CoreGraphics will use 12 instead. |
- // If the text size is 0, set it to something tiny. |
- if (textSize < CGFLOAT_MIN) { |
- textSize = CGFLOAT_MIN; |
- } |
+ CGFloat textSize = ScalarToCG(scale.y()); |
fCTFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize, &transform, ctFontDesc)); |
fCGFont.reset(CTFontCopyGraphicsFont(fCTFont, NULL)); |
@@ -739,14 +755,39 @@ SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface, |
} |
// The fUnitMatrix includes the text size (and em) as it is used to scale the raw font data. |
- fRec.getSingleMatrix(&fFUnitMatrix); |
SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont))); |
fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit); |
} |
+extern "C" { |
+ |
+/** CTFontDrawGlyphs was introduced in 10.7. */ |
+typedef void (*CTFontDrawGlyphsProc)(CTFontRef, const CGGlyph[], const CGPoint[], |
+ size_t, CGContextRef); |
+ |
+/** This is an implementation of CTFontDrawGlyphs for 10.6. */ |
+static void sk_legacy_CTFontDrawGlyphs(CTFontRef, const CGGlyph glyphs[], const CGPoint points[], |
+ size_t count, CGContextRef cg) |
+{ |
+ CGContextShowGlyphsAtPositions(cg, glyphs, points, count); |
+} |
+ |
+} |
+ |
+CTFontDrawGlyphsProc SkChooseCTFontDrawGlyphs() { |
+ CTFontDrawGlyphsProc realCTFontDrawGlyphs; |
+ *reinterpret_cast<void**>(&realCTFontDrawGlyphs) = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs"); |
+ return realCTFontDrawGlyphs ? realCTFontDrawGlyphs : sk_legacy_CTFontDrawGlyphs; |
+}; |
+ |
+SK_DECLARE_STATIC_LAZY_FN_PTR(CTFontDrawGlyphsProc, gCTFontDrawGlyphs, SkChooseCTFontDrawGlyphs); |
+ |
CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph, |
CGGlyph glyphID, size_t* rowBytesPtr, |
- bool generateA8FromLCD) { |
+ bool generateA8FromLCD) |
+{ |
+ CTFontDrawGlyphsProc ctFontDrawGlyphs = gCTFontDrawGlyphs.get(); |
+ |
if (!fRGBSpace) { |
//It doesn't appear to matter what color space is specified. |
//Regular blends and antialiased text are always (s*a + d*(1-a)) |
@@ -769,6 +810,13 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& |
doAA = true; |
} |
+ // If this font might have color glyphs, disable LCD as there's no way to support it. |
+ // CoreText doesn't tell us which format it ended up using, so we can't detect it. |
+ // A8 will be ugly too (white on transparent), but TODO: we can detect gray and set to A8. |
+ if (SkMask::kARGB32_Format == glyph.fMaskFormat) { |
+ doLCD = false; |
+ } |
+ |
size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel); |
if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) { |
if (fSize.fWidth < glyph.fWidth) { |
@@ -780,24 +828,25 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& |
rowBytes = fSize.fWidth * sizeof(CGRGBPixel); |
void* image = fImageStorage.reset(rowBytes * fSize.fHeight); |
+ const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat) |
+ ? kCGImageAlphaPremultipliedFirst |
+ : kCGImageAlphaNoneSkipFirst; |
+ const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha; |
fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8, |
- rowBytes, fRGBSpace, BITMAP_INFO_RGB)); |
+ rowBytes, fRGBSpace, bitmapInfo)); |
- // skia handles quantization itself, so we disable this for cg to get |
- // full fractional data from them. |
+ // Skia handles quantization and subpixel positioning, |
+ // so disable quantization and enabe subpixel positioning in CG. |
CGContextSetAllowsFontSubpixelQuantization(fCG, false); |
CGContextSetShouldSubpixelQuantizeFonts(fCG, false); |
- CGContextSetTextDrawingMode(fCG, kCGTextFill); |
- CGContextSetFont(fCG, context.fCGFont); |
- CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont)); |
- CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont)); |
- |
// 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); |
+ CGContextSetAllowsFontSubpixelPositioning(fCG, true); |
+ CGContextSetShouldSubpixelPositionFonts(fCG, true); |
+ |
+ CGContextSetTextDrawingMode(fCG, kCGTextFill); |
// Draw white on black to create mask. |
// TODO: Draw black on white and invert, CG has a special case codepath. |
@@ -806,6 +855,14 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& |
// force our checks below to happen |
fDoAA = !doAA; |
fDoLCD = !doLCD; |
+ |
+ if (sk_legacy_CTFontDrawGlyphs == ctFontDrawGlyphs) { |
+ // CTFontDrawGlyphs will apply the font, font size, and font matrix to the CGContext. |
+ // Our 'fake' one does not, so set up the CGContext here. |
+ CGContextSetFont(fCG, context.fCGFont); |
+ CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont)); |
+ CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont)); |
+ } |
} |
if (fDoAA != doAA) { |
@@ -831,7 +888,7 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& |
subY = SkFixedToFloat(glyph.getSubYFixed()); |
} |
- // CGContextShowGlyphsAtPoint always draws using the horizontal baseline origin. |
+ // CoreText and CoreGraphics always draw using the horizontal baseline origin. |
if (context.fVertical) { |
SkPoint offset; |
context.getVerticalOffset(glyphID, &offset); |
@@ -839,9 +896,12 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& |
subY += offset.fY; |
} |
- CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX, |
- glyph.fTop + glyph.fHeight - subY, |
- &glyphID, 1); |
+ // 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); |
+ ctFontDrawGlyphs(context.fCTFont, &glyphID, &point, 1, fCG); |
SkASSERT(rowBytesPtr); |
*rowBytesPtr = rowBytes; |
@@ -1045,10 +1105,6 @@ void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) { |
glyph->fTop = SkToS16(skIBounds.fTop); |
glyph->fWidth = SkToU16(skIBounds.width()); |
glyph->fHeight = SkToU16(skIBounds.height()); |
- |
-#ifdef HACK_COLORGLYPHS |
- glyph->fMaskFormat = SkMask::kARGB32_Format; |
-#endif |
} |
#include "SkColorPriv.h" |
@@ -1140,22 +1196,14 @@ static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowByt |
} |
} |
-#ifdef HACK_COLORGLYPHS |
-// hack to colorize the output for testing kARGB32_Format |
-static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb, const SkGlyph& glyph, |
- int x, int y) { |
+static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) { |
+ U8CPU a = (rgb >> 24) & 0xFF; |
U8CPU r = (rgb >> 16) & 0xFF; |
U8CPU g = (rgb >> 8) & 0xFF; |
U8CPU b = (rgb >> 0) & 0xFF; |
- unsigned a = SkComputeLuminance(r, g, b); |
- // compute gradient from x,y |
- r = x * 255 / glyph.fWidth; |
- g = 0; |
- b = (glyph.fHeight - y) * 255 / glyph.fHeight; |
- return SkPreMultiplyARGB(a, r, g, b); // red |
+ return SkPackARGB32(a, r, g, b); |
} |
-#endif |
template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) { |
return (T*)((char*)ptr + byteOffset); |
@@ -1228,20 +1276,18 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) { |
dst += dstRB; |
} |
} break; |
-#ifdef HACK_COLORGLYPHS |
case SkMask::kARGB32_Format: { |
const int width = glyph.fWidth; |
size_t dstRB = glyph.rowBytes(); |
SkPMColor* dst = (SkPMColor*)glyph.fImage; |
for (int y = 0; y < glyph.fHeight; y++) { |
for (int x = 0; x < width; ++x) { |
- dst[x] = cgpixels_to_pmcolor(cgPixels[x], glyph, x, y); |
+ dst[x] = cgpixels_to_pmcolor(cgPixels[x]); |
} |
cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
dst = (SkPMColor*)((char*)dst + dstRB); |
} |
} break; |
-#endif |
default: |
SkDEBUGFAIL("unexpected mask format"); |
break; |
@@ -1828,6 +1874,13 @@ void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const { |
} |
} |
+ // CoreText provides no information as to whether a glyph will be color or not. |
+ // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis. |
+ // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd. |
+ if (fHasSbixTable) { |
+ rec->fMaskFormat = SkMask::kARGB32_Format; |
+ } |
+ |
// Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8. |
// All other masks can use regular gamma. |
if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) { |