'../third_party/freetype/include/**',
],
'sources': [
- '../src/ports/SkFontHost_mac_coretext.cpp',
+ '../src/ports/SkFontHost_mac.cpp',
'../src/utils/mac/SkStream_mac.cpp',
# '../src/ports/SkFontHost_FreeType.cpp',
# '../src/ports/SkFontHost_FreeType_common.cpp',
'../include/utils/mac',
],
'sources': [
- '../src/ports/SkFontHost_mac_coretext.cpp',
+ '../src/ports/SkFontHost_mac.cpp',
'../src/utils/mac/SkStream_mac.cpp',
'../src/ports/SkThread_pthread.cpp',
],
* found in the LICENSE file.
*/
+#include <vector>
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#ifdef SK_BUILD_FOR_IOS
+#include <CoreText/CoreText.h>
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+#include "SkFontHost.h"
+#include "SkCGUtils.h"
+#include "SkColorPriv.h"
+#include "SkDescriptor.h"
+#include "SkEndian.h"
+#include "SkFontDescriptor.h"
+#include "SkFloatingPoint.h"
+#include "SkGlyph.h"
+#include "SkMaskGamma.h"
+#include "SkSFNTHeader.h"
+#include "SkOTTable_glyf.h"
+#include "SkOTTable_head.h"
+#include "SkOTTable_hhea.h"
+#include "SkOTTable_loca.h"
+#include "SkOTUtils.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include "SkThread.h"
+#include "SkTypeface_mac.h"
+#include "SkUtils.h"
+#include "SkTypefaceCache.h"
-/*
- ** Mac Text API
- **
- **
- ** Two text APIs are available on the Mac, ATSUI and CoreText.
- **
- ** ATSUI is available on all versions of Mac OS X, but is 32-bit only.
- **
- ** The replacement API, CoreText, supports both 32-bit and 64-bit builds
- ** but is only available from Mac OS X 10.5 onwards.
- **
- ** To maintain support for Mac OS X 10.4, we default to ATSUI in 32-bit
- ** builds unless SK_USE_CORETEXT is defined.
-*/
-#ifndef SK_USE_CORETEXT
- #if TARGET_RT_64_BIT || defined(SK_USE_MAC_CORE_TEXT)
- #define SK_USE_CORETEXT 1
- #else
- #define SK_USE_CORETEXT 0
- #endif
+class SkScalerContext_Mac;
+
+// Being templated and taking const T* prevents calling
+// CFSafeRelease(autoCFRelease) through implicit conversion.
+template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
+ if (cfTypeRef) {
+ CFRelease(cfTypeRef);
+ }
+}
+
+// Being templated and taking const T* prevents calling
+// CFSafeRetain(autoCFRelease) through implicit conversion.
+template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
+ if (cfTypeRef) {
+ CFRetain(cfTypeRef);
+ }
+}
+
+/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
+template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
+public:
+ explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
+ ~AutoCFRelease() { CFSafeRelease(fCFRef); }
+
+ void reset(CFRef that = NULL) {
+ CFSafeRetain(that);
+ CFSafeRelease(fCFRef);
+ fCFRef = that;
+ }
+
+ AutoCFRelease& operator =(CFRef that) {
+ reset(that);
+ return *this;
+ }
+
+ operator CFRef() const { return fCFRef; }
+ CFRef get() const { return fCFRef; }
+
+private:
+ CFRef fCFRef;
+};
+
+template<typename T> class AutoCGTable : SkNoncopyable {
+public:
+ AutoCGTable(CGFontRef font)
+ //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
+ : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
+ , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
+ { }
+
+ const T* operator->() const { return fData; }
+
+private:
+ AutoCFRelease<CFDataRef> fCFData;
+public:
+ const T* fData;
+};
+
+// inline versions of these rect helpers
+
+static bool CGRectIsEmpty_inline(const CGRect& rect) {
+ 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;
+}
+
+static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
+ return rect.origin.x + rect.size.width;
+}
+
+static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
+ return rect.origin.y;
+}
+
+static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
+ return rect.origin.y + rect.size.height;
+}
+
+static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
+ return rect.size.width;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
+ size_t width, size_t height, size_t rowBytes) {
+ SkASSERT(width);
+ SkASSERT(width * sizeof(uint32_t) <= rowBytes);
+
+ if (width >= 32) {
+ while (height) {
+ sk_memset32(ptr, value, width);
+ ptr = (uint32_t*)((char*)ptr + rowBytes);
+ height -= 1;
+ }
+ return;
+ }
+
+ rowBytes -= width * sizeof(uint32_t);
+
+ if (width >= 8) {
+ while (height) {
+ int w = width;
+ do {
+ *ptr++ = value; *ptr++ = value;
+ *ptr++ = value; *ptr++ = value;
+ *ptr++ = value; *ptr++ = value;
+ *ptr++ = value; *ptr++ = value;
+ w -= 8;
+ } while (w >= 8);
+ while (--w >= 0) {
+ *ptr++ = value;
+ }
+ ptr = (uint32_t*)((char*)ptr + rowBytes);
+ height -= 1;
+ }
+ } else {
+ while (height) {
+ int w = width;
+ do {
+ *ptr++ = value;
+ } while (--w > 0);
+ ptr = (uint32_t*)((char*)ptr + rowBytes);
+ height -= 1;
+ }
+ }
+}
+
+#include <sys/utsname.h>
+
+typedef uint32_t CGRGBPixel;
+
+static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
+ return pixel & 0xFF;
+}
+
+// The calls to support subpixel are present in 10.5, but are not included in
+// the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are
+// included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for
+// instance, is present in the 10.5 CoreGraphics libary, use:
+// cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/
+// cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/
+// nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts
+
+#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
+CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value);
+CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value);
+CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value);
+CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value);
+CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value);
#endif
-#if SK_USE_CORETEXT
- #include "SkFontHost_mac_coretext.cpp"
-#else
- #include "SkFontHost_mac_atsui.cpp"
+static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
+
+// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
+static int readVersion() {
+ struct utsname info;
+ if (uname(&info) != 0) {
+ SkDebugf("uname failed\n");
+ return 0;
+ }
+ if (strcmp(info.sysname, "Darwin") != 0) {
+ SkDebugf("unexpected uname sysname %s\n", info.sysname);
+ return 0;
+ }
+ char* dot = strchr(info.release, '.');
+ if (!dot) {
+ SkDebugf("expected dot in uname release %s\n", info.release);
+ return 0;
+ }
+ int version = atoi(info.release);
+ if (version == 0) {
+ SkDebugf("could not parse uname release %s\n", info.release);
+ }
+ return version;
+}
+
+static int darwinVersion() {
+ static int darwin_version = readVersion();
+ return darwin_version;
+}
+
+static bool isLeopard() {
+ return darwinVersion() == 9;
+}
+
+static bool isSnowLeopard() {
+ return darwinVersion() == 10;
+}
+
+static bool isLion() {
+ return darwinVersion() == 11;
+}
+
+static bool isMountainLion() {
+ return darwinVersion() == 12;
+}
+
+static bool isLCDFormat(unsigned format) {
+ return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
+}
+
+static CGFloat ScalarToCG(SkScalar scalar) {
+ if (sizeof(CGFloat) == sizeof(float)) {
+ return SkScalarToFloat(scalar);
+ } else {
+ SkASSERT(sizeof(CGFloat) == sizeof(double));
+ return (CGFloat) SkScalarToDouble(scalar);
+ }
+}
+
+static SkScalar CGToScalar(CGFloat cgFloat) {
+ if (sizeof(CGFloat) == sizeof(float)) {
+ return SkFloatToScalar(cgFloat);
+ } else {
+ SkASSERT(sizeof(CGFloat) == sizeof(double));
+ return SkDoubleToScalar(cgFloat);
+ }
+}
+
+static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
+ SkScalar sx = SK_Scalar1,
+ SkScalar sy = SK_Scalar1) {
+ return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
+ -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
+ -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
+ ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
+ ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
+ ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
+}
+
+static SkScalar getFontScale(CGFontRef cgFont) {
+ int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
+ return SkScalarInvert(SkIntToScalar(unitsPerEm));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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
+ * font smoothing will be applied if we request it. The main issue is that if
+ * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
+ */
+static bool supports_LCD() {
+ static int gSupportsLCD = -1;
+ if (gSupportsLCD >= 0) {
+ return (bool) gSupportsLCD;
+ }
+ uint32_t rgb = 0;
+ AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
+ AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
+ colorspace, BITMAP_INFO_RGB));
+ CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
+ CGContextSetShouldSmoothFonts(cgContext, true);
+ CGContextSetShouldAntialias(cgContext, true);
+ CGContextSetTextDrawingMode(cgContext, kCGTextFill);
+ CGContextSetGrayFillColor(cgContext, 1, 1);
+ CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
+ uint32_t r = (rgb >> 16) & 0xFF;
+ uint32_t g = (rgb >> 8) & 0xFF;
+ uint32_t b = (rgb >> 0) & 0xFF;
+ gSupportsLCD = (r != g || r != b);
+ return (bool) gSupportsLCD;
+}
+
+class Offscreen {
+public:
+ Offscreen();
+
+ CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
+ CGGlyph glyphID, size_t* rowBytesPtr,
+ bool generateA8FromLCD);
+
+private:
+ enum {
+ kSize = 32 * 32 * sizeof(CGRGBPixel)
+ };
+ SkAutoSMalloc<kSize> fImageStorage;
+ AutoCFRelease<CGColorSpaceRef> fRGBSpace;
+
+ // cached state
+ AutoCFRelease<CGContextRef> fCG;
+ SkISize fSize;
+ bool fDoAA;
+ bool fDoLCD;
+
+ static int RoundSize(int dimension) {
+ return SkNextPow2(dimension);
+ }
+};
+
+Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL) {
+ fSize.set(0, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isMonospace) {
+ unsigned style = SkTypeface::kNormal;
+ CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
+
+ if (traits & kCTFontBoldTrait) {
+ style |= SkTypeface::kBold;
+ }
+ if (traits & kCTFontItalicTrait) {
+ style |= SkTypeface::kItalic;
+ }
+ if (isMonospace) {
+ *isMonospace = (traits & kCTFontMonoSpaceTrait) != 0;
+ }
+ return (SkTypeface::Style)style;
+}
+
+static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
+ SkFontID id = 0;
+// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
+// bracket this to be Mac only.
+#ifdef SK_BUILD_FOR_MAC
+ ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
+ id = (SkFontID)ats;
+ if (id != 0) {
+ id &= 0x3FFFFFFF; // make top two bits 00
+ return id;
+ }
+#endif
+ // CTFontGetPlatformFont returns NULL if the font is local
+ // (e.g., was created by a CSS3 @font-face rule).
+ AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
+ AutoCGTable<SkOTTableHead> headTable(cgFont);
+ if (headTable.fData) {
+ id = (SkFontID) headTable->checksumAdjustment;
+ id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
+ }
+ // well-formed fonts have checksums, but as a last resort, use the pointer.
+ if (id == 0) {
+ id = (SkFontID) (uintptr_t) fontRef;
+ id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
+ }
+ return id;
+}
+
+class SkTypeface_Mac : public SkTypeface {
+public:
+ SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isMonospace,
+ CTFontRef fontRef, const char name[])
+ : SkTypeface(style, fontID, isMonospace)
+ , fName(name)
+ , fFontRef(fontRef) // caller has already called CFRetain for us
+ {
+ SkASSERT(fontRef);
+ }
+
+ SkString fName;
+ AutoCFRelease<CTFontRef> fFontRef;
+
+protected:
+ friend class SkFontHost; // to access our protected members for deprecated methods
+
+ virtual int onGetUPEM() const SK_OVERRIDE;
+ virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
+ virtual size_t onGetTableData(SkFontTableTag, size_t offset,
+ size_t length, void* data) const SK_OVERRIDE;
+ virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
+ virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
+ virtual void onGetFontDescriptor(SkFontDescriptor*) const SK_OVERRIDE;
+
+private:
+ typedef SkTypeface INHERITED;
+};
+
+static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
+ SkASSERT(fontRef);
+ bool isMonospace;
+ SkTypeface::Style style = computeStyleBits(fontRef, &isMonospace);
+ SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
+
+ return new SkTypeface_Mac(style, fontID, isMonospace, fontRef, name);
+}
+
+static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
+ CTFontRef ctFont = NULL;
+
+ CTFontSymbolicTraits ctFontTraits = 0;
+ if (theStyle & SkTypeface::kBold) {
+ ctFontTraits |= kCTFontBoldTrait;
+ }
+ if (theStyle & SkTypeface::kItalic) {
+ ctFontTraits |= kCTFontItalicTrait;
+ }
+
+ // Create the font info
+ AutoCFRelease<CFStringRef> cfFontName(
+ CFStringCreateWithCString(NULL, familyName, kCFStringEncodingUTF8));
+
+ AutoCFRelease<CFNumberRef> cfFontTraits(
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
+
+ AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ AutoCFRelease<CFMutableDictionaryRef> cfTraits(
+ CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ // Create the font
+ if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
+ CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
+
+ CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
+ CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
+
+ AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
+ 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);
+ }
+ }
+ }
+
+ return ctFont ? NewFromFontRef(ctFont, familyName) : NULL;
+}
+
+static CTFontRef GetFontRefFromFontID(SkFontID fontID) {
+ SkTypeface_Mac* face = reinterpret_cast<SkTypeface_Mac*>(SkTypefaceCache::FindByID(fontID));
+ return face ? face->fFontRef.get() : NULL;
+}
+
+static SkTypeface* GetDefaultFace() {
+ SK_DECLARE_STATIC_MUTEX(gMutex);
+ SkAutoMutexAcquire ma(gMutex);
+
+ static SkTypeface* gDefaultFace;
+
+ if (NULL == gDefaultFace) {
+ gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal);
+ SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal);
+ }
+ return gDefaultFace;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
+CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
+ const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
+ return macface ? macface->fFontRef.get() : NULL;
+}
+
+/* This function is visible on the outside. It first searches the cache, and if
+ * not found, returns a new entry (after adding it to the cache).
+ */
+SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
+ SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
+ SkTypeface* face = SkTypefaceCache::FindByID(fontID);
+ if (face) {
+ face->ref();
+ } else {
+ face = NewFromFontRef(fontRef, NULL);
+ SkTypefaceCache::Add(face, face->style());
+ // NewFromFontRef doesn't retain the parameter, but the typeface it
+ // creates does release it in its destructor, so we balance that with
+ // a retain call here.
+ CFRetain(fontRef);
+ }
+ SkASSERT(face->getRefCnt() > 1);
+ return face;
+}
+
+struct NameStyleRec {
+ const char* fName;
+ SkTypeface::Style fStyle;
+};
+
+static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style,
+ void* ctx) {
+ const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face);
+ const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx);
+
+ return rec->fStyle == style && mface->fName.equals(rec->fName);
+}
+
+static const char* map_css_names(const char* name) {
+ static const struct {
+ const char* fFrom; // name the caller specified
+ const char* fTo; // "canonical" name we map to
+ } gPairs[] = {
+ { "sans-serif", "Helvetica" },
+ { "serif", "Times" },
+ { "monospace", "Courier" }
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+ if (strcmp(name, gPairs[i].fFrom) == 0) {
+ return gPairs[i].fTo;
+ }
+ }
+ return name; // no change
+}
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+ const char familyName[],
+ SkTypeface::Style style) {
+ if (familyName) {
+ familyName = map_css_names(familyName);
+ }
+
+ // Clone an existing typeface
+ // TODO: only clone if style matches the familyFace's style...
+ if (familyName == NULL && familyFace != NULL) {
+ familyFace->ref();
+ return const_cast<SkTypeface*>(familyFace);
+ }
+
+ if (!familyName || !*familyName) {
+ familyName = FONT_DEFAULT_NAME;
+ }
+
+ NameStyleRec rec = { familyName, style };
+ SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec);
+
+ if (NULL == face) {
+ face = NewFromName(familyName, style);
+ if (face) {
+ SkTypefaceCache::Add(face, style);
+ } else {
+ face = GetDefaultFace();
+ face->ref();
+ }
+ }
+ return face;
+}
+
+static void flip(SkMatrix* matrix) {
+ matrix->setSkewX(-matrix->getSkewX());
+ matrix->setSkewY(-matrix->getSkewY());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct GlyphRect {
+ int16_t fMinX;
+ int16_t fMinY;
+ int16_t fMaxX;
+ int16_t fMaxY;
+};
+
+class SkScalerContext_Mac : public SkScalerContext {
+public:
+ SkScalerContext_Mac(const SkDescriptor* desc);
+ virtual ~SkScalerContext_Mac(void);
+
+
+protected:
+ unsigned generateGlyphCount(void) SK_OVERRIDE;
+ uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
+ void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
+ void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
+ void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
+ void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
+ void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
+
+private:
+ static void CTPathElement(void *info, const CGPathElement *element);
+ uint16_t getFBoundingBoxesGlyphOffset();
+ void getVerticalOffset(CGGlyph glyphID, SkIPoint* 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
+ Offscreen fOffscreen;
+ AutoCFRelease<CTFontRef> fCTFont;
+ AutoCFRelease<CTFontRef> fCTVerticalFont; // for vertical advance
+ AutoCFRelease<CGFontRef> fCGFont;
+ GlyphRect* fFBoundingBoxes;
+ uint16_t fFBoundingBoxesGlyphOffset;
+ uint16_t fGlyphCount;
+ bool fGeneratedFBoundingBoxes;
+ bool fDoSubPosition;
+ bool fVertical;
+
+ friend class Offscreen;
+};
+
+SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
+ : SkScalerContext(desc)
+ , fFBoundingBoxes(NULL)
+ , fFBoundingBoxesGlyphOffset(0)
+ , fGeneratedFBoundingBoxes(false)
+{
+ CTFontRef ctFont = GetFontRefFromFontID(fRec.fFontID);
+ 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);
+ AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
+ if (fVertical) {
+ AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
+ kCFAllocatorDefault, 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+ if (cfAttributes) {
+ CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
+ AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
+ kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
+ CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
+ ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
+ }
+ }
+ fCTFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &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);
+ }
+ fGlyphCount = SkToU16(numGlyphs);
+ fDoSubPosition = SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
+}
+
+SkScalerContext_Mac::~SkScalerContext_Mac() {
+ delete[] fFBoundingBoxes;
+}
+
+CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
+ CGGlyph glyphID, size_t* rowBytesPtr,
+ bool generateA8FromLCD) {
+ 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))
+ //and smoothed text is always g=2.0.
+ fRGBSpace = CGColorSpaceCreateDeviceRGB();
+ }
+
+ // default to kBW_Format
+ bool doAA = false;
+ bool doLCD = false;
+
+ if (SkMask::kBW_Format != glyph.fMaskFormat) {
+ doLCD = true;
+ doAA = true;
+ }
+
+ // FIXME: lcd smoothed un-hinted rasterization unsupported.
+ if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
+ doLCD = false;
+ doAA = true;
+ }
+
+ size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
+ if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
+ if (fSize.fWidth < glyph.fWidth) {
+ fSize.fWidth = RoundSize(glyph.fWidth);
+ }
+ if (fSize.fHeight < glyph.fHeight) {
+ fSize.fHeight = RoundSize(glyph.fHeight);
+ }
+
+ rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
+ void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
+ fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
+ rowBytes, fRGBSpace, BITMAP_INFO_RGB);
+
+ // skia handles quantization itself, so we disable this for cg to get
+ // full fractional data from them.
+ CGContextSetAllowsFontSubpixelQuantization(fCG, false);
+ CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
+
+ CGContextSetTextDrawingMode(fCG, kCGTextFill);
+ CGContextSetFont(fCG, context.fCGFont);
+ CGContextSetFontSize(fCG, 1);
+ CGContextSetTextMatrix(fCG, context.fTransform);
+
+ CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition);
+ CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition);
+
+ // Draw white on black to create mask.
+ // TODO: Draw black on white and invert, CG has a special case codepath.
+ CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
+
+ // force our checks below to happen
+ fDoAA = !doAA;
+ fDoLCD = !doLCD;
+ }
+
+ if (fDoAA != doAA) {
+ CGContextSetShouldAntialias(fCG, doAA);
+ fDoAA = doAA;
+ }
+ if (fDoLCD != doLCD) {
+ CGContextSetShouldSmoothFonts(fCG, doLCD);
+ fDoLCD = doLCD;
+ }
+
+ CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
+ // skip rows based on the glyph's height
+ image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
+
+ // erase to black
+ sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
+
+ float subX = 0;
+ float subY = 0;
+ if (context.fDoSubPosition) {
+ subX = SkFixedToFloat(glyph.getSubXFixed());
+ subY = SkFixedToFloat(glyph.getSubYFixed());
+ }
+ if (context.fVertical) {
+ SkIPoint offset;
+ context.getVerticalOffset(glyphID, &offset);
+ subX += offset.fX;
+ subY += offset.fY;
+ }
+ CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
+ glyph.fTop + glyph.fHeight - subY,
+ &glyphID, 1);
+
+ SkASSERT(rowBytesPtr);
+ *rowBytesPtr = rowBytes;
+ 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);
+ }
+ offset->fX = SkScalarRound(floatOffset.fX);
+ offset->fY = SkScalarRound(floatOffset.fY);
+}
+
+uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
+ if (fFBoundingBoxesGlyphOffset) {
+ return fFBoundingBoxesGlyphOffset;
+ }
+ fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
+ AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
+ if (hheaTable.fData) {
+ fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
+ }
+ return fFBoundingBoxesGlyphOffset;
+}
+
+/*
+ * Lion has a bug in CTFontGetBoundingRectsForGlyphs which returns a bad value
+ * in theBounds.origin.x for fonts whose numOfLogHorMetrics is less than its
+ * glyph count. This workaround reads the glyph bounds from the font directly.
+ *
+ * The table is computed only if the font is a TrueType font, if the glyph
+ * value is >= fFBoundingBoxesGlyphOffset. (called only if fFBoundingBoxesGlyphOffset < fGlyphCount).
+ *
+ * TODO: A future optimization will compute fFBoundingBoxes once per CGFont, and
+ * compute fFBoundingBoxesMatrix once per font context.
+ */
+bool SkScalerContext_Mac::generateBBoxes() {
+ if (fGeneratedFBoundingBoxes) {
+ return NULL != fFBoundingBoxes;
+ }
+ fGeneratedFBoundingBoxes = true;
+
+ AutoCGTable<SkOTTableHead> headTable(fCGFont);
+ if (!headTable.fData) {
+ return false;
+ }
+
+ AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
+ if (!locaTable.fData) {
+ return false;
+ }
+
+ AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
+ if (!glyfTable.fData) {
+ return false;
+ }
+
+ uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
+ fFBoundingBoxes = new GlyphRect[entries];
+
+ SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
+ SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
+ glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
+ for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
+ const SkOTTableGlyphData* glyphData = glyphDataIter.next();
+ GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
+ rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
+ rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
+ 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;
+}
+
+unsigned SkScalerContext_Mac::generateGlyphCount(void) {
+ return fGlyphCount;
+}
+
+uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
+ CGGlyph cgGlyph;
+ UniChar theChar;
+
+ // Validate our parameters and state
+ SkASSERT(uni <= 0x0000FFFF);
+ SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
+
+ // Get the glyph
+ theChar = (UniChar) uni;
+
+ if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1)) {
+ cgGlyph = 0;
+ }
+
+ return cgGlyph;
+}
+
+void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
+ this->generateMetrics(glyph);
+}
+
+void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
+ CGSize advance;
+ CGRect bounds;
+ CGGlyph cgGlyph;
+
+ // Get the state we need
+ cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
+
+ if (fVertical) {
+ if (!isSnowLeopard()) {
+ // Lion and Leopard respect the vertical font metrics.
+ CTFontGetBoundingRectsForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
+ &cgGlyph, &bounds, 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);
+ }
+ CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
+ &cgGlyph, &advance, 1);
+ } else {
+ CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
+ &cgGlyph, &bounds, 1);
+ CTFontGetAdvancesForGlyphs(fCTFont, kCTFontDefaultOrientation,
+ &cgGlyph, &advance, 1);
+ }
+
+ // 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) {
+ AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
+ if (NULL == path || CGPathIsEmpty(path)) {
+ bounds = CGRectMake(0, 0, 0, 0);
+ }
+ }
+
+ glyph->zeroMetrics();
+ glyph->fAdvanceX = SkFloatToFixed_Check(advance.width);
+ glyph->fAdvanceY = -SkFloatToFixed_Check(advance.height);
+
+ if (CGRectIsEmpty_inline(bounds)) {
+ 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;
+ 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;
+ }
+ // 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.
+ getVerticalOffset(cgGlyph, &offset);
+ glyph->fLeft += offset.fX;
+ glyph->fTop += offset.fY;
+ }
+}
+
+#include "SkColorPriv.h"
+
+static void build_power_table(uint8_t table[], float ee) {
+ for (int i = 0; i < 256; i++) {
+ float x = i / 255.f;
+ x = sk_float_pow(x, ee);
+ int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
+ table[i] = SkToU8(xx);
+ }
+}
+
+/**
+ * This will invert the gamma applied by CoreGraphics, so we can get linear
+ * values.
+ *
+ * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
+ * The color space used does not appear to affect this choice.
+ */
+static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
+ static bool gInited;
+ static uint8_t gTableCoreGraphicsSmoothing[256];
+ if (!gInited) {
+ build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
+ gInited = true;
+ }
+ return gTableCoreGraphicsSmoothing;
+}
+
+static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
+ while (count > 0) {
+ uint8_t mask = 0;
+ for (int i = 7; i >= 0; --i) {
+ mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
+ if (0 == --count) {
+ break;
+ }
+ }
+ *dst++ = mask;
+ }
+}
+
+template<bool APPLY_PREBLEND>
+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);
+}
+template<bool APPLY_PREBLEND>
+static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
+ const SkGlyph& glyph, const uint8_t* table8) {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
+
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int i = 0; i < width; ++i) {
+ dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
+ }
+ cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+ dst += dstRB;
+ }
+}
+
+template<bool APPLY_PREBLEND>
+static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
+ const uint8_t* tableG,
+ const uint8_t* tableB) {
+ 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);
+ return SkPack888ToRGB16(r, g, b);
+}
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
+ const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
+
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int i = 0; i < width; i++) {
+ dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
+ }
+ cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+ dst = (uint16_t*)((char*)dst + dstRB);
+ }
+}
+
+template<bool APPLY_PREBLEND>
+static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
+ const uint8_t* tableG,
+ const uint8_t* tableB) {
+ 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);
+ return SkPackARGB32(0xFF, r, g, b);
+}
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
+ const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
+ for (int y = 0; y < glyph.fHeight; y++) {
+ for (int i = 0; i < width; i++) {
+ dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
+ }
+ cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+ dst = (uint32_t*)((char*)dst + dstRB);
+ }
+}
+
+template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
+ return (T*)((char*)ptr + byteOffset);
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
+ CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
+
+ // FIXME: lcd smoothed un-hinted rasterization unsupported.
+ bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
+
+ // Draw the glyph
+ size_t cgRowBytes;
+ CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
+ if (cgPixels == NULL) {
+ return;
+ }
+
+ //TODO: see if drawing black on white and inverting is faster (at least in
+ //lcd case) as core graphics appears to have special case code for drawing
+ //black text.
+
+ // Fix the glyph
+ const bool isLCD = isLCDFormat(glyph.fMaskFormat);
+ if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
+ const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
+
+ //Note that the following cannot really be integrated into the
+ //pre-blend, since we may not be applying the pre-blend; when we aren't
+ //applying the pre-blend it means that a filter wants linear anyway.
+ //Other code may also be applying the pre-blend, so we'd need another
+ //one with this and one without.
+ CGRGBPixel* addr = cgPixels;
+ for (int y = 0; y < glyph.fHeight; ++y) {
+ for (int x = 0; x < glyph.fWidth; ++x) {
+ int r = (addr[x] >> 16) & 0xFF;
+ int g = (addr[x] >> 8) & 0xFF;
+ int b = (addr[x] >> 0) & 0xFF;
+ addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
+ }
+ addr = SkTAddByteOffset(addr, cgRowBytes);
+ }
+ }
+
+ // Convert glyph to mask
+ switch (glyph.fMaskFormat) {
+ case SkMask::kLCD32_Format: {
+ if (fPreBlend.isApplicable()) {
+ rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
+ fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ } else {
+ rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
+ fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ }
+ } break;
+ case SkMask::kLCD16_Format: {
+ if (fPreBlend.isApplicable()) {
+ rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
+ fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ } else {
+ rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
+ fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+ }
+ } break;
+ case SkMask::kA8_Format: {
+ if (fPreBlend.isApplicable()) {
+ rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
+ } else {
+ rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
+ }
+ } break;
+ case SkMask::kBW_Format: {
+ const int width = glyph.fWidth;
+ size_t dstRB = glyph.rowBytes();
+ uint8_t* dst = (uint8_t*)glyph.fImage;
+ for (int y = 0; y < glyph.fHeight; y++) {
+ cgpixels_to_bits(dst, cgPixels, width);
+ cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+ dst += dstRB;
+ }
+ } break;
+ default:
+ SkDEBUGFAIL("unexpected mask format");
+ break;
+ }
+}
+
+/*
+ * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
+ * seems sufficient, and possibly even correct, to allow the hinted outline
+ * to be subpixel positioned.
+ */
+#define kScaleForSubPixelPositionHinting (4.0f)
+
+void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
+ CTFontRef font = fCTFont;
+ SkScalar scaleX = SK_Scalar1;
+ SkScalar scaleY = SK_Scalar1;
+
+ /*
+ * For subpixel positioning, we want to return an unhinted outline, so it
+ * can be positioned nicely at fractional offsets. However, we special-case
+ * if the baseline of the (horizontal) text is axis-aligned. In those cases
+ * we want to retain hinting in the direction orthogonal to the baseline.
+ * e.g. for horizontal baseline, we want to retain hinting in Y.
+ * The way we remove hinting is to scale the font by some value (4) in that
+ * direction, ask for the path, and then scale the path back down.
+ */
+ if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
+ SkMatrix m;
+ fRec.getSingleMatrix(&m);
+
+ // start out by assuming that we want no hining in X and Y
+ scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
+ // now see if we need to restore hinting for axis-aligned baselines
+ switch (SkComputeAxisAlignmentForHText(m)) {
+ case kX_SkAxisAlignment:
+ scaleY = SK_Scalar1; // want hinting in the Y direction
+ break;
+ case kY_SkAxisAlignment:
+ scaleX = SK_Scalar1; // want hinting in the X direction
+ break;
+ default:
+ break;
+ }
+
+ CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
+ // need to release font when we're done
+ font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
+ }
+
+ CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
+ AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
+
+ path->reset();
+ if (cgPath != NULL) {
+ CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
+ }
+
+ if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
+ 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;
+ getVerticalOffset(cgGlyph, &offset);
+ path->offset(SkIntToScalar(offset.fX), SkIntToScalar(offset.fY));
+ }
+}
+
+void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
+ SkPaint::FontMetrics* my) {
+ CGRect theBounds = CTFontGetBoundingBox(fCTFont);
+
+ SkPaint::FontMetrics theMetrics;
+ theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
+ theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
+ theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont));
+ theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
+ theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont));
+ theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
+ theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
+ theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
+ theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
+
+ if (mx != NULL) {
+ *mx = theMetrics;
+ }
+ if (my != NULL) {
+ *my = theMetrics;
+ }
+}
+
+void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
+ SkPath* skPath = (SkPath*)info;
+
+ // Process the path element
+ switch (element->type) {
+ case kCGPathElementMoveToPoint:
+ skPath->moveTo(element->points[0].x, -element->points[0].y);
+ break;
+
+ case kCGPathElementAddLineToPoint:
+ skPath->lineTo(element->points[0].x, -element->points[0].y);
+ break;
+
+ case kCGPathElementAddQuadCurveToPoint:
+ skPath->quadTo(element->points[0].x, -element->points[0].y,
+ element->points[1].x, -element->points[1].y);
+ break;
+
+ case kCGPathElementAddCurveToPoint:
+ skPath->cubicTo(element->points[0].x, -element->points[0].y,
+ element->points[1].x, -element->points[1].y,
+ element->points[2].x, -element->points[2].y);
+ break;
+
+ case kCGPathElementCloseSubpath:
+ skPath->close();
+ break;
+
+ default:
+ SkDEBUGFAIL("Unknown path element!");
+ break;
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Returns NULL on failure
+// Call must still manage its ownership of provider
+static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
+ AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
+ if (NULL == cg) {
+ return NULL;
+ }
+ CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
+ return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+ AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
+ if (NULL == provider) {
+ return NULL;
+ }
+ return create_from_dataProvider(provider);
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+ AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
+ if (NULL == provider) {
+ return NULL;
+ }
+ return create_from_dataProvider(provider);
+}
+
+// Web fonts added to the the CTFont registry do not return their character set.
+// Iterate through the font in this case. The existing caller caches the result,
+// so the performance impact isn't too bad.
+static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
+ SkTDArray<SkUnichar>* glyphToUnicode) {
+ glyphToUnicode->setCount(glyphCount);
+ SkUnichar* out = glyphToUnicode->begin();
+ sk_bzero(out, glyphCount * sizeof(SkUnichar));
+ UniChar unichar = 0;
+ while (glyphCount > 0) {
+ CGGlyph glyph;
+ if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
+ out[glyph] = unichar;
+ --glyphCount;
+ }
+ if (++unichar == 0) {
+ break;
+ }
+ }
+}
+
+// Construct Glyph to Unicode table.
+// Unicode code points that require conjugate pairs in utf16 are not
+// supported.
+static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
+ SkTDArray<SkUnichar>* glyphToUnicode) {
+ AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
+ if (!charSet) {
+ populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
+ return;
+ }
+
+ AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
+ charSet));
+ if (!bitmap) {
+ return;
+ }
+ CFIndex length = CFDataGetLength(bitmap);
+ if (!length) {
+ return;
+ }
+ if (length > 8192) {
+ // TODO: Add support for Unicode above 0xFFFF
+ // Consider only the BMP portion of the Unicode character points.
+ // The bitmap may contain other planes, up to plane 16.
+ // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
+ length = 8192;
+ }
+ const UInt8* bits = CFDataGetBytePtr(bitmap);
+ glyphToUnicode->setCount(glyphCount);
+ SkUnichar* out = glyphToUnicode->begin();
+ sk_bzero(out, glyphCount * sizeof(SkUnichar));
+ for (int i = 0; i < length; i++) {
+ int mask = bits[i];
+ if (!mask) {
+ continue;
+ }
+ for (int j = 0; j < 8; j++) {
+ CGGlyph glyph;
+ UniChar unichar = static_cast<UniChar>((i << 3) + j);
+ if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
+ out[glyph] = unichar;
+ }
+ }
+ }
+}
+
+static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
+ CGSize advance;
+ advance.width = 0;
+ CGGlyph glyph = gId;
+ CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
+ *data = sk_float_round2int(advance.width);
+ return true;
+}
+
+// we might move this into our CGUtils...
+static void CFStringToSkString(CFStringRef src, SkString* dst) {
+ // Reserve enough room for the worst-case string,
+ // plus 1 byte for the trailing null.
+ CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
+ kCFStringEncodingUTF8) + 1;
+ dst->resize(length);
+ CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
+ // Resize to the actual UTF-8 length used, stripping the null character.
+ dst->resize(strlen(dst->c_str()));
+}
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+ uint32_t fontID,
+ SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+ const uint32_t* glyphIDs,
+ uint32_t glyphIDsCount) {
+ CTFontRef originalCTFont = GetFontRefFromFontID(fontID);
+ AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
+ originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
+ SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
+
+ {
+ AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
+ CFStringToSkString(fontName, &info->fFontName);
+ }
+
+ info->fMultiMaster = false;
+ CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
+ info->fLastGlyphID = SkToU16(glyphCount - 1);
+ info->fEmSize = CTFontGetUnitsPerEm(ctFont);
+
+ if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+ populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
+ }
+
+ info->fStyle = 0;
+
+ // If it's not a truetype font, mark it as 'other'. Assume that TrueType
+ // fonts always have both glyf and loca tables. At the least, this is what
+ // sfntly needs to subset the font. CTFontCopyAttribute() does not always
+ // succeed in determining this directly.
+ if (!GetTableSize(fontID, 'glyf') || !GetTableSize(fontID, 'loca')) {
+ info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
+ info->fItalicAngle = 0;
+ info->fAscent = 0;
+ info->fDescent = 0;
+ info->fStemV = 0;
+ info->fCapHeight = 0;
+ info->fBBox = SkIRect::MakeEmpty();
+ return info;
+ }
+
+ info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+ CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
+ if (symbolicTraits & kCTFontMonoSpaceTrait) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+ }
+ if (symbolicTraits & kCTFontItalicTrait) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+ }
+ CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
+ if (stylisticClass & kCTFontSymbolicClass) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+ }
+ if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+ } else if (stylisticClass & kCTFontScriptsClass) {
+ info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+ }
+ info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
+ info->fAscent = (int16_t) CTFontGetAscent(ctFont);
+ info->fDescent = (int16_t) CTFontGetDescent(ctFont);
+ info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
+ CGRect bbox = CTFontGetBoundingBox(ctFont);
+
+ SkRect r;
+ r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
+ CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
+ CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
+ CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
+
+ r.roundOut(&(info->fBBox));
+
+ // Figure out a good guess for StemV - Min width of i, I, !, 1.
+ // This probably isn't very good with an italic font.
+ int16_t min_width = SHRT_MAX;
+ info->fStemV = 0;
+ static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
+ const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
+ CGGlyph glyphs[count];
+ CGRect boundingRects[count];
+ if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
+ CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
+ glyphs, boundingRects, count);
+ for (size_t i = 0; i < count; i++) {
+ int16_t width = (int16_t) boundingRects[i].size.width;
+ if (width > 0 && width < min_width) {
+ min_width = width;
+ info->fStemV = min_width;
+ }
+ }
+ }
+
+ if (false) { // TODO: haven't figured out how to know if font is embeddable
+ // (information is in the OS/2 table)
+ info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+ } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+ if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
+ skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
+ info->fGlyphWidths->fAdvance.append(1, &min_width);
+ skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
+ SkAdvancedTypefaceMetrics::WidthRange::kDefault);
+ } else {
+ info->fGlyphWidths.reset(
+ skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
+ glyphCount,
+ glyphIDs,
+ glyphIDsCount,
+ &getWidthAdvance));
+ }
+ }
+ return info;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SK_SFNT_ULONG get_font_type_tag(SkFontID uniqueID) {
+ CTFontRef ctFont = GetFontRefFromFontID(uniqueID);
+ AutoCFRelease<CFNumberRef> fontFormatRef(
+ static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
+ if (!fontFormatRef) {
+ return 0;
+ }
+
+ SInt32 fontFormatValue;
+ if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
+ return 0;
+ }
+
+ switch (fontFormatValue) {
+ case kCTFontFormatOpenTypePostScript:
+ return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
+ case kCTFontFormatOpenTypeTrueType:
+ return SkSFNTHeader::fontType_WindowsTrueType::TAG;
+ case kCTFontFormatTrueType:
+ return SkSFNTHeader::fontType_MacTrueType::TAG;
+ case kCTFontFormatPostScript:
+ return SkSFNTHeader::fontType_PostScript::TAG;
+ case kCTFontFormatBitmap:
+ return SkSFNTHeader::fontType_MacTrueType::TAG;
+ case kCTFontFormatUnrecognized:
+ default:
+ //CT seems to be unreliable in being able to obtain the type,
+ //even if all we want is the first four bytes of the font resource.
+ //Just the presence of the FontForge 'FFTM' table seems to throw it off.
+ return SkSFNTHeader::fontType_WindowsTrueType::TAG;
+ }
+}
+
+SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
+ SK_SFNT_ULONG fontType = get_font_type_tag(uniqueID);
+ if (0 == fontType) {
+ return NULL;
+ }
+
+ // get table tags
+ int numTables = CountTables(uniqueID);
+ SkTDArray<SkFontTableTag> tableTags;
+ tableTags.setCount(numTables);
+ GetTableTags(uniqueID, tableTags.begin());
+
+ // calc total size for font, save sizes
+ SkTDArray<size_t> tableSizes;
+ size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
+ for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+ size_t tableSize = GetTableSize(uniqueID, tableTags[tableIndex]);
+ totalSize += (tableSize + 3) & ~3;
+ *tableSizes.append() = tableSize;
+ }
+
+ // reserve memory for stream, and zero it (tables must be zero padded)
+ SkMemoryStream* stream = new SkMemoryStream(totalSize);
+ char* dataStart = (char*)stream->getMemoryBase();
+ sk_bzero(dataStart, totalSize);
+ char* dataPtr = dataStart;
+
+ // compute font header entries
+ uint16_t entrySelector = 0;
+ uint16_t searchRange = 1;
+ while (searchRange < numTables >> 1) {
+ entrySelector++;
+ searchRange <<= 1;
+ }
+ searchRange <<= 4;
+ uint16_t rangeShift = (numTables << 4) - searchRange;
+
+ // write font header
+ SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
+ header->fontType = fontType;
+ header->numTables = SkEndian_SwapBE16(numTables);
+ header->searchRange = SkEndian_SwapBE16(searchRange);
+ header->entrySelector = SkEndian_SwapBE16(entrySelector);
+ header->rangeShift = SkEndian_SwapBE16(rangeShift);
+ dataPtr += sizeof(SkSFNTHeader);
+
+ // write tables
+ SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
+ dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
+ for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+ size_t tableSize = tableSizes[tableIndex];
+ GetTableData(uniqueID, tableTags[tableIndex], 0, tableSize, dataPtr);
+ entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
+ entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
+ tableSize));
+ entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
+ entry->logicalLength = SkEndian_SwapBE32(tableSize);
+
+ dataPtr += (tableSize + 3) & ~3;
+ ++entry;
+ }
+
+ return stream;
+}
+
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) {
+ SkDEBUGFAIL("SkFontHost::GetFileName unimplemented");
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ SkFontDescriptor desc;
+ face->onGetFontDescriptor(&desc);
+
+ desc.serialize(stream);
+
+ // by convention, we also write out the actual sfnt data, preceeded by
+ // a packed-length. For now we skip that, so we just write the zero.
+ stream->writePackedUInt(0);
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ SkFontDescriptor desc(stream);
+
+ // by convention, Serialize will have also written the actual sfnt data.
+ // for now, we just want to skip it.
+ size_t size = stream->readPackedUInt();
+ stream->skip(size);
+
+ return SkFontHost::CreateTypeface(NULL, desc.getFamilyName(), desc.getStyle());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// DEPRECATED
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+ return new SkScalerContext_Mac(desc);
+}
+
+// DEPRECATED
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+ SkFontID nextFontID = 0;
+ SkTypeface* face = GetDefaultFace();
+ if (face->uniqueID() != currFontID) {
+ nextFontID = face->uniqueID();
+ }
+ return nextFontID;
+}
+
+// DEPRECATED
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface* face) {
+ face->onFilterRec(rec);
+}
+
+// DEPRECATED
+int SkFontHost::CountTables(SkFontID fontID) {
+ SkTypeface* face = SkTypefaceCache::FindByID(fontID);
+ return face ? face->onGetTableTags(NULL) : 0;
+}
+
+// DEPRECATED
+int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
+ SkTypeface* face = SkTypefaceCache::FindByID(fontID);
+ return face ? face->onGetTableTags(tags) : 0;
+}
+
+// DEPRECATED
+size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
+ SkTypeface* face = SkTypefaceCache::FindByID(fontID);
+ return face ? face->onGetTableData(tag, 0, ~0U, NULL) : 0;
+}
+
+// DEPRECATED
+size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
+ size_t offset, size_t length, void* dst) {
+ SkTypeface* face = SkTypefaceCache::FindByID(fontID);
+ return face ? face->onGetTableData(tag, offset, length, dst) : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int SkTypeface_Mac::onGetUPEM() const {
+ AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
+ return CGFontGetUnitsPerEm(cgFont);
+}
+
+// If, as is the case with web fonts, the CTFont data isn't available,
+// the CGFont data may work. While the CGFont may always provide the
+// right result, leave the CTFont code path to minimize disruption.
+static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
+ CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
+ kCTFontTableOptionNoOptions);
+ if (NULL == data) {
+ AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
+ data = CGFontCopyTableForTag(cgFont, tag);
+ }
+ return data;
+}
+
+int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
+ AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
+ kCTFontTableOptionNoOptions));
+ if (NULL == cfArray) {
+ return 0;
+ }
+ int count = CFArrayGetCount(cfArray);
+ if (tags) {
+ for (int i = 0; i < count; ++i) {
+ uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
+ tags[i] = static_cast<SkFontTableTag>(fontTag);
+ }
+ }
+ return count;
+}
+
+size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
+ size_t length, void* dstData) const {
+ AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
+ if (NULL == srcData) {
+ return 0;
+ }
+
+ size_t srcSize = CFDataGetLength(srcData);
+ if (offset >= srcSize) {
+ return 0;
+ }
+ if (length > srcSize - offset) {
+ length = srcSize - offset;
+ }
+ if (dstData) {
+ memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
+ }
+ return length;
+}
+
+SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
+ return new SkScalerContext_Mac(desc);
+}
+
+void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
+ unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
+ SkScalerContext::kAutohinting_Flag;
+
+ rec->fFlags &= ~flagsWeDontSupport;
+
+ bool lcdSupport = supports_LCD();
+
+ // Only two levels of hinting are supported.
+ // kNo_Hinting means avoid CoreGraphics outline dilation.
+ // kNormal_Hinting means CoreGraphics outline dilation is allowed.
+ // If there is no lcd support, hinting (dilation) cannot be supported.
+ SkPaint::Hinting hinting = rec->getHinting();
+ if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
+ hinting = SkPaint::kNo_Hinting;
+ } else if (SkPaint::kFull_Hinting == hinting) {
+ hinting = SkPaint::kNormal_Hinting;
+ }
+ rec->setHinting(hinting);
+
+ // FIXME: lcd smoothed un-hinted rasterization unsupported.
+ // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
+ // There is no current means to honor a request for unhinted lcd,
+ // so arbitrarilly ignore the hinting request and honor lcd.
+
+ // Hinting and smoothing should be orthogonal, but currently they are not.
+ // CoreGraphics has no API to influence hinting. However, its lcd smoothed
+ // output is drawn from auto-dilated outlines (the amount of which is
+ // determined by AppleFontSmoothing). Its regular anti-aliased output is
+ // drawn from un-dilated outlines.
+
+ // The behavior of Skia is as follows:
+ // [AA][no-hint]: generate AA using CoreGraphic's AA output.
+ // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
+ // channel. This matches [LCD][yes-hint] in weight.
+ // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
+ // Currenly side with LCD, effectively ignoring the hinting setting.
+ // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
+
+ if (isLCDFormat(rec->fMaskFormat)) {
+ if (lcdSupport) {
+ //CoreGraphics creates 555 masks for smoothed text anyway.
+ rec->fMaskFormat = SkMask::kLCD16_Format;
+ rec->setHinting(SkPaint::kNormal_Hinting);
+ } else {
+ rec->fMaskFormat = SkMask::kA8_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) {
+#ifndef SK_GAMMA_APPLY_TO_A8
+ rec->ignorePreBlend();
#endif
+ } else {
+ //CoreGraphics dialates smoothed text as needed.
+ rec->setContrast(0);
+ }
+}
+
+// we take ownership of the ref
+static const char* get_str(CFStringRef ref, SkString* str) {
+ CFStringToSkString(ref, str);
+ CFSafeRelease(ref);
+ return str->c_str();
+}
+
+void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc) const {
+ this->INHERITED::onGetFontDescriptor(desc);
+ SkString tmpStr;
+
+ desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
+ desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
+ desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
+}
+
+++ /dev/null
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include <Carbon/Carbon.h>
-#include "SkFontHost.h"
-#include "SkDescriptor.h"
-#include "SkEndian.h"
-#include "SkFloatingPoint.h"
-#include "SkPaint.h"
-#include "SkPoint.h"
-
-const char* gDefaultfont = "Arial"; // hard code for now
-SK_DECLARE_STATIC_MUTEX(gFTMutex);
-
-static inline SkPoint F32PtToSkPoint(const Float32Point p) {
- SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) };
- return sp;
-}
-
-static inline uint32_t _rotl(uint32_t v, uint32_t r) {
- return (v << r | v >> (32 - r));
-}
-
-class SkTypeface_Mac : public SkTypeface {
-public:
- SkTypeface_Mac(SkTypeface::Style style, uint32_t id)
- : SkTypeface(style, id) {}
-};
-
-#pragma mark -
-
-static uint32_t find_from_name(const char name[]) {
- CFStringRef str = CFStringCreateWithCString(NULL, name,
- kCFStringEncodingUTF8);
- uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault);
- CFRelease(str);
- return fontID;
-}
-
-static uint32_t find_default_fontID() {
- static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" };
-
- uint32_t fontID;
- for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) {
- fontID = find_from_name(gDefaultNames[i]);
- if (fontID) {
- return fontID;
- }
- }
- sk_throw();
- return 0;
-}
-
-static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) {
- uint32_t fontID = 0;
- if (NULL != name) {
- fontID = find_from_name(name);
- }
- if (0 == fontID) {
- fontID = find_default_fontID();
- }
- // we lie (for now) and report that we found the exact style bits
- return new SkTypeface_Mac(style, fontID);
-}
-
-#pragma mark -
-
-class SkScalerContext_Mac : public SkScalerContext {
-public:
- SkScalerContext_Mac(const SkDescriptor* desc);
- virtual ~SkScalerContext_Mac();
-
-protected:
- virtual unsigned generateGlyphCount();
- virtual uint16_t generateCharToGlyph(SkUnichar uni);
- virtual void generateAdvance(SkGlyph* glyph);
- virtual void generateMetrics(SkGlyph* glyph);
- virtual void generateImage(const SkGlyph& glyph);
- virtual void generatePath(const SkGlyph& glyph, SkPath* path);
- virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
-
-private:
- ATSUTextLayout fLayout;
- ATSUStyle fStyle;
- CGColorSpaceRef fGrayColorSpace;
- CGAffineTransform fTransform;
-
- static OSStatus MoveTo(const Float32Point *pt, void *cb);
- static OSStatus Line(const Float32Point *pt, void *cb);
- static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb);
- static OSStatus Close(void *cb);
-};
-
-void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface*) {
- // we only support 2 levels of hinting
- SkPaint::Hinting h = rec->getHinting();
- if (SkPaint::kSlight_Hinting == h) {
- h = SkPaint::kNo_Hinting;
- } else if (SkPaint::kFull_Hinting == h) {
- h = SkPaint::kNormal_Hinting;
- }
- rec->setHinting(h);
-
- // we don't support LCD text
- if (SkMask::kLCD16_Format == rec->fMaskFormat ||
- SkMask::kLCD32_Format == rec->fMaskFormat) {
- rec->fMaskFormat = SkMask::kA8_Format;
- }
-}
-
-SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
- : SkScalerContext(desc), fLayout(0), fStyle(0)
-{
- SkAutoMutexAcquire ac(gFTMutex);
- OSStatus err;
-
- err = ::ATSUCreateStyle(&fStyle);
- SkASSERT(0 == err);
-
- SkMatrix m;
- fRec.getSingleMatrix(&m);
-
- fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]),
- SkScalarToFloat(m[SkMatrix::kMSkewX]),
- SkScalarToFloat(m[SkMatrix::kMSkewY]),
- SkScalarToFloat(m[SkMatrix::kMScaleY]),
- SkScalarToFloat(m[SkMatrix::kMTransX]),
- SkScalarToFloat(m[SkMatrix::kMTransY]));
-
- ATSStyleRenderingOptions renderOpts = kATSStyleApplyAntiAliasing;
- switch (fRec.getHinting()) {
- case SkPaint::kNo_Hinting:
- case SkPaint::kSlight_Hinting:
- renderOpts |= kATSStyleNoHinting;
- break;
- case SkPaint::kNormal_Hinting:
- case SkPaint::kFull_Hinting:
- renderOpts |= kATSStyleApplyHints;
- break;
- }
-
- ATSUFontID fontID = FMGetFontFromATSFontRef(fRec.fFontID);
- // we put everything in the matrix, so our pt size is just 1.0
- Fixed fixedSize = SK_Fixed1;
- static const ATSUAttributeTag tags[] = {
- kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag
- };
- static const ByteCount sizes[] = {
- sizeof(fontID), sizeof(fixedSize), sizeof(fTransform), sizeof(renderOpts)
- };
- const ATSUAttributeValuePtr values[] = {
- &fontID, &fixedSize, &fTransform, &renderOpts
- };
- err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags),
- tags, sizes, values);
- SkASSERT(0 == err);
-
- err = ::ATSUCreateTextLayout(&fLayout);
- SkASSERT(0 == err);
-
- fGrayColorSpace = ::CGColorSpaceCreateDeviceGray();
-}
-
-SkScalerContext_Mac::~SkScalerContext_Mac() {
- ::CGColorSpaceRelease(fGrayColorSpace);
- ::ATSUDisposeTextLayout(fLayout);
- ::ATSUDisposeStyle(fStyle);
-}
-
-// man, we need to consider caching this, since it is just dependent on
-// fFontID, and not on any of the other settings like matrix or flags
-unsigned SkScalerContext_Mac::generateGlyphCount() {
- // The 'maxp' table stores the number of glyphs a offset 4, in 2 bytes
- uint16_t numGlyphs;
- if (SkFontHost::GetTableData(fRec.fFontID,
- SkSetFourByteTag('m', 'a', 'x', 'p'),
- 4, 2, &numGlyphs) != 2) {
- return 0xFFFF;
- }
- return SkEndian_SwapBE16(numGlyphs);
-}
-
-uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
-{
- SkAutoMutexAcquire ac(gFTMutex);
-
- OSStatus err;
- UniChar achar = uni;
- err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1);
- err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd);
-
- ATSLayoutRecord *layoutPtr;
- ItemCount count;
- ATSGlyphRef glyph;
-
- err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count);
- glyph = layoutPtr->glyphID;
- ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr);
- return glyph;
-}
-
-static void set_glyph_metrics_on_error(SkGlyph* glyph) {
- glyph->fRsbDelta = 0;
- glyph->fLsbDelta = 0;
- glyph->fWidth = 0;
- glyph->fHeight = 0;
- glyph->fTop = 0;
- glyph->fLeft = 0;
- glyph->fAdvanceX = 0;
- glyph->fAdvanceY = 0;
-}
-
-void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
- this->generateMetrics(glyph);
-}
-
-void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
- GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount);
- ATSGlyphScreenMetrics screenMetrics;
- ATSGlyphIdealMetrics idealMetrics;
-
- OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true,
- &screenMetrics);
- if (noErr != err) {
- set_glyph_metrics_on_error(glyph);
- return;
- }
- err = ATSUGlyphGetIdealMetrics(fStyle, 1, &glyphID, 0, &idealMetrics);
- if (noErr != err) {
- set_glyph_metrics_on_error(glyph);
- return;
- }
-
- if ((fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) == 0) {
- glyph->fAdvanceX = SkFloatToFixed(screenMetrics.deviceAdvance.x);
- glyph->fAdvanceY = -SkFloatToFixed(screenMetrics.deviceAdvance.y);
- } else {
- glyph->fAdvanceX = SkFloatToFixed(idealMetrics.advance.x);
- glyph->fAdvanceY = -SkFloatToFixed(idealMetrics.advance.y);
- }
-
- // specify an extra 1-pixel border, go tive CG room for its antialiasing
- // i.e. without this, I was seeing some edges chopped off!
- glyph->fWidth = screenMetrics.width + 2;
- glyph->fHeight = screenMetrics.height + 2;
- glyph->fLeft = sk_float_round2int(screenMetrics.topLeft.x) - 1;
- glyph->fTop = -sk_float_round2int(screenMetrics.topLeft.y) - 1;
-}
-
-void SkScalerContext_Mac::generateImage(const SkGlyph& glyph)
-{
- SkAutoMutexAcquire ac(gFTMutex);
- SkASSERT(fLayout);
-
- sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes());
- CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage,
- glyph.fWidth, glyph.fHeight, 8,
- glyph.rowBytes(), fGrayColorSpace,
- kCGImageAlphaNone);
- if (!contextRef) {
- SkASSERT(false);
- return;
- }
-
- ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0);
- ::CGContextSetTextDrawingMode(contextRef, kCGTextFill);
-
- CGGlyph glyphID = glyph.getGlyphID(fBaseGlyphCount);
- CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID);
- CGContextSetFont(contextRef, fontRef);
- CGContextSetFontSize(contextRef, 1);
- CGContextSetTextMatrix(contextRef, fTransform);
- CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft,
- glyph.fTop + glyph.fHeight, &glyphID, 1);
-
- ::CGContextRelease(contextRef);
-}
-
-#if 0
-static void convert_metrics(SkPaint::FontMetrics* dst,
- const ATSFontMetrics& src) {
- dst->fTop = -SkFloatToScalar(src.ascent);
- dst->fAscent = -SkFloatToScalar(src.ascent);
- dst->fDescent = SkFloatToScalar(src.descent);
- dst->fBottom = SkFloatToScalar(src.descent);
- dst->fLeading = SkFloatToScalar(src.leading);
-}
-#endif
-
-static void* get_font_table(ATSFontRef fontID, uint32_t tag) {
- ByteCount size;
- OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size);
- if (err) {
- return NULL;
- }
- void* data = sk_malloc_throw(size);
- err = ATSFontGetTable(fontID, tag, 0, size, data, &size);
- if (err) {
- sk_free(data);
- data = NULL;
- }
- return data;
-}
-
-static int get_be16(const void* data, size_t offset) {
- const char* ptr = reinterpret_cast<const char*>(data);
- uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset);
- int n = SkEndian_SwapBE16(value);
- // now force it to be signed
- return n << 16 >> 16;
-}
-
-#define SFNT_HEAD_UPEM_OFFSET 18
-#define SFNT_HEAD_YMIN_OFFSET 38
-#define SFNT_HEAD_YMAX_OFFSET 42
-#define SFNT_HEAD_STYLE_OFFSET 44
-
-#define SFNT_HHEA_ASCENT_OFFSET 4
-#define SFNT_HHEA_DESCENT_OFFSET 6
-#define SFNT_HHEA_LEADING_OFFSET 8
-
-static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) {
- void* head = get_font_table(font, 'head');
- if (NULL == head) {
- return false;
- }
- void* hhea = get_font_table(font, 'hhea');
- if (NULL == hhea) {
- sk_free(head);
- return false;
- }
-
- int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET);
- int ys[5];
-
- ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET);
- ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET);
- ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET);
- ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET);
- ys[4] = get_be16(hhea, SFNT_HHEA_LEADING_OFFSET);
-
- // now do some cleanup, to ensure y[max,min] are really that
- if (ys[0] > ys[1]) {
- ys[0] = ys[1];
- }
- if (ys[3] < ys[2]) {
- ys[3] = ys[2];
- }
-
- for (int i = 0; i < 5; i++) {
- pts[i].set(0, SkIntToScalar(ys[i]) / upem);
- }
-
- sk_free(hhea);
- sk_free(head);
- return true;
-}
-
-void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
- SkPaint::FontMetrics* my) {
- SkPoint pts[5];
-
- if (!init_vertical_metrics(fRec.fFontID, pts)) {
- // these are not as accurate as init_vertical_metrics :(
- ATSFontMetrics metrics;
- ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault,
- &metrics);
- pts[0].set(0, -SkFloatToScalar(metrics.ascent));
- pts[1].set(0, -SkFloatToScalar(metrics.ascent));
- pts[2].set(0, -SkFloatToScalar(metrics.descent));
- pts[3].set(0, -SkFloatToScalar(metrics.descent));
- pts[4].set(0, SkFloatToScalar(metrics.leading)); //+ or -?
- }
-
- SkMatrix m;
- fRec.getSingleMatrix(&m);
- m.mapPoints(pts, 5);
-
- if (mx) {
- mx->fTop = pts[0].fX;
- mx->fAscent = pts[1].fX;
- mx->fDescent = pts[2].fX;
- mx->fBottom = pts[3].fX;
- mx->fLeading = pts[4].fX;
- // FIXME:
- mx->fAvgCharWidth = 0;
- mx->fXMin = 0;
- mx->fXMax = 0;
- mx->fXHeight = 0;
- }
- if (my) {
- my->fTop = pts[0].fY;
- my->fAscent = pts[1].fY;
- my->fDescent = pts[2].fY;
- my->fBottom = pts[3].fY;
- my->fLeading = pts[4].fY;
- // FIXME:
- my->fAvgCharWidth = 0;
- my->fXMin = 0;
- my->fXMax = 0;
- my->fXHeight = 0;
- }
-}
-
-void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path)
-{
- SkAutoMutexAcquire ac(gFTMutex);
- OSStatus err,result;
-
- err = ::ATSUGlyphGetCubicPaths(
- fStyle,glyph.fID,
- &SkScalerContext_Mac::MoveTo,
- &SkScalerContext_Mac::Line,
- &SkScalerContext_Mac::Curve,
- &SkScalerContext_Mac::Close,
- path,&result);
- SkASSERT(err == noErr);
-}
-
-OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb)
-{
- reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt));
- return noErr;
-}
-
-OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb)
-{
- reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt));
- return noErr;
-}
-
-OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1,
- const Float32Point *pt2,
- const Float32Point *pt3, void *cb)
-{
- reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1),
- F32PtToSkPoint(*pt2),
- F32PtToSkPoint(*pt3));
- return noErr;
-}
-
-OSStatus SkScalerContext_Mac::Close(void *cb)
-{
- reinterpret_cast<SkPath*>(cb)->close();
- return noErr;
-}
-
-#pragma mark -
-
-void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
- SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
-}
-
-SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
- SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
- return NULL;
-}
-
-SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
- return NULL;
-}
-
-SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
- return NULL;
-}
-
-// static
-SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
- uint32_t fontID,
- SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
- const uint32_t* glyphIDs,
- uint32_t glyphIDsCount) {
- SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
- return NULL;
-}
-
-SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
- return new SkScalerContext_Mac(desc);
-}
-
-SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
- uint32_t newFontID = find_default_fontID();
- if (newFontID == currFontID) {
- newFontID = 0;
- }
- return newFontID;
-}
-
-SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
- const char familyName[],
- SkTypeface::Style style) {
- // todo: we don't know how to respect style bits
- if (NULL == familyName && NULL != familyFace) {
- familyFace->ref();
- return const_cast<SkTypeface*>(familyFace);
- } else {
- return CreateTypeface_(familyName, style);
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct SkSFNTHeader {
- uint32_t fVersion;
- uint16_t fNumTables;
- uint16_t fSearchRange;
- uint16_t fEntrySelector;
- uint16_t fRangeShift;
-};
-
-struct SkSFNTDirEntry {
- uint32_t fTag;
- uint32_t fChecksum;
- uint32_t fOffset;
- uint32_t fLength;
-};
-
-struct SfntHeader {
- SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) {
- ByteCount size;
- if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) {
- return;
- }
-
- SkAutoMalloc storage(size);
- SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get());
- if (ATSFontGetTableDirectory(fontID, size, header, &size)) {
- return;
- }
-
- fCount = SkEndian_SwapBE16(header->fNumTables);
- fData = header;
- storage.detach();
- }
-
- ~SfntHeader() {
- sk_free(fData);
- }
-
- int count() const { return fCount; }
- const SkSFNTDirEntry* entries() const {
- return reinterpret_cast<const SkSFNTDirEntry*>
- (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader));
- }
-
-private:
- int fCount;
- void* fData;
-};
-
-int SkFontHost::CountTables(SkFontID fontID) {
- SfntHeader header(fontID, false);
- return header.count();
-}
-
-int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
- SfntHeader header(fontID, true);
- int count = header.count();
- const SkSFNTDirEntry* entry = header.entries();
- for (int i = 0; i < count; i++) {
- tags[i] = SkEndian_SwapBE32(entry[i].fTag);
- }
- return count;
-}
-
-size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
- ByteCount size;
- if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) {
- return 0;
- }
- return size;
-}
-
-size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
- size_t offset, size_t length, void* data) {
- ByteCount size;
- if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) {
- return 0;
- }
- if (offset >= size) {
- return 0;
- }
- if (offset + length > size) {
- length = size - offset;
- }
- return length;
-}
+++ /dev/null
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include <vector>
-#ifdef SK_BUILD_FOR_MAC
-#import <ApplicationServices/ApplicationServices.h>
-#endif
-
-#ifdef SK_BUILD_FOR_IOS
-#include <CoreText/CoreText.h>
-#include <CoreGraphics/CoreGraphics.h>
-#include <CoreFoundation/CoreFoundation.h>
-#endif
-
-#include "SkFontHost.h"
-#include "SkCGUtils.h"
-#include "SkColorPriv.h"
-#include "SkDescriptor.h"
-#include "SkEndian.h"
-#include "SkFontDescriptor.h"
-#include "SkFloatingPoint.h"
-#include "SkGlyph.h"
-#include "SkMaskGamma.h"
-#include "SkSFNTHeader.h"
-#include "SkOTTable_glyf.h"
-#include "SkOTTable_head.h"
-#include "SkOTTable_hhea.h"
-#include "SkOTTable_loca.h"
-#include "SkOTUtils.h"
-#include "SkPaint.h"
-#include "SkPath.h"
-#include "SkString.h"
-#include "SkStream.h"
-#include "SkThread.h"
-#include "SkTypeface_mac.h"
-#include "SkUtils.h"
-#include "SkTypefaceCache.h"
-
-class SkScalerContext_Mac;
-
-// Being templated and taking const T* prevents calling
-// CFSafeRelease(autoCFRelease) through implicit conversion.
-template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
- if (cfTypeRef) {
- CFRelease(cfTypeRef);
- }
-}
-
-// Being templated and taking const T* prevents calling
-// CFSafeRetain(autoCFRelease) through implicit conversion.
-template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
- if (cfTypeRef) {
- CFRetain(cfTypeRef);
- }
-}
-
-/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
-template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
-public:
- explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
- ~AutoCFRelease() { CFSafeRelease(fCFRef); }
-
- void reset(CFRef that = NULL) {
- CFSafeRetain(that);
- CFSafeRelease(fCFRef);
- fCFRef = that;
- }
-
- AutoCFRelease& operator =(CFRef that) {
- reset(that);
- return *this;
- }
-
- operator CFRef() const { return fCFRef; }
- CFRef get() const { return fCFRef; }
-
-private:
- CFRef fCFRef;
-};
-
-template<typename T> class AutoCGTable : SkNoncopyable {
-public:
- AutoCGTable(CGFontRef font)
- //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
- : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
- , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
- { }
-
- const T* operator->() const { return fData; }
-
-private:
- AutoCFRelease<CFDataRef> fCFData;
-public:
- const T* fData;
-};
-
-// inline versions of these rect helpers
-
-static bool CGRectIsEmpty_inline(const CGRect& rect) {
- 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;
-}
-
-static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
- return rect.origin.x + rect.size.width;
-}
-
-static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
- return rect.origin.y;
-}
-
-static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
- return rect.origin.y + rect.size.height;
-}
-
-static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
- return rect.size.width;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
- size_t width, size_t height, size_t rowBytes) {
- SkASSERT(width);
- SkASSERT(width * sizeof(uint32_t) <= rowBytes);
-
- if (width >= 32) {
- while (height) {
- sk_memset32(ptr, value, width);
- ptr = (uint32_t*)((char*)ptr + rowBytes);
- height -= 1;
- }
- return;
- }
-
- rowBytes -= width * sizeof(uint32_t);
-
- if (width >= 8) {
- while (height) {
- int w = width;
- do {
- *ptr++ = value; *ptr++ = value;
- *ptr++ = value; *ptr++ = value;
- *ptr++ = value; *ptr++ = value;
- *ptr++ = value; *ptr++ = value;
- w -= 8;
- } while (w >= 8);
- while (--w >= 0) {
- *ptr++ = value;
- }
- ptr = (uint32_t*)((char*)ptr + rowBytes);
- height -= 1;
- }
- } else {
- while (height) {
- int w = width;
- do {
- *ptr++ = value;
- } while (--w > 0);
- ptr = (uint32_t*)((char*)ptr + rowBytes);
- height -= 1;
- }
- }
-}
-
-#include <sys/utsname.h>
-
-typedef uint32_t CGRGBPixel;
-
-static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
- return pixel & 0xFF;
-}
-
-// The calls to support subpixel are present in 10.5, but are not included in
-// the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are
-// included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for
-// instance, is present in the 10.5 CoreGraphics libary, use:
-// cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/
-// cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/
-// nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts
-
-#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
-CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value);
-CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value);
-CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value);
-CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value);
-CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value);
-#endif
-
-static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
-
-// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
-static int readVersion() {
- struct utsname info;
- if (uname(&info) != 0) {
- SkDebugf("uname failed\n");
- return 0;
- }
- if (strcmp(info.sysname, "Darwin") != 0) {
- SkDebugf("unexpected uname sysname %s\n", info.sysname);
- return 0;
- }
- char* dot = strchr(info.release, '.');
- if (!dot) {
- SkDebugf("expected dot in uname release %s\n", info.release);
- return 0;
- }
- int version = atoi(info.release);
- if (version == 0) {
- SkDebugf("could not parse uname release %s\n", info.release);
- }
- return version;
-}
-
-static int darwinVersion() {
- static int darwin_version = readVersion();
- return darwin_version;
-}
-
-static bool isLeopard() {
- return darwinVersion() == 9;
-}
-
-static bool isSnowLeopard() {
- return darwinVersion() == 10;
-}
-
-static bool isLion() {
- return darwinVersion() == 11;
-}
-
-static bool isMountainLion() {
- return darwinVersion() == 12;
-}
-
-static bool isLCDFormat(unsigned format) {
- return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
-}
-
-static CGFloat ScalarToCG(SkScalar scalar) {
- if (sizeof(CGFloat) == sizeof(float)) {
- return SkScalarToFloat(scalar);
- } else {
- SkASSERT(sizeof(CGFloat) == sizeof(double));
- return (CGFloat) SkScalarToDouble(scalar);
- }
-}
-
-static SkScalar CGToScalar(CGFloat cgFloat) {
- if (sizeof(CGFloat) == sizeof(float)) {
- return SkFloatToScalar(cgFloat);
- } else {
- SkASSERT(sizeof(CGFloat) == sizeof(double));
- return SkDoubleToScalar(cgFloat);
- }
-}
-
-static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
- SkScalar sx = SK_Scalar1,
- SkScalar sy = SK_Scalar1) {
- return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
- -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
- -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
- ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
- ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
- ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
-}
-
-static SkScalar getFontScale(CGFontRef cgFont) {
- int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
- return SkScalarInvert(SkIntToScalar(unitsPerEm));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#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
- * font smoothing will be applied if we request it. The main issue is that if
- * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
- */
-static bool supports_LCD() {
- static int gSupportsLCD = -1;
- if (gSupportsLCD >= 0) {
- return (bool) gSupportsLCD;
- }
- uint32_t rgb = 0;
- AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
- AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
- colorspace, BITMAP_INFO_RGB));
- CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
- CGContextSetShouldSmoothFonts(cgContext, true);
- CGContextSetShouldAntialias(cgContext, true);
- CGContextSetTextDrawingMode(cgContext, kCGTextFill);
- CGContextSetGrayFillColor(cgContext, 1, 1);
- CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
- uint32_t r = (rgb >> 16) & 0xFF;
- uint32_t g = (rgb >> 8) & 0xFF;
- uint32_t b = (rgb >> 0) & 0xFF;
- gSupportsLCD = (r != g || r != b);
- return (bool) gSupportsLCD;
-}
-
-class Offscreen {
-public:
- Offscreen();
-
- CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
- CGGlyph glyphID, size_t* rowBytesPtr,
- bool generateA8FromLCD);
-
-private:
- enum {
- kSize = 32 * 32 * sizeof(CGRGBPixel)
- };
- SkAutoSMalloc<kSize> fImageStorage;
- AutoCFRelease<CGColorSpaceRef> fRGBSpace;
-
- // cached state
- AutoCFRelease<CGContextRef> fCG;
- SkISize fSize;
- bool fDoAA;
- bool fDoLCD;
-
- static int RoundSize(int dimension) {
- return SkNextPow2(dimension);
- }
-};
-
-Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL) {
- fSize.set(0, 0);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isMonospace) {
- unsigned style = SkTypeface::kNormal;
- CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
-
- if (traits & kCTFontBoldTrait) {
- style |= SkTypeface::kBold;
- }
- if (traits & kCTFontItalicTrait) {
- style |= SkTypeface::kItalic;
- }
- if (isMonospace) {
- *isMonospace = (traits & kCTFontMonoSpaceTrait) != 0;
- }
- return (SkTypeface::Style)style;
-}
-
-static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
- SkFontID id = 0;
-// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
-// bracket this to be Mac only.
-#ifdef SK_BUILD_FOR_MAC
- ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
- id = (SkFontID)ats;
- if (id != 0) {
- id &= 0x3FFFFFFF; // make top two bits 00
- return id;
- }
-#endif
- // CTFontGetPlatformFont returns NULL if the font is local
- // (e.g., was created by a CSS3 @font-face rule).
- AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
- AutoCGTable<SkOTTableHead> headTable(cgFont);
- if (headTable.fData) {
- id = (SkFontID) headTable->checksumAdjustment;
- id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
- }
- // well-formed fonts have checksums, but as a last resort, use the pointer.
- if (id == 0) {
- id = (SkFontID) (uintptr_t) fontRef;
- id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
- }
- return id;
-}
-
-class SkTypeface_Mac : public SkTypeface {
-public:
- SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isMonospace,
- CTFontRef fontRef, const char name[])
- : SkTypeface(style, fontID, isMonospace)
- , fName(name)
- , fFontRef(fontRef) // caller has already called CFRetain for us
- {
- SkASSERT(fontRef);
- }
-
- SkString fName;
- AutoCFRelease<CTFontRef> fFontRef;
-
-protected:
- friend class SkFontHost; // to access our protected members for deprecated methods
-
- virtual int onGetUPEM() const SK_OVERRIDE;
- virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
- virtual size_t onGetTableData(SkFontTableTag, size_t offset,
- size_t length, void* data) const SK_OVERRIDE;
- virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
- virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
- virtual void onGetFontDescriptor(SkFontDescriptor*) const SK_OVERRIDE;
-
-private:
- typedef SkTypeface INHERITED;
-};
-
-static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
- SkASSERT(fontRef);
- bool isMonospace;
- SkTypeface::Style style = computeStyleBits(fontRef, &isMonospace);
- SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
-
- return new SkTypeface_Mac(style, fontID, isMonospace, fontRef, name);
-}
-
-static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
- CTFontRef ctFont = NULL;
-
- CTFontSymbolicTraits ctFontTraits = 0;
- if (theStyle & SkTypeface::kBold) {
- ctFontTraits |= kCTFontBoldTrait;
- }
- if (theStyle & SkTypeface::kItalic) {
- ctFontTraits |= kCTFontItalicTrait;
- }
-
- // Create the font info
- AutoCFRelease<CFStringRef> cfFontName(
- CFStringCreateWithCString(NULL, familyName, kCFStringEncodingUTF8));
-
- AutoCFRelease<CFNumberRef> cfFontTraits(
- CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
-
- AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
-
- AutoCFRelease<CFMutableDictionaryRef> cfTraits(
- CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
-
- // Create the font
- if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
- CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
-
- CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
- CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
-
- AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
- 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);
- }
- }
- }
-
- return ctFont ? NewFromFontRef(ctFont, familyName) : NULL;
-}
-
-static CTFontRef GetFontRefFromFontID(SkFontID fontID) {
- SkTypeface_Mac* face = reinterpret_cast<SkTypeface_Mac*>(SkTypefaceCache::FindByID(fontID));
- return face ? face->fFontRef.get() : NULL;
-}
-
-static SkTypeface* GetDefaultFace() {
- SK_DECLARE_STATIC_MUTEX(gMutex);
- SkAutoMutexAcquire ma(gMutex);
-
- static SkTypeface* gDefaultFace;
-
- if (NULL == gDefaultFace) {
- gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal);
- SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal);
- }
- return gDefaultFace;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
-CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
- const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
- return macface ? macface->fFontRef.get() : NULL;
-}
-
-/* This function is visible on the outside. It first searches the cache, and if
- * not found, returns a new entry (after adding it to the cache).
- */
-SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
- SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
- SkTypeface* face = SkTypefaceCache::FindByID(fontID);
- if (face) {
- face->ref();
- } else {
- face = NewFromFontRef(fontRef, NULL);
- SkTypefaceCache::Add(face, face->style());
- // NewFromFontRef doesn't retain the parameter, but the typeface it
- // creates does release it in its destructor, so we balance that with
- // a retain call here.
- CFRetain(fontRef);
- }
- SkASSERT(face->getRefCnt() > 1);
- return face;
-}
-
-struct NameStyleRec {
- const char* fName;
- SkTypeface::Style fStyle;
-};
-
-static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style,
- void* ctx) {
- const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face);
- const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx);
-
- return rec->fStyle == style && mface->fName.equals(rec->fName);
-}
-
-static const char* map_css_names(const char* name) {
- static const struct {
- const char* fFrom; // name the caller specified
- const char* fTo; // "canonical" name we map to
- } gPairs[] = {
- { "sans-serif", "Helvetica" },
- { "serif", "Times" },
- { "monospace", "Courier" }
- };
-
- for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
- if (strcmp(name, gPairs[i].fFrom) == 0) {
- return gPairs[i].fTo;
- }
- }
- return name; // no change
-}
-
-SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
- const char familyName[],
- SkTypeface::Style style) {
- if (familyName) {
- familyName = map_css_names(familyName);
- }
-
- // Clone an existing typeface
- // TODO: only clone if style matches the familyFace's style...
- if (familyName == NULL && familyFace != NULL) {
- familyFace->ref();
- return const_cast<SkTypeface*>(familyFace);
- }
-
- if (!familyName || !*familyName) {
- familyName = FONT_DEFAULT_NAME;
- }
-
- NameStyleRec rec = { familyName, style };
- SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec);
-
- if (NULL == face) {
- face = NewFromName(familyName, style);
- if (face) {
- SkTypefaceCache::Add(face, style);
- } else {
- face = GetDefaultFace();
- face->ref();
- }
- }
- return face;
-}
-
-static void flip(SkMatrix* matrix) {
- matrix->setSkewX(-matrix->getSkewX());
- matrix->setSkewY(-matrix->getSkewY());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct GlyphRect {
- int16_t fMinX;
- int16_t fMinY;
- int16_t fMaxX;
- int16_t fMaxY;
-};
-
-class SkScalerContext_Mac : public SkScalerContext {
-public:
- SkScalerContext_Mac(const SkDescriptor* desc);
- virtual ~SkScalerContext_Mac(void);
-
-
-protected:
- unsigned generateGlyphCount(void) SK_OVERRIDE;
- uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
- void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
- void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
- void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
- void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
- void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
-
-private:
- static void CTPathElement(void *info, const CGPathElement *element);
- uint16_t getFBoundingBoxesGlyphOffset();
- void getVerticalOffset(CGGlyph glyphID, SkIPoint* 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
- Offscreen fOffscreen;
- AutoCFRelease<CTFontRef> fCTFont;
- AutoCFRelease<CTFontRef> fCTVerticalFont; // for vertical advance
- AutoCFRelease<CGFontRef> fCGFont;
- GlyphRect* fFBoundingBoxes;
- uint16_t fFBoundingBoxesGlyphOffset;
- uint16_t fGlyphCount;
- bool fGeneratedFBoundingBoxes;
- bool fDoSubPosition;
- bool fVertical;
-
- friend class Offscreen;
-};
-
-SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
- : SkScalerContext(desc)
- , fFBoundingBoxes(NULL)
- , fFBoundingBoxesGlyphOffset(0)
- , fGeneratedFBoundingBoxes(false)
-{
- CTFontRef ctFont = GetFontRefFromFontID(fRec.fFontID);
- 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);
- AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
- if (fVertical) {
- AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
- kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
- if (cfAttributes) {
- CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
- AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
- kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
- CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
- ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
- }
- }
- fCTFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &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);
- }
- fGlyphCount = SkToU16(numGlyphs);
- fDoSubPosition = SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
-}
-
-SkScalerContext_Mac::~SkScalerContext_Mac() {
- delete[] fFBoundingBoxes;
-}
-
-CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
- CGGlyph glyphID, size_t* rowBytesPtr,
- bool generateA8FromLCD) {
- 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))
- //and smoothed text is always g=2.0.
- fRGBSpace = CGColorSpaceCreateDeviceRGB();
- }
-
- // default to kBW_Format
- bool doAA = false;
- bool doLCD = false;
-
- if (SkMask::kBW_Format != glyph.fMaskFormat) {
- doLCD = true;
- doAA = true;
- }
-
- // FIXME: lcd smoothed un-hinted rasterization unsupported.
- if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
- doLCD = false;
- doAA = true;
- }
-
- size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
- if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
- if (fSize.fWidth < glyph.fWidth) {
- fSize.fWidth = RoundSize(glyph.fWidth);
- }
- if (fSize.fHeight < glyph.fHeight) {
- fSize.fHeight = RoundSize(glyph.fHeight);
- }
-
- rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
- void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
- fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
- rowBytes, fRGBSpace, BITMAP_INFO_RGB);
-
- // skia handles quantization itself, so we disable this for cg to get
- // full fractional data from them.
- CGContextSetAllowsFontSubpixelQuantization(fCG, false);
- CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
-
- CGContextSetTextDrawingMode(fCG, kCGTextFill);
- CGContextSetFont(fCG, context.fCGFont);
- CGContextSetFontSize(fCG, 1);
- CGContextSetTextMatrix(fCG, context.fTransform);
-
- CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition);
- CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition);
-
- // Draw white on black to create mask.
- // TODO: Draw black on white and invert, CG has a special case codepath.
- CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
-
- // force our checks below to happen
- fDoAA = !doAA;
- fDoLCD = !doLCD;
- }
-
- if (fDoAA != doAA) {
- CGContextSetShouldAntialias(fCG, doAA);
- fDoAA = doAA;
- }
- if (fDoLCD != doLCD) {
- CGContextSetShouldSmoothFonts(fCG, doLCD);
- fDoLCD = doLCD;
- }
-
- CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
- // skip rows based on the glyph's height
- image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
-
- // erase to black
- sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
-
- float subX = 0;
- float subY = 0;
- if (context.fDoSubPosition) {
- subX = SkFixedToFloat(glyph.getSubXFixed());
- subY = SkFixedToFloat(glyph.getSubYFixed());
- }
- if (context.fVertical) {
- SkIPoint offset;
- context.getVerticalOffset(glyphID, &offset);
- subX += offset.fX;
- subY += offset.fY;
- }
- CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
- glyph.fTop + glyph.fHeight - subY,
- &glyphID, 1);
-
- SkASSERT(rowBytesPtr);
- *rowBytesPtr = rowBytes;
- 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);
- }
- offset->fX = SkScalarRound(floatOffset.fX);
- offset->fY = SkScalarRound(floatOffset.fY);
-}
-
-uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
- if (fFBoundingBoxesGlyphOffset) {
- return fFBoundingBoxesGlyphOffset;
- }
- fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
- AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
- if (hheaTable.fData) {
- fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
- }
- return fFBoundingBoxesGlyphOffset;
-}
-
-/*
- * Lion has a bug in CTFontGetBoundingRectsForGlyphs which returns a bad value
- * in theBounds.origin.x for fonts whose numOfLogHorMetrics is less than its
- * glyph count. This workaround reads the glyph bounds from the font directly.
- *
- * The table is computed only if the font is a TrueType font, if the glyph
- * value is >= fFBoundingBoxesGlyphOffset. (called only if fFBoundingBoxesGlyphOffset < fGlyphCount).
- *
- * TODO: A future optimization will compute fFBoundingBoxes once per CGFont, and
- * compute fFBoundingBoxesMatrix once per font context.
- */
-bool SkScalerContext_Mac::generateBBoxes() {
- if (fGeneratedFBoundingBoxes) {
- return NULL != fFBoundingBoxes;
- }
- fGeneratedFBoundingBoxes = true;
-
- AutoCGTable<SkOTTableHead> headTable(fCGFont);
- if (!headTable.fData) {
- return false;
- }
-
- AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
- if (!locaTable.fData) {
- return false;
- }
-
- AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
- if (!glyfTable.fData) {
- return false;
- }
-
- uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
- fFBoundingBoxes = new GlyphRect[entries];
-
- SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
- SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
- glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
- for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
- const SkOTTableGlyphData* glyphData = glyphDataIter.next();
- GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
- rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
- rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
- 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;
-}
-
-unsigned SkScalerContext_Mac::generateGlyphCount(void) {
- return fGlyphCount;
-}
-
-uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
- CGGlyph cgGlyph;
- UniChar theChar;
-
- // Validate our parameters and state
- SkASSERT(uni <= 0x0000FFFF);
- SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
-
- // Get the glyph
- theChar = (UniChar) uni;
-
- if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1)) {
- cgGlyph = 0;
- }
-
- return cgGlyph;
-}
-
-void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
- this->generateMetrics(glyph);
-}
-
-void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
- CGSize advance;
- CGRect bounds;
- CGGlyph cgGlyph;
-
- // Get the state we need
- cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
-
- if (fVertical) {
- if (!isSnowLeopard()) {
- // Lion and Leopard respect the vertical font metrics.
- CTFontGetBoundingRectsForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
- &cgGlyph, &bounds, 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);
- }
- CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
- &cgGlyph, &advance, 1);
- } else {
- CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
- &cgGlyph, &bounds, 1);
- CTFontGetAdvancesForGlyphs(fCTFont, kCTFontDefaultOrientation,
- &cgGlyph, &advance, 1);
- }
-
- // 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) {
- AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
- if (NULL == path || CGPathIsEmpty(path)) {
- bounds = CGRectMake(0, 0, 0, 0);
- }
- }
-
- glyph->zeroMetrics();
- glyph->fAdvanceX = SkFloatToFixed_Check(advance.width);
- glyph->fAdvanceY = -SkFloatToFixed_Check(advance.height);
-
- if (CGRectIsEmpty_inline(bounds)) {
- 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;
- 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;
- }
- // 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.
- getVerticalOffset(cgGlyph, &offset);
- glyph->fLeft += offset.fX;
- glyph->fTop += offset.fY;
- }
-}
-
-#include "SkColorPriv.h"
-
-static void build_power_table(uint8_t table[], float ee) {
- for (int i = 0; i < 256; i++) {
- float x = i / 255.f;
- x = sk_float_pow(x, ee);
- int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
- table[i] = SkToU8(xx);
- }
-}
-
-/**
- * This will invert the gamma applied by CoreGraphics, so we can get linear
- * values.
- *
- * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
- * The color space used does not appear to affect this choice.
- */
-static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
- static bool gInited;
- static uint8_t gTableCoreGraphicsSmoothing[256];
- if (!gInited) {
- build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
- gInited = true;
- }
- return gTableCoreGraphicsSmoothing;
-}
-
-static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
- while (count > 0) {
- uint8_t mask = 0;
- for (int i = 7; i >= 0; --i) {
- mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
- if (0 == --count) {
- break;
- }
- }
- *dst++ = mask;
- }
-}
-
-template<bool APPLY_PREBLEND>
-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);
-}
-template<bool APPLY_PREBLEND>
-static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
- const SkGlyph& glyph, const uint8_t* table8) {
- const int width = glyph.fWidth;
- size_t dstRB = glyph.rowBytes();
- uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
-
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int i = 0; i < width; ++i) {
- dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
- }
- cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
- dst += dstRB;
- }
-}
-
-template<bool APPLY_PREBLEND>
-static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
- const uint8_t* tableG,
- const uint8_t* tableB) {
- 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);
- return SkPack888ToRGB16(r, g, b);
-}
-template<bool APPLY_PREBLEND>
-static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
- const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
- const int width = glyph.fWidth;
- size_t dstRB = glyph.rowBytes();
- uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
-
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int i = 0; i < width; i++) {
- dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
- }
- cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
- dst = (uint16_t*)((char*)dst + dstRB);
- }
-}
-
-template<bool APPLY_PREBLEND>
-static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
- const uint8_t* tableG,
- const uint8_t* tableB) {
- 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);
- return SkPackARGB32(0xFF, r, g, b);
-}
-template<bool APPLY_PREBLEND>
-static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
- const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
- const int width = glyph.fWidth;
- size_t dstRB = glyph.rowBytes();
- uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
- for (int y = 0; y < glyph.fHeight; y++) {
- for (int i = 0; i < width; i++) {
- dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
- }
- cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
- dst = (uint32_t*)((char*)dst + dstRB);
- }
-}
-
-template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
- return (T*)((char*)ptr + byteOffset);
-}
-
-void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
- CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
-
- // FIXME: lcd smoothed un-hinted rasterization unsupported.
- bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
-
- // Draw the glyph
- size_t cgRowBytes;
- CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
- if (cgPixels == NULL) {
- return;
- }
-
- //TODO: see if drawing black on white and inverting is faster (at least in
- //lcd case) as core graphics appears to have special case code for drawing
- //black text.
-
- // Fix the glyph
- const bool isLCD = isLCDFormat(glyph.fMaskFormat);
- if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
- const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
-
- //Note that the following cannot really be integrated into the
- //pre-blend, since we may not be applying the pre-blend; when we aren't
- //applying the pre-blend it means that a filter wants linear anyway.
- //Other code may also be applying the pre-blend, so we'd need another
- //one with this and one without.
- CGRGBPixel* addr = cgPixels;
- for (int y = 0; y < glyph.fHeight; ++y) {
- for (int x = 0; x < glyph.fWidth; ++x) {
- int r = (addr[x] >> 16) & 0xFF;
- int g = (addr[x] >> 8) & 0xFF;
- int b = (addr[x] >> 0) & 0xFF;
- addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
- }
- addr = SkTAddByteOffset(addr, cgRowBytes);
- }
- }
-
- // Convert glyph to mask
- switch (glyph.fMaskFormat) {
- case SkMask::kLCD32_Format: {
- if (fPreBlend.isApplicable()) {
- rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
- fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
- } else {
- rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
- fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
- }
- } break;
- case SkMask::kLCD16_Format: {
- if (fPreBlend.isApplicable()) {
- rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
- fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
- } else {
- rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
- fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
- }
- } break;
- case SkMask::kA8_Format: {
- if (fPreBlend.isApplicable()) {
- rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
- } else {
- rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
- }
- } break;
- case SkMask::kBW_Format: {
- const int width = glyph.fWidth;
- size_t dstRB = glyph.rowBytes();
- uint8_t* dst = (uint8_t*)glyph.fImage;
- for (int y = 0; y < glyph.fHeight; y++) {
- cgpixels_to_bits(dst, cgPixels, width);
- cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
- dst += dstRB;
- }
- } break;
- default:
- SkDEBUGFAIL("unexpected mask format");
- break;
- }
-}
-
-/*
- * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
- * seems sufficient, and possibly even correct, to allow the hinted outline
- * to be subpixel positioned.
- */
-#define kScaleForSubPixelPositionHinting (4.0f)
-
-void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
- CTFontRef font = fCTFont;
- SkScalar scaleX = SK_Scalar1;
- SkScalar scaleY = SK_Scalar1;
-
- /*
- * For subpixel positioning, we want to return an unhinted outline, so it
- * can be positioned nicely at fractional offsets. However, we special-case
- * if the baseline of the (horizontal) text is axis-aligned. In those cases
- * we want to retain hinting in the direction orthogonal to the baseline.
- * e.g. for horizontal baseline, we want to retain hinting in Y.
- * The way we remove hinting is to scale the font by some value (4) in that
- * direction, ask for the path, and then scale the path back down.
- */
- if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
- SkMatrix m;
- fRec.getSingleMatrix(&m);
-
- // start out by assuming that we want no hining in X and Y
- scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
- // now see if we need to restore hinting for axis-aligned baselines
- switch (SkComputeAxisAlignmentForHText(m)) {
- case kX_SkAxisAlignment:
- scaleY = SK_Scalar1; // want hinting in the Y direction
- break;
- case kY_SkAxisAlignment:
- scaleX = SK_Scalar1; // want hinting in the X direction
- break;
- default:
- break;
- }
-
- CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
- // need to release font when we're done
- font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
- }
-
- CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
- AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
-
- path->reset();
- if (cgPath != NULL) {
- CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
- }
-
- if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
- 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;
- getVerticalOffset(cgGlyph, &offset);
- path->offset(SkIntToScalar(offset.fX), SkIntToScalar(offset.fY));
- }
-}
-
-void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
- SkPaint::FontMetrics* my) {
- CGRect theBounds = CTFontGetBoundingBox(fCTFont);
-
- SkPaint::FontMetrics theMetrics;
- theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
- theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
- theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont));
- theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
- theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont));
- theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
- theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
- theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
- theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
-
- if (mx != NULL) {
- *mx = theMetrics;
- }
- if (my != NULL) {
- *my = theMetrics;
- }
-}
-
-void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
- SkPath* skPath = (SkPath*)info;
-
- // Process the path element
- switch (element->type) {
- case kCGPathElementMoveToPoint:
- skPath->moveTo(element->points[0].x, -element->points[0].y);
- break;
-
- case kCGPathElementAddLineToPoint:
- skPath->lineTo(element->points[0].x, -element->points[0].y);
- break;
-
- case kCGPathElementAddQuadCurveToPoint:
- skPath->quadTo(element->points[0].x, -element->points[0].y,
- element->points[1].x, -element->points[1].y);
- break;
-
- case kCGPathElementAddCurveToPoint:
- skPath->cubicTo(element->points[0].x, -element->points[0].y,
- element->points[1].x, -element->points[1].y,
- element->points[2].x, -element->points[2].y);
- break;
-
- case kCGPathElementCloseSubpath:
- skPath->close();
- break;
-
- default:
- SkDEBUGFAIL("Unknown path element!");
- break;
- }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-// Returns NULL on failure
-// Call must still manage its ownership of provider
-static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
- AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
- if (NULL == cg) {
- return NULL;
- }
- CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
- return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
-}
-
-SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
- AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
- if (NULL == provider) {
- return NULL;
- }
- return create_from_dataProvider(provider);
-}
-
-SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
- AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
- if (NULL == provider) {
- return NULL;
- }
- return create_from_dataProvider(provider);
-}
-
-// Web fonts added to the the CTFont registry do not return their character set.
-// Iterate through the font in this case. The existing caller caches the result,
-// so the performance impact isn't too bad.
-static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
- SkTDArray<SkUnichar>* glyphToUnicode) {
- glyphToUnicode->setCount(glyphCount);
- SkUnichar* out = glyphToUnicode->begin();
- sk_bzero(out, glyphCount * sizeof(SkUnichar));
- UniChar unichar = 0;
- while (glyphCount > 0) {
- CGGlyph glyph;
- if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
- out[glyph] = unichar;
- --glyphCount;
- }
- if (++unichar == 0) {
- break;
- }
- }
-}
-
-// Construct Glyph to Unicode table.
-// Unicode code points that require conjugate pairs in utf16 are not
-// supported.
-static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
- SkTDArray<SkUnichar>* glyphToUnicode) {
- AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
- if (!charSet) {
- populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
- return;
- }
-
- AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
- charSet));
- if (!bitmap) {
- return;
- }
- CFIndex length = CFDataGetLength(bitmap);
- if (!length) {
- return;
- }
- if (length > 8192) {
- // TODO: Add support for Unicode above 0xFFFF
- // Consider only the BMP portion of the Unicode character points.
- // The bitmap may contain other planes, up to plane 16.
- // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
- length = 8192;
- }
- const UInt8* bits = CFDataGetBytePtr(bitmap);
- glyphToUnicode->setCount(glyphCount);
- SkUnichar* out = glyphToUnicode->begin();
- sk_bzero(out, glyphCount * sizeof(SkUnichar));
- for (int i = 0; i < length; i++) {
- int mask = bits[i];
- if (!mask) {
- continue;
- }
- for (int j = 0; j < 8; j++) {
- CGGlyph glyph;
- UniChar unichar = static_cast<UniChar>((i << 3) + j);
- if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
- out[glyph] = unichar;
- }
- }
- }
-}
-
-static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
- CGSize advance;
- advance.width = 0;
- CGGlyph glyph = gId;
- CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
- *data = sk_float_round2int(advance.width);
- return true;
-}
-
-// we might move this into our CGUtils...
-static void CFStringToSkString(CFStringRef src, SkString* dst) {
- // Reserve enough room for the worst-case string,
- // plus 1 byte for the trailing null.
- CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
- kCFStringEncodingUTF8) + 1;
- dst->resize(length);
- CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
- // Resize to the actual UTF-8 length used, stripping the null character.
- dst->resize(strlen(dst->c_str()));
-}
-
-// static
-SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
- uint32_t fontID,
- SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
- const uint32_t* glyphIDs,
- uint32_t glyphIDsCount) {
- CTFontRef originalCTFont = GetFontRefFromFontID(fontID);
- AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
- originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
- SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
-
- {
- AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
- CFStringToSkString(fontName, &info->fFontName);
- }
-
- info->fMultiMaster = false;
- CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
- info->fLastGlyphID = SkToU16(glyphCount - 1);
- info->fEmSize = CTFontGetUnitsPerEm(ctFont);
-
- if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
- populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
- }
-
- info->fStyle = 0;
-
- // If it's not a truetype font, mark it as 'other'. Assume that TrueType
- // fonts always have both glyf and loca tables. At the least, this is what
- // sfntly needs to subset the font. CTFontCopyAttribute() does not always
- // succeed in determining this directly.
- if (!GetTableSize(fontID, 'glyf') || !GetTableSize(fontID, 'loca')) {
- info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
- info->fItalicAngle = 0;
- info->fAscent = 0;
- info->fDescent = 0;
- info->fStemV = 0;
- info->fCapHeight = 0;
- info->fBBox = SkIRect::MakeEmpty();
- return info;
- }
-
- info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
- CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
- if (symbolicTraits & kCTFontMonoSpaceTrait) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
- }
- if (symbolicTraits & kCTFontItalicTrait) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
- }
- CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
- if (stylisticClass & kCTFontSymbolicClass) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
- }
- if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
- } else if (stylisticClass & kCTFontScriptsClass) {
- info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
- }
- info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
- info->fAscent = (int16_t) CTFontGetAscent(ctFont);
- info->fDescent = (int16_t) CTFontGetDescent(ctFont);
- info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
- CGRect bbox = CTFontGetBoundingBox(ctFont);
-
- SkRect r;
- r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
- CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
- CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
- CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
-
- r.roundOut(&(info->fBBox));
-
- // Figure out a good guess for StemV - Min width of i, I, !, 1.
- // This probably isn't very good with an italic font.
- int16_t min_width = SHRT_MAX;
- info->fStemV = 0;
- static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
- const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
- CGGlyph glyphs[count];
- CGRect boundingRects[count];
- if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
- CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
- glyphs, boundingRects, count);
- for (size_t i = 0; i < count; i++) {
- int16_t width = (int16_t) boundingRects[i].size.width;
- if (width > 0 && width < min_width) {
- min_width = width;
- info->fStemV = min_width;
- }
- }
- }
-
- if (false) { // TODO: haven't figured out how to know if font is embeddable
- // (information is in the OS/2 table)
- info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
- } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
- if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
- skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
- info->fGlyphWidths->fAdvance.append(1, &min_width);
- skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
- SkAdvancedTypefaceMetrics::WidthRange::kDefault);
- } else {
- info->fGlyphWidths.reset(
- skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
- glyphCount,
- glyphIDs,
- glyphIDsCount,
- &getWidthAdvance));
- }
- }
- return info;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SK_SFNT_ULONG get_font_type_tag(SkFontID uniqueID) {
- CTFontRef ctFont = GetFontRefFromFontID(uniqueID);
- AutoCFRelease<CFNumberRef> fontFormatRef(
- static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
- if (!fontFormatRef) {
- return 0;
- }
-
- SInt32 fontFormatValue;
- if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
- return 0;
- }
-
- switch (fontFormatValue) {
- case kCTFontFormatOpenTypePostScript:
- return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
- case kCTFontFormatOpenTypeTrueType:
- return SkSFNTHeader::fontType_WindowsTrueType::TAG;
- case kCTFontFormatTrueType:
- return SkSFNTHeader::fontType_MacTrueType::TAG;
- case kCTFontFormatPostScript:
- return SkSFNTHeader::fontType_PostScript::TAG;
- case kCTFontFormatBitmap:
- return SkSFNTHeader::fontType_MacTrueType::TAG;
- case kCTFontFormatUnrecognized:
- default:
- //CT seems to be unreliable in being able to obtain the type,
- //even if all we want is the first four bytes of the font resource.
- //Just the presence of the FontForge 'FFTM' table seems to throw it off.
- return SkSFNTHeader::fontType_WindowsTrueType::TAG;
- }
-}
-
-SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
- SK_SFNT_ULONG fontType = get_font_type_tag(uniqueID);
- if (0 == fontType) {
- return NULL;
- }
-
- // get table tags
- int numTables = CountTables(uniqueID);
- SkTDArray<SkFontTableTag> tableTags;
- tableTags.setCount(numTables);
- GetTableTags(uniqueID, tableTags.begin());
-
- // calc total size for font, save sizes
- SkTDArray<size_t> tableSizes;
- size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
- for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
- size_t tableSize = GetTableSize(uniqueID, tableTags[tableIndex]);
- totalSize += (tableSize + 3) & ~3;
- *tableSizes.append() = tableSize;
- }
-
- // reserve memory for stream, and zero it (tables must be zero padded)
- SkMemoryStream* stream = new SkMemoryStream(totalSize);
- char* dataStart = (char*)stream->getMemoryBase();
- sk_bzero(dataStart, totalSize);
- char* dataPtr = dataStart;
-
- // compute font header entries
- uint16_t entrySelector = 0;
- uint16_t searchRange = 1;
- while (searchRange < numTables >> 1) {
- entrySelector++;
- searchRange <<= 1;
- }
- searchRange <<= 4;
- uint16_t rangeShift = (numTables << 4) - searchRange;
-
- // write font header
- SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
- header->fontType = fontType;
- header->numTables = SkEndian_SwapBE16(numTables);
- header->searchRange = SkEndian_SwapBE16(searchRange);
- header->entrySelector = SkEndian_SwapBE16(entrySelector);
- header->rangeShift = SkEndian_SwapBE16(rangeShift);
- dataPtr += sizeof(SkSFNTHeader);
-
- // write tables
- SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
- dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
- for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
- size_t tableSize = tableSizes[tableIndex];
- GetTableData(uniqueID, tableTags[tableIndex], 0, tableSize, dataPtr);
- entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
- entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
- tableSize));
- entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
- entry->logicalLength = SkEndian_SwapBE32(tableSize);
-
- dataPtr += (tableSize + 3) & ~3;
- ++entry;
- }
-
- return stream;
-}
-
-size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) {
- SkDEBUGFAIL("SkFontHost::GetFileName unimplemented");
- return 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkStream.h"
-
-void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
- SkFontDescriptor desc;
- face->onGetFontDescriptor(&desc);
-
- desc.serialize(stream);
-
- // by convention, we also write out the actual sfnt data, preceeded by
- // a packed-length. For now we skip that, so we just write the zero.
- stream->writePackedUInt(0);
-}
-
-SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
- SkFontDescriptor desc(stream);
-
- // by convention, Serialize will have also written the actual sfnt data.
- // for now, we just want to skip it.
- size_t size = stream->readPackedUInt();
- stream->skip(size);
-
- return SkFontHost::CreateTypeface(NULL, desc.getFamilyName(), desc.getStyle());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-// DEPRECATED
-SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
- return new SkScalerContext_Mac(desc);
-}
-
-// DEPRECATED
-SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
- SkFontID nextFontID = 0;
- SkTypeface* face = GetDefaultFace();
- if (face->uniqueID() != currFontID) {
- nextFontID = face->uniqueID();
- }
- return nextFontID;
-}
-
-// DEPRECATED
-void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface* face) {
- face->onFilterRec(rec);
-}
-
-// DEPRECATED
-int SkFontHost::CountTables(SkFontID fontID) {
- SkTypeface* face = SkTypefaceCache::FindByID(fontID);
- return face ? face->onGetTableTags(NULL) : 0;
-}
-
-// DEPRECATED
-int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
- SkTypeface* face = SkTypefaceCache::FindByID(fontID);
- return face ? face->onGetTableTags(tags) : 0;
-}
-
-// DEPRECATED
-size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
- SkTypeface* face = SkTypefaceCache::FindByID(fontID);
- return face ? face->onGetTableData(tag, 0, ~0U, NULL) : 0;
-}
-
-// DEPRECATED
-size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
- size_t offset, size_t length, void* dst) {
- SkTypeface* face = SkTypefaceCache::FindByID(fontID);
- return face ? face->onGetTableData(tag, offset, length, dst) : 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-int SkTypeface_Mac::onGetUPEM() const {
- AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
- return CGFontGetUnitsPerEm(cgFont);
-}
-
-// If, as is the case with web fonts, the CTFont data isn't available,
-// the CGFont data may work. While the CGFont may always provide the
-// right result, leave the CTFont code path to minimize disruption.
-static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
- CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
- kCTFontTableOptionNoOptions);
- if (NULL == data) {
- AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
- data = CGFontCopyTableForTag(cgFont, tag);
- }
- return data;
-}
-
-int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
- AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
- kCTFontTableOptionNoOptions));
- if (NULL == cfArray) {
- return 0;
- }
- int count = CFArrayGetCount(cfArray);
- if (tags) {
- for (int i = 0; i < count; ++i) {
- uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
- tags[i] = static_cast<SkFontTableTag>(fontTag);
- }
- }
- return count;
-}
-
-size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
- size_t length, void* dstData) const {
- AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
- if (NULL == srcData) {
- return 0;
- }
-
- size_t srcSize = CFDataGetLength(srcData);
- if (offset >= srcSize) {
- return 0;
- }
- if (length > srcSize - offset) {
- length = srcSize - offset;
- }
- if (dstData) {
- memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
- }
- return length;
-}
-
-SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
- return new SkScalerContext_Mac(desc);
-}
-
-void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
- unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
- SkScalerContext::kAutohinting_Flag;
-
- rec->fFlags &= ~flagsWeDontSupport;
-
- bool lcdSupport = supports_LCD();
-
- // Only two levels of hinting are supported.
- // kNo_Hinting means avoid CoreGraphics outline dilation.
- // kNormal_Hinting means CoreGraphics outline dilation is allowed.
- // If there is no lcd support, hinting (dilation) cannot be supported.
- SkPaint::Hinting hinting = rec->getHinting();
- if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
- hinting = SkPaint::kNo_Hinting;
- } else if (SkPaint::kFull_Hinting == hinting) {
- hinting = SkPaint::kNormal_Hinting;
- }
- rec->setHinting(hinting);
-
- // FIXME: lcd smoothed un-hinted rasterization unsupported.
- // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
- // There is no current means to honor a request for unhinted lcd,
- // so arbitrarilly ignore the hinting request and honor lcd.
-
- // Hinting and smoothing should be orthogonal, but currently they are not.
- // CoreGraphics has no API to influence hinting. However, its lcd smoothed
- // output is drawn from auto-dilated outlines (the amount of which is
- // determined by AppleFontSmoothing). Its regular anti-aliased output is
- // drawn from un-dilated outlines.
-
- // The behavior of Skia is as follows:
- // [AA][no-hint]: generate AA using CoreGraphic's AA output.
- // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
- // channel. This matches [LCD][yes-hint] in weight.
- // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
- // Currenly side with LCD, effectively ignoring the hinting setting.
- // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
-
- if (isLCDFormat(rec->fMaskFormat)) {
- if (lcdSupport) {
- //CoreGraphics creates 555 masks for smoothed text anyway.
- rec->fMaskFormat = SkMask::kLCD16_Format;
- rec->setHinting(SkPaint::kNormal_Hinting);
- } else {
- rec->fMaskFormat = SkMask::kA8_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) {
-#ifndef SK_GAMMA_APPLY_TO_A8
- rec->ignorePreBlend();
-#endif
- } else {
- //CoreGraphics dialates smoothed text as needed.
- rec->setContrast(0);
- }
-}
-
-// we take ownership of the ref
-static const char* get_str(CFStringRef ref, SkString* str) {
- CFStringToSkString(ref, str);
- CFSafeRelease(ref);
- return str->c_str();
-}
-
-void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc) const {
- this->INHERITED::onGetFontDescriptor(desc);
- SkString tmpStr;
-
- desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
- desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
- desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
-}
-