blob: 987f18510104f3fce341ec541e1fc7a7b4626f19 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com0680d6c2008-12-19 19:46:15 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00009#include <vector>
10#ifdef SK_BUILD_FOR_MAC
11#import <ApplicationServices/ApplicationServices.h>
12#endif
reed@android.com0680d6c2008-12-19 19:46:15 +000013
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000014#ifdef SK_BUILD_FOR_IOS
15#include <CoreText/CoreText.h>
reed@google.com3dcbd462013-03-27 13:56:34 +000016#include <CoreText/CTFontManager.h>
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000017#include <CoreGraphics/CoreGraphics.h>
18#include <CoreFoundation/CoreFoundation.h>
19#endif
20
21#include "SkFontHost.h"
22#include "SkCGUtils.h"
23#include "SkColorPriv.h"
24#include "SkDescriptor.h"
25#include "SkEndian.h"
26#include "SkFontDescriptor.h"
27#include "SkFloatingPoint.h"
28#include "SkGlyph.h"
29#include "SkMaskGamma.h"
30#include "SkSFNTHeader.h"
31#include "SkOTTable_glyf.h"
32#include "SkOTTable_head.h"
33#include "SkOTTable_hhea.h"
34#include "SkOTTable_loca.h"
35#include "SkOTUtils.h"
36#include "SkPaint.h"
37#include "SkPath.h"
38#include "SkString.h"
39#include "SkStream.h"
40#include "SkThread.h"
41#include "SkTypeface_mac.h"
42#include "SkUtils.h"
43#include "SkTypefaceCache.h"
reed@google.comce8b3de2013-03-26 19:30:16 +000044#include "SkFontMgr.h"
reed@google.combcb42ae2013-07-02 13:56:39 +000045#include "SkUtils.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000046
reed@google.comf77b35d2013-05-02 20:39:44 +000047//#define HACK_COLORGLYPHS
48
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000049class SkScalerContext_Mac;
50
reed@google.com3dcbd462013-03-27 13:56:34 +000051// CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
52// provide a wrapper here that will return an empty array if need be.
53static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() {
54#ifdef SK_BUILD_FOR_IOS
55 return CFArrayCreate(NULL, NULL, 0, NULL);
56#else
57 return CTFontManagerCopyAvailableFontFamilyNames();
58#endif
59}
60
61
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000062// Being templated and taking const T* prevents calling
63// CFSafeRelease(autoCFRelease) through implicit conversion.
64template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
65 if (cfTypeRef) {
66 CFRelease(cfTypeRef);
67 }
68}
69
70// Being templated and taking const T* prevents calling
71// CFSafeRetain(autoCFRelease) through implicit conversion.
72template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
73 if (cfTypeRef) {
74 CFRetain(cfTypeRef);
75 }
76}
77
78/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
79template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
80public:
81 explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
82 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
83
84 void reset(CFRef that = NULL) {
85 CFSafeRetain(that);
86 CFSafeRelease(fCFRef);
87 fCFRef = that;
88 }
89
90 AutoCFRelease& operator =(CFRef that) {
91 reset(that);
92 return *this;
93 }
94
95 operator CFRef() const { return fCFRef; }
96 CFRef get() const { return fCFRef; }
97
bungeman@google.coma9802692013-08-07 02:45:25 +000098 CFRef* operator&() { SkASSERT(fCFRef == NULL); return &fCFRef; }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000099private:
100 CFRef fCFRef;
101};
102
reed@google.com964988f2013-03-29 14:57:22 +0000103static CFStringRef make_CFString(const char str[]) {
104 return CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
105}
106
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000107template<typename T> class AutoCGTable : SkNoncopyable {
108public:
109 AutoCGTable(CGFontRef font)
110 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
111 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
112 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
113 { }
114
115 const T* operator->() const { return fData; }
116
117private:
118 AutoCFRelease<CFDataRef> fCFData;
119public:
120 const T* fData;
121};
122
123// inline versions of these rect helpers
124
125static bool CGRectIsEmpty_inline(const CGRect& rect) {
126 return rect.size.width <= 0 || rect.size.height <= 0;
127}
128
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000129static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
130 return rect.origin.x;
131}
132
133static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
134 return rect.origin.x + rect.size.width;
135}
136
137static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
138 return rect.origin.y;
139}
140
141static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
142 return rect.origin.y + rect.size.height;
143}
144
145static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
146 return rect.size.width;
147}
148
149///////////////////////////////////////////////////////////////////////////////
150
151static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
152 size_t width, size_t height, size_t rowBytes) {
153 SkASSERT(width);
154 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
155
156 if (width >= 32) {
157 while (height) {
158 sk_memset32(ptr, value, width);
159 ptr = (uint32_t*)((char*)ptr + rowBytes);
160 height -= 1;
161 }
162 return;
163 }
164
165 rowBytes -= width * sizeof(uint32_t);
166
167 if (width >= 8) {
168 while (height) {
169 int w = width;
170 do {
171 *ptr++ = value; *ptr++ = value;
172 *ptr++ = value; *ptr++ = value;
173 *ptr++ = value; *ptr++ = value;
174 *ptr++ = value; *ptr++ = value;
175 w -= 8;
176 } while (w >= 8);
177 while (--w >= 0) {
178 *ptr++ = value;
179 }
180 ptr = (uint32_t*)((char*)ptr + rowBytes);
181 height -= 1;
182 }
183 } else {
184 while (height) {
185 int w = width;
186 do {
187 *ptr++ = value;
188 } while (--w > 0);
189 ptr = (uint32_t*)((char*)ptr + rowBytes);
190 height -= 1;
191 }
192 }
193}
194
195#include <sys/utsname.h>
196
197typedef uint32_t CGRGBPixel;
198
199static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
200 return pixel & 0xFF;
201}
202
203// The calls to support subpixel are present in 10.5, but are not included in
204// the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are
205// included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for
206// instance, is present in the 10.5 CoreGraphics libary, use:
207// cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/
208// cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/
209// nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts
210
211#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
212CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value);
213CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value);
214CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value);
215CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value);
216CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value);
217#endif
218
219static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
220
221// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
222static int readVersion() {
223 struct utsname info;
224 if (uname(&info) != 0) {
225 SkDebugf("uname failed\n");
226 return 0;
227 }
228 if (strcmp(info.sysname, "Darwin") != 0) {
229 SkDebugf("unexpected uname sysname %s\n", info.sysname);
230 return 0;
231 }
232 char* dot = strchr(info.release, '.');
233 if (!dot) {
234 SkDebugf("expected dot in uname release %s\n", info.release);
235 return 0;
236 }
237 int version = atoi(info.release);
238 if (version == 0) {
239 SkDebugf("could not parse uname release %s\n", info.release);
240 }
241 return version;
242}
243
244static int darwinVersion() {
245 static int darwin_version = readVersion();
246 return darwin_version;
247}
248
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000249static bool isSnowLeopard() {
250 return darwinVersion() == 10;
251}
252
253static bool isLion() {
254 return darwinVersion() == 11;
255}
256
257static bool isMountainLion() {
258 return darwinVersion() == 12;
259}
260
261static bool isLCDFormat(unsigned format) {
262 return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
263}
264
265static CGFloat ScalarToCG(SkScalar scalar) {
266 if (sizeof(CGFloat) == sizeof(float)) {
267 return SkScalarToFloat(scalar);
268 } else {
269 SkASSERT(sizeof(CGFloat) == sizeof(double));
270 return (CGFloat) SkScalarToDouble(scalar);
271 }
272}
273
274static SkScalar CGToScalar(CGFloat cgFloat) {
275 if (sizeof(CGFloat) == sizeof(float)) {
276 return SkFloatToScalar(cgFloat);
277 } else {
278 SkASSERT(sizeof(CGFloat) == sizeof(double));
279 return SkDoubleToScalar(cgFloat);
280 }
281}
282
283static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
284 SkScalar sx = SK_Scalar1,
285 SkScalar sy = SK_Scalar1) {
286 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
287 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
288 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
289 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
290 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
291 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
292}
293
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000294///////////////////////////////////////////////////////////////////////////////
295
296#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
297#define BITMAP_INFO_GRAY (kCGImageAlphaNone)
298
299/**
300 * There does not appear to be a publicly accessable API for determining if lcd
301 * font smoothing will be applied if we request it. The main issue is that if
302 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
303 */
304static bool supports_LCD() {
305 static int gSupportsLCD = -1;
306 if (gSupportsLCD >= 0) {
307 return (bool) gSupportsLCD;
308 }
309 uint32_t rgb = 0;
310 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
311 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
312 colorspace, BITMAP_INFO_RGB));
313 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
314 CGContextSetShouldSmoothFonts(cgContext, true);
315 CGContextSetShouldAntialias(cgContext, true);
316 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
317 CGContextSetGrayFillColor(cgContext, 1, 1);
318 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
319 uint32_t r = (rgb >> 16) & 0xFF;
320 uint32_t g = (rgb >> 8) & 0xFF;
321 uint32_t b = (rgb >> 0) & 0xFF;
322 gSupportsLCD = (r != g || r != b);
323 return (bool) gSupportsLCD;
324}
325
326class Offscreen {
327public:
328 Offscreen();
329
330 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
331 CGGlyph glyphID, size_t* rowBytesPtr,
332 bool generateA8FromLCD);
333
334private:
335 enum {
336 kSize = 32 * 32 * sizeof(CGRGBPixel)
337 };
338 SkAutoSMalloc<kSize> fImageStorage;
339 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
340
341 // cached state
342 AutoCFRelease<CGContextRef> fCG;
343 SkISize fSize;
344 bool fDoAA;
345 bool fDoLCD;
346
347 static int RoundSize(int dimension) {
348 return SkNextPow2(dimension);
349 }
350};
351
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +0000352Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL),
robertphillips@google.com87379e12013-03-29 12:11:10 +0000353 fDoAA(false), fDoLCD(false) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000354 fSize.set(0, 0);
355}
356
357///////////////////////////////////////////////////////////////////////////////
358
bungeman@google.comfe747652013-03-25 19:36:11 +0000359static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000360 unsigned style = SkTypeface::kNormal;
361 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
362
363 if (traits & kCTFontBoldTrait) {
364 style |= SkTypeface::kBold;
365 }
366 if (traits & kCTFontItalicTrait) {
367 style |= SkTypeface::kItalic;
368 }
bungeman@google.comfe747652013-03-25 19:36:11 +0000369 if (isFixedPitch) {
370 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000371 }
372 return (SkTypeface::Style)style;
373}
374
375static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
376 SkFontID id = 0;
377// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
378// bracket this to be Mac only.
379#ifdef SK_BUILD_FOR_MAC
380 ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
381 id = (SkFontID)ats;
382 if (id != 0) {
383 id &= 0x3FFFFFFF; // make top two bits 00
384 return id;
385 }
386#endif
387 // CTFontGetPlatformFont returns NULL if the font is local
388 // (e.g., was created by a CSS3 @font-face rule).
389 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
390 AutoCGTable<SkOTTableHead> headTable(cgFont);
391 if (headTable.fData) {
392 id = (SkFontID) headTable->checksumAdjustment;
393 id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
394 }
395 // well-formed fonts have checksums, but as a last resort, use the pointer.
396 if (id == 0) {
397 id = (SkFontID) (uintptr_t) fontRef;
398 id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
399 }
400 return id;
401}
402
reed@google.comce8b3de2013-03-26 19:30:16 +0000403static SkFontStyle stylebits2fontstyle(SkTypeface::Style styleBits) {
404 return SkFontStyle((styleBits & SkTypeface::kBold)
405 ? SkFontStyle::kBold_Weight
406 : SkFontStyle::kNormal_Weight,
407 SkFontStyle::kNormal_Width,
408 (styleBits & SkTypeface::kItalic)
409 ? SkFontStyle::kItalic_Slant
410 : SkFontStyle::kUpright_Slant);
411}
412
413#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
414
415static SkTypeface::Style fontstyle2stylebits(const SkFontStyle& fs) {
416 unsigned style = 0;
417 if (fs.width() >= WEIGHT_THRESHOLD) {
418 style |= SkTypeface::kBold;
419 }
420 if (fs.isItalic()) {
421 style |= SkTypeface::kItalic;
422 }
423 return (SkTypeface::Style)style;
424}
425
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000426class SkTypeface_Mac : public SkTypeface {
427public:
bungeman@google.comfe747652013-03-25 19:36:11 +0000428 SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isFixedPitch,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000429 CTFontRef fontRef, const char name[])
reed@google.comce8b3de2013-03-26 19:30:16 +0000430 : SkTypeface(style, fontID, isFixedPitch)
431 , fName(name)
432 , fFontRef(fontRef) // caller has already called CFRetain for us
433 , fFontStyle(stylebits2fontstyle(style))
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000434 {
435 SkASSERT(fontRef);
436 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000437
reed@google.comce8b3de2013-03-26 19:30:16 +0000438 SkTypeface_Mac(const SkFontStyle& fs, SkFontID fontID, bool isFixedPitch,
439 CTFontRef fontRef, const char name[])
440 : SkTypeface(fontstyle2stylebits(fs), fontID, isFixedPitch)
441 , fName(name)
442 , fFontRef(fontRef) // caller has already called CFRetain for us
443 , fFontStyle(fs)
444 {
445 SkASSERT(fontRef);
446 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000447
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000448 SkString fName;
449 AutoCFRelease<CTFontRef> fFontRef;
reed@google.comce8b3de2013-03-26 19:30:16 +0000450 SkFontStyle fFontStyle;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000451
452protected:
453 friend class SkFontHost; // to access our protected members for deprecated methods
454
455 virtual int onGetUPEM() const SK_OVERRIDE;
reed@google.comcc9aad52013-03-21 19:28:10 +0000456 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
bungeman@google.com839702b2013-08-07 17:09:22 +0000457 virtual SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000458 virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
459 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
460 size_t length, void* data) const SK_OVERRIDE;
461 virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
462 virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
reed@google.com5526ede2013-03-25 13:03:37 +0000463 virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
reed@google.com2689f612013-03-20 20:01:47 +0000464 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
465 SkAdvancedTypefaceMetrics::PerGlyphInfo,
466 const uint32_t*, uint32_t) const SK_OVERRIDE;
reed@google.combcb42ae2013-07-02 13:56:39 +0000467 virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
468 int glyphCount) const SK_OVERRIDE;
469 virtual int onCountGlyphs() const SK_OVERRIDE;
reed@google.com7fdcd442013-07-30 21:25:49 +0000470 virtual SkTypeface* onRefMatchingStyle(Style) const SK_OVERRIDE;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000471
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000472private:
reed@google.comce8b3de2013-03-26 19:30:16 +0000473
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000474 typedef SkTypeface INHERITED;
475};
476
477static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
478 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000479 bool isFixedPitch;
480 SkTypeface::Style style = computeStyleBits(fontRef, &isFixedPitch);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000481 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
482
bungeman@google.comfe747652013-03-25 19:36:11 +0000483 return new SkTypeface_Mac(style, fontID, isFixedPitch, fontRef, name);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000484}
485
486static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
487 CTFontRef ctFont = NULL;
488
489 CTFontSymbolicTraits ctFontTraits = 0;
490 if (theStyle & SkTypeface::kBold) {
491 ctFontTraits |= kCTFontBoldTrait;
492 }
493 if (theStyle & SkTypeface::kItalic) {
494 ctFontTraits |= kCTFontItalicTrait;
495 }
496
497 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000498 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000499
500 AutoCFRelease<CFNumberRef> cfFontTraits(
501 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
502
503 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
504 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
505 &kCFTypeDictionaryKeyCallBacks,
506 &kCFTypeDictionaryValueCallBacks));
507
508 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
509 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
510 &kCFTypeDictionaryKeyCallBacks,
511 &kCFTypeDictionaryValueCallBacks));
512
513 // Create the font
514 if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
515 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
516
517 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
518 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
519
520 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
521 CTFontDescriptorCreateWithAttributes(cfAttributes));
522
523 if (ctFontDesc != NULL) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000524 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000525 }
526 }
527
528 return ctFont ? NewFromFontRef(ctFont, familyName) : NULL;
529}
530
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000531static SkTypeface* GetDefaultFace() {
532 SK_DECLARE_STATIC_MUTEX(gMutex);
533 SkAutoMutexAcquire ma(gMutex);
534
535 static SkTypeface* gDefaultFace;
536
537 if (NULL == gDefaultFace) {
538 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal);
539 SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal);
540 }
541 return gDefaultFace;
542}
543
544///////////////////////////////////////////////////////////////////////////////
545
546extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
547CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
548 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
549 return macface ? macface->fFontRef.get() : NULL;
550}
551
552/* This function is visible on the outside. It first searches the cache, and if
553 * not found, returns a new entry (after adding it to the cache).
554 */
555SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
556 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
557 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
558 if (face) {
559 face->ref();
560 } else {
561 face = NewFromFontRef(fontRef, NULL);
562 SkTypefaceCache::Add(face, face->style());
563 // NewFromFontRef doesn't retain the parameter, but the typeface it
564 // creates does release it in its destructor, so we balance that with
565 // a retain call here.
566 CFRetain(fontRef);
567 }
568 SkASSERT(face->getRefCnt() > 1);
569 return face;
570}
571
572struct NameStyleRec {
573 const char* fName;
574 SkTypeface::Style fStyle;
575};
576
577static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style,
578 void* ctx) {
579 const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face);
580 const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx);
581
582 return rec->fStyle == style && mface->fName.equals(rec->fName);
583}
584
585static const char* map_css_names(const char* name) {
586 static const struct {
587 const char* fFrom; // name the caller specified
588 const char* fTo; // "canonical" name we map to
589 } gPairs[] = {
590 { "sans-serif", "Helvetica" },
591 { "serif", "Times" },
592 { "monospace", "Courier" }
593 };
594
595 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
596 if (strcmp(name, gPairs[i].fFrom) == 0) {
597 return gPairs[i].fTo;
598 }
599 }
600 return name; // no change
601}
602
reed@google.com7fdcd442013-07-30 21:25:49 +0000603static SkTypeface* create_typeface(const SkTypeface* familyFace,
604 const char familyName[],
605 SkTypeface::Style style) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000606 if (familyName) {
607 familyName = map_css_names(familyName);
608 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +0000609
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000610 // Clone an existing typeface
611 // TODO: only clone if style matches the familyFace's style...
612 if (familyName == NULL && familyFace != NULL) {
613 familyFace->ref();
614 return const_cast<SkTypeface*>(familyFace);
615 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +0000616
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000617 if (!familyName || !*familyName) {
618 familyName = FONT_DEFAULT_NAME;
619 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +0000620
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000621 NameStyleRec rec = { familyName, style };
622 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec);
skia.committer@gmail.com76015b02013-07-31 07:01:00 +0000623
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000624 if (NULL == face) {
625 face = NewFromName(familyName, style);
626 if (face) {
627 SkTypefaceCache::Add(face, style);
628 } else {
629 face = GetDefaultFace();
630 face->ref();
631 }
632 }
633 return face;
634}
635
reed@google.com7fdcd442013-07-30 21:25:49 +0000636SkTypeface* SkTypeface_Mac::onRefMatchingStyle(Style styleBits) const {
637 return create_typeface(this, NULL, styleBits);
638}
639
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000640///////////////////////////////////////////////////////////////////////////////
641
bungeman@google.comcefd9812013-05-15 15:07:32 +0000642/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000643struct GlyphRect {
644 int16_t fMinX;
645 int16_t fMinY;
646 int16_t fMaxX;
647 int16_t fMaxY;
648};
649
650class SkScalerContext_Mac : public SkScalerContext {
651public:
reed@google.com0da48612013-03-19 16:06:52 +0000652 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000653
654protected:
655 unsigned generateGlyphCount(void) SK_OVERRIDE;
656 uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
657 void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
658 void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
659 void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
660 void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
661 void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
662
663private:
664 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000665
bungeman@google.comcefd9812013-05-15 15:07:32 +0000666 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
667 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000668
669 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
670 *
671 * For use with (and must be called before) generateBBoxes.
672 */
673 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000674
bungeman@google.comcefd9812013-05-15 15:07:32 +0000675 /** Initializes fFBoundingBoxes and returns true on success.
676 *
677 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
678 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
679 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
680 * font directly.
681 *
682 * This routine initializes fFBoundingBoxes to an array of
683 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
684 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
685 *
686 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
687 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
688 *
689 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
690 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000691 bool generateBBoxes();
692
bungeman@google.comcefd9812013-05-15 15:07:32 +0000693 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
694 *
695 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
696 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
697 */
698 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000699
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000700 Offscreen fOffscreen;
701 AutoCFRelease<CTFontRef> fCTFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000702
bungeman@google.comcefd9812013-05-15 15:07:32 +0000703 /** Vertical variant of fCTFont.
704 *
705 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
706 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
707 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
708 * Use fCTVerticalFont with kCTFontVerticalOrientation to get metrics in the same space.
709 */
710 AutoCFRelease<CTFontRef> fCTVerticalFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000711
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000712 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000713 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000714 uint16_t fFBoundingBoxesGlyphOffset;
715 uint16_t fGlyphCount;
716 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000717 const bool fDoSubPosition;
718 const bool fVertical;
719
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000720 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000721
722 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000723};
724
reed@google.com0da48612013-03-19 16:06:52 +0000725SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
726 const SkDescriptor* desc)
727 : INHERITED(typeface, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000728 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000729 , fFBoundingBoxesGlyphOffset(0)
730 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000731 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
732 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
733
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000734{
reed@google.com2689f612013-03-20 20:01:47 +0000735 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000736 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000737 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
738 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000739
bungeman@google.comcefd9812013-05-15 15:07:32 +0000740 fRec.getSingleMatrix(&fFUnitMatrix);
741 CGAffineTransform transform = MatrixToCGAffineTransform(fFUnitMatrix);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000742
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000743 AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
744 if (fVertical) {
745 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
746 kCFAllocatorDefault, 0,
747 &kCFTypeDictionaryKeyCallBacks,
748 &kCFTypeDictionaryValueCallBacks));
749 if (cfAttributes) {
750 CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
751 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
752 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
753 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
754 ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
755 }
756 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000757 // Since our matrix includes everything, we pass 1 for size.
758 fCTFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, ctFontDesc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000759 fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL);
760 if (fVertical) {
761 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
762 transform = CGAffineTransformConcat(rotateLeft, transform);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000763 fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000764 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000765
bungeman@google.comcefd9812013-05-15 15:07:32 +0000766 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
767 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000768}
769
770CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
771 CGGlyph glyphID, size_t* rowBytesPtr,
772 bool generateA8FromLCD) {
773 if (!fRGBSpace) {
774 //It doesn't appear to matter what color space is specified.
775 //Regular blends and antialiased text are always (s*a + d*(1-a))
776 //and smoothed text is always g=2.0.
777 fRGBSpace = CGColorSpaceCreateDeviceRGB();
778 }
779
780 // default to kBW_Format
781 bool doAA = false;
782 bool doLCD = false;
783
784 if (SkMask::kBW_Format != glyph.fMaskFormat) {
785 doLCD = true;
786 doAA = true;
787 }
788
789 // FIXME: lcd smoothed un-hinted rasterization unsupported.
790 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
791 doLCD = false;
792 doAA = true;
793 }
794
795 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
796 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
797 if (fSize.fWidth < glyph.fWidth) {
798 fSize.fWidth = RoundSize(glyph.fWidth);
799 }
800 if (fSize.fHeight < glyph.fHeight) {
801 fSize.fHeight = RoundSize(glyph.fHeight);
802 }
803
804 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
805 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
806 fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
807 rowBytes, fRGBSpace, BITMAP_INFO_RGB);
808
809 // skia handles quantization itself, so we disable this for cg to get
810 // full fractional data from them.
811 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
812 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
813
814 CGContextSetTextDrawingMode(fCG, kCGTextFill);
815 CGContextSetFont(fCG, context.fCGFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000816 CGContextSetFontSize(fCG, 1 /*CTFontGetSize(context.fCTFont)*/);
817 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000818
bungeman@google.comcefd9812013-05-15 15:07:32 +0000819 // Because CG always draws from the horizontal baseline,
820 // if there is a non-integral translation from the horizontal origin to the vertical origin,
821 // then CG cannot draw the glyph in the correct location without subpixel positioning.
822 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical);
823 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || context.fVertical);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000824
825 // Draw white on black to create mask.
826 // TODO: Draw black on white and invert, CG has a special case codepath.
827 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
828
829 // force our checks below to happen
830 fDoAA = !doAA;
831 fDoLCD = !doLCD;
832 }
833
834 if (fDoAA != doAA) {
835 CGContextSetShouldAntialias(fCG, doAA);
836 fDoAA = doAA;
837 }
838 if (fDoLCD != doLCD) {
839 CGContextSetShouldSmoothFonts(fCG, doLCD);
840 fDoLCD = doLCD;
841 }
842
843 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
844 // skip rows based on the glyph's height
845 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
846
847 // erase to black
848 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
849
850 float subX = 0;
851 float subY = 0;
852 if (context.fDoSubPosition) {
853 subX = SkFixedToFloat(glyph.getSubXFixed());
854 subY = SkFixedToFloat(glyph.getSubYFixed());
855 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000856
bungeman@google.comcefd9812013-05-15 15:07:32 +0000857 // CGContextShowGlyphsAtPoint always draws using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000858 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000859 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000860 context.getVerticalOffset(glyphID, &offset);
861 subX += offset.fX;
862 subY += offset.fY;
863 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000864
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000865 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
866 glyph.fTop + glyph.fHeight - subY,
867 &glyphID, 1);
868
869 SkASSERT(rowBytesPtr);
870 *rowBytesPtr = rowBytes;
871 return image;
872}
873
bungeman@google.comcefd9812013-05-15 15:07:32 +0000874void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
875 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
876 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
877 CGSize cgVertOffset;
878 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
879
880 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
881 if (isSnowLeopard()) {
882 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
883 fFUnitMatrix.mapPoints(&skVertOffset, 1);
884 } else {
885 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
886 skVertOffset.fY = -skVertOffset.fY;
887 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000888
bungeman@google.comcefd9812013-05-15 15:07:32 +0000889 *offset = skVertOffset;
890}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000891
892uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
893 if (fFBoundingBoxesGlyphOffset) {
894 return fFBoundingBoxesGlyphOffset;
895 }
896 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
897 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
898 if (hheaTable.fData) {
899 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
900 }
901 return fFBoundingBoxesGlyphOffset;
902}
reed@android.com0680d6c2008-12-19 19:46:15 +0000903
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000904bool SkScalerContext_Mac::generateBBoxes() {
905 if (fGeneratedFBoundingBoxes) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000906 return NULL != fFBoundingBoxes.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000907 }
908 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000909
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000910 AutoCGTable<SkOTTableHead> headTable(fCGFont);
911 if (!headTable.fData) {
912 return false;
913 }
914
915 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
916 if (!locaTable.fData) {
917 return false;
918 }
919
920 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
921 if (!glyfTable.fData) {
922 return false;
923 }
924
925 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000926 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000927
928 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
929 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
930 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
931 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
932 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
933 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
934 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
935 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
936 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
937 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
938 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000939
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000940 return true;
941}
942
943unsigned SkScalerContext_Mac::generateGlyphCount(void) {
944 return fGlyphCount;
945}
946
947uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
948 CGGlyph cgGlyph;
949 UniChar theChar;
950
951 // Validate our parameters and state
952 SkASSERT(uni <= 0x0000FFFF);
953 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
954
955 // Get the glyph
956 theChar = (UniChar) uni;
957
958 if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1)) {
959 cgGlyph = 0;
960 }
961
962 return cgGlyph;
963}
964
965void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
966 this->generateMetrics(glyph);
967}
968
bungeman@google.comcefd9812013-05-15 15:07:32 +0000969void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
970 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
971 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000972
bungeman@google.comcefd9812013-05-15 15:07:32 +0000973 // The following block produces cgAdvance in CG units (pixels, y up).
974 CGSize cgAdvance;
975 if (fVertical) {
976 CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
977 &cgGlyph, &cgAdvance, 1);
978 } else {
979 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
980 &cgGlyph, &cgAdvance, 1);
981 }
982 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
983 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000984
bungeman@google.comcefd9812013-05-15 15:07:32 +0000985 // The following produces skBounds in SkGlyph units (pixels, y down),
986 // or returns early if skBounds would be empty.
987 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000988
bungeman@google.comcefd9812013-05-15 15:07:32 +0000989 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
990 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
991 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
992 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
993 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
994 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
995 // to center the glyph along the vertical baseline and also perform some mysterious shift
996 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
997 // these steps.
998 //
999 // It is not known which is correct (or if either is correct). However, we must always draw
1000 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
1001 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001002
bungeman@google.comcefd9812013-05-15 15:07:32 +00001003 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
1004 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001005
bungeman@google.comcefd9812013-05-15 15:07:32 +00001006 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
1007 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
1008 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
1009 // font directly.
1010 if ((isLion() || isMountainLion()) &&
1011 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1012 {
1013 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1014 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1015 return;
1016 }
1017 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1018 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1019 fFUnitMatrix.mapRect(&skBounds);
1020
1021 } else {
1022 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1023 CGRect cgBounds;
1024 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1025 &cgGlyph, &cgBounds, 1);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001026
bungeman@google.comcefd9812013-05-15 15:07:32 +00001027 // BUG?
1028 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1029 // it should be empty. So, if we see a zero-advance, we check if it has an
1030 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1031 // is rare, so we won't incur a big performance cost for this extra check.
1032 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
1033 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
1034 if (NULL == path || CGPathIsEmpty(path)) {
1035 return;
1036 }
1037 }
1038
1039 if (CGRectIsEmpty_inline(cgBounds)) {
1040 return;
1041 }
1042
1043 // Convert cgBounds to SkGlyph units (pixels, y down).
1044 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1045 cgBounds.size.width, cgBounds.size.height);
1046 }
1047
1048 if (fVertical) {
1049 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1050 // Convert these horizontal bounds into vertical bounds.
1051 SkPoint offset;
1052 getVerticalOffset(cgGlyph, &offset);
1053 skBounds.offset(offset);
1054 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001055
bungeman@google.comcefd9812013-05-15 15:07:32 +00001056 // Currently the bounds are based on being rendered at (0,0).
1057 // The top left must not move, since that is the base from which subpixel positioning is offset.
1058 if (fDoSubPosition) {
1059 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1060 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1061 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001062
bungeman@google.comcefd9812013-05-15 15:07:32 +00001063 SkIRect skIBounds;
1064 skBounds.roundOut(&skIBounds);
1065 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1066 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1067 // is not currently known, as CG dilates the outlines by some percentage.
1068 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1069 skIBounds.outset(1, 1);
1070 glyph->fLeft = SkToS16(skIBounds.fLeft);
1071 glyph->fTop = SkToS16(skIBounds.fTop);
1072 glyph->fWidth = SkToU16(skIBounds.width());
1073 glyph->fHeight = SkToU16(skIBounds.height());
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001074
bungeman@google.comcefd9812013-05-15 15:07:32 +00001075#ifdef HACK_COLORGLYPHS
1076 glyph->fMaskFormat = SkMask::kARGB32_Format;
1077#endif
1078}
1079
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001080#include "SkColorPriv.h"
1081
1082static void build_power_table(uint8_t table[], float ee) {
1083 for (int i = 0; i < 256; i++) {
1084 float x = i / 255.f;
1085 x = sk_float_pow(x, ee);
1086 int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
1087 table[i] = SkToU8(xx);
1088 }
1089}
1090
1091/**
1092 * This will invert the gamma applied by CoreGraphics, so we can get linear
1093 * values.
1094 *
1095 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1096 * The color space used does not appear to affect this choice.
1097 */
1098static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1099 static bool gInited;
1100 static uint8_t gTableCoreGraphicsSmoothing[256];
1101 if (!gInited) {
1102 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1103 gInited = true;
1104 }
1105 return gTableCoreGraphicsSmoothing;
1106}
1107
1108static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1109 while (count > 0) {
1110 uint8_t mask = 0;
1111 for (int i = 7; i >= 0; --i) {
1112 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1113 if (0 == --count) {
1114 break;
1115 }
1116 }
1117 *dst++ = mask;
1118 }
1119}
1120
1121template<bool APPLY_PREBLEND>
1122static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1123 U8CPU r = (rgb >> 16) & 0xFF;
1124 U8CPU g = (rgb >> 8) & 0xFF;
1125 U8CPU b = (rgb >> 0) & 0xFF;
1126 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1127}
1128template<bool APPLY_PREBLEND>
1129static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1130 const SkGlyph& glyph, const uint8_t* table8) {
1131 const int width = glyph.fWidth;
1132 size_t dstRB = glyph.rowBytes();
1133 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1134
1135 for (int y = 0; y < glyph.fHeight; y++) {
1136 for (int i = 0; i < width; ++i) {
1137 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1138 }
1139 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1140 dst += dstRB;
1141 }
1142}
1143
1144template<bool APPLY_PREBLEND>
1145static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1146 const uint8_t* tableG,
1147 const uint8_t* tableB) {
1148 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1149 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1150 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1151 return SkPack888ToRGB16(r, g, b);
1152}
1153template<bool APPLY_PREBLEND>
1154static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1155 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1156 const int width = glyph.fWidth;
1157 size_t dstRB = glyph.rowBytes();
1158 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1159
1160 for (int y = 0; y < glyph.fHeight; y++) {
1161 for (int i = 0; i < width; i++) {
1162 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1163 }
1164 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1165 dst = (uint16_t*)((char*)dst + dstRB);
1166 }
1167}
1168
1169template<bool APPLY_PREBLEND>
1170static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
1171 const uint8_t* tableG,
1172 const uint8_t* tableB) {
1173 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1174 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1175 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1176 return SkPackARGB32(0xFF, r, g, b);
1177}
1178template<bool APPLY_PREBLEND>
1179static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1180 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1181 const int width = glyph.fWidth;
1182 size_t dstRB = glyph.rowBytes();
1183 uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
1184 for (int y = 0; y < glyph.fHeight; y++) {
1185 for (int i = 0; i < width; i++) {
1186 dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1187 }
1188 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1189 dst = (uint32_t*)((char*)dst + dstRB);
1190 }
1191}
1192
reed@google.comf77b35d2013-05-02 20:39:44 +00001193#ifdef HACK_COLORGLYPHS
1194// hack to colorize the output for testing kARGB32_Format
1195static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb, const SkGlyph& glyph,
1196 int x, int y) {
1197 U8CPU r = (rgb >> 16) & 0xFF;
1198 U8CPU g = (rgb >> 8) & 0xFF;
1199 U8CPU b = (rgb >> 0) & 0xFF;
1200 unsigned a = SkComputeLuminance(r, g, b);
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +00001201
reed@google.comf77b35d2013-05-02 20:39:44 +00001202 // compute gradient from x,y
1203 r = x * 255 / glyph.fWidth;
1204 g = 0;
1205 b = (glyph.fHeight - y) * 255 / glyph.fHeight;
1206 return SkPreMultiplyARGB(a, r, g, b); // red
1207}
1208#endif
1209
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001210template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1211 return (T*)((char*)ptr + byteOffset);
1212}
1213
1214void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
1215 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
1216
1217 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1218 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1219
1220 // Draw the glyph
1221 size_t cgRowBytes;
1222 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
1223 if (cgPixels == NULL) {
1224 return;
1225 }
1226
1227 //TODO: see if drawing black on white and inverting is faster (at least in
1228 //lcd case) as core graphics appears to have special case code for drawing
1229 //black text.
1230
1231 // Fix the glyph
1232 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1233 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1234 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1235
1236 //Note that the following cannot really be integrated into the
1237 //pre-blend, since we may not be applying the pre-blend; when we aren't
1238 //applying the pre-blend it means that a filter wants linear anyway.
1239 //Other code may also be applying the pre-blend, so we'd need another
1240 //one with this and one without.
1241 CGRGBPixel* addr = cgPixels;
1242 for (int y = 0; y < glyph.fHeight; ++y) {
1243 for (int x = 0; x < glyph.fWidth; ++x) {
1244 int r = (addr[x] >> 16) & 0xFF;
1245 int g = (addr[x] >> 8) & 0xFF;
1246 int b = (addr[x] >> 0) & 0xFF;
1247 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1248 }
1249 addr = SkTAddByteOffset(addr, cgRowBytes);
1250 }
1251 }
1252
1253 // Convert glyph to mask
1254 switch (glyph.fMaskFormat) {
1255 case SkMask::kLCD32_Format: {
1256 if (fPreBlend.isApplicable()) {
1257 rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
1258 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1259 } else {
1260 rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
1261 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1262 }
1263 } break;
1264 case SkMask::kLCD16_Format: {
1265 if (fPreBlend.isApplicable()) {
1266 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1267 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1268 } else {
1269 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1270 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1271 }
1272 } break;
1273 case SkMask::kA8_Format: {
1274 if (fPreBlend.isApplicable()) {
1275 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1276 } else {
1277 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1278 }
1279 } break;
1280 case SkMask::kBW_Format: {
1281 const int width = glyph.fWidth;
1282 size_t dstRB = glyph.rowBytes();
1283 uint8_t* dst = (uint8_t*)glyph.fImage;
1284 for (int y = 0; y < glyph.fHeight; y++) {
1285 cgpixels_to_bits(dst, cgPixels, width);
1286 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1287 dst += dstRB;
1288 }
1289 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001290#ifdef HACK_COLORGLYPHS
1291 case SkMask::kARGB32_Format: {
1292 const int width = glyph.fWidth;
1293 size_t dstRB = glyph.rowBytes();
1294 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1295 for (int y = 0; y < glyph.fHeight; y++) {
1296 for (int x = 0; x < width; ++x) {
1297 dst[x] = cgpixels_to_pmcolor(cgPixels[x], glyph, x, y);
1298 }
1299 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1300 dst = (SkPMColor*)((char*)dst + dstRB);
1301 }
1302 } break;
1303#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001304 default:
1305 SkDEBUGFAIL("unexpected mask format");
1306 break;
1307 }
1308}
1309
1310/*
1311 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1312 * seems sufficient, and possibly even correct, to allow the hinted outline
1313 * to be subpixel positioned.
1314 */
1315#define kScaleForSubPixelPositionHinting (4.0f)
1316
1317void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
1318 CTFontRef font = fCTFont;
1319 SkScalar scaleX = SK_Scalar1;
1320 SkScalar scaleY = SK_Scalar1;
1321
1322 /*
1323 * For subpixel positioning, we want to return an unhinted outline, so it
1324 * can be positioned nicely at fractional offsets. However, we special-case
1325 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1326 * we want to retain hinting in the direction orthogonal to the baseline.
1327 * e.g. for horizontal baseline, we want to retain hinting in Y.
1328 * The way we remove hinting is to scale the font by some value (4) in that
1329 * direction, ask for the path, and then scale the path back down.
1330 */
1331 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1332 SkMatrix m;
1333 fRec.getSingleMatrix(&m);
1334
1335 // start out by assuming that we want no hining in X and Y
1336 scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
1337 // now see if we need to restore hinting for axis-aligned baselines
1338 switch (SkComputeAxisAlignmentForHText(m)) {
1339 case kX_SkAxisAlignment:
1340 scaleY = SK_Scalar1; // want hinting in the Y direction
1341 break;
1342 case kY_SkAxisAlignment:
1343 scaleX = SK_Scalar1; // want hinting in the X direction
1344 break;
1345 default:
1346 break;
1347 }
1348
1349 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
1350 // need to release font when we're done
1351 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
1352 }
1353
1354 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
1355 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
1356
1357 path->reset();
1358 if (cgPath != NULL) {
1359 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1360 }
1361
bungeman@google.comcefd9812013-05-15 15:07:32 +00001362 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001363 SkMatrix m;
1364 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1365 path->transform(m);
1366 // balance the call to CTFontCreateCopyWithAttributes
1367 CFSafeRelease(font);
1368 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001369 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001370 SkPoint offset;
1371 getVerticalOffset(cgGlyph, &offset);
1372 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001373 }
1374}
1375
1376void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
1377 SkPaint::FontMetrics* my) {
1378 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1379
1380 SkPaint::FontMetrics theMetrics;
1381 theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1382 theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1383 theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1384 theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1385 theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1386 theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1387 theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1388 theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
1389 theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
1390
1391 if (mx != NULL) {
1392 *mx = theMetrics;
1393 }
1394 if (my != NULL) {
1395 *my = theMetrics;
1396 }
1397}
1398
1399void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1400 SkPath* skPath = (SkPath*)info;
1401
1402 // Process the path element
1403 switch (element->type) {
1404 case kCGPathElementMoveToPoint:
1405 skPath->moveTo(element->points[0].x, -element->points[0].y);
1406 break;
1407
1408 case kCGPathElementAddLineToPoint:
1409 skPath->lineTo(element->points[0].x, -element->points[0].y);
1410 break;
1411
1412 case kCGPathElementAddQuadCurveToPoint:
1413 skPath->quadTo(element->points[0].x, -element->points[0].y,
1414 element->points[1].x, -element->points[1].y);
1415 break;
1416
1417 case kCGPathElementAddCurveToPoint:
1418 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1419 element->points[1].x, -element->points[1].y,
1420 element->points[2].x, -element->points[2].y);
1421 break;
1422
1423 case kCGPathElementCloseSubpath:
1424 skPath->close();
1425 break;
1426
1427 default:
1428 SkDEBUGFAIL("Unknown path element!");
1429 break;
1430 }
1431}
1432
1433
1434///////////////////////////////////////////////////////////////////////////////
1435
1436// Returns NULL on failure
1437// Call must still manage its ownership of provider
1438static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1439 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
1440 if (NULL == cg) {
1441 return NULL;
1442 }
1443 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
1444 return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
1445}
1446
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001447// Web fonts added to the the CTFont registry do not return their character set.
1448// Iterate through the font in this case. The existing caller caches the result,
1449// so the performance impact isn't too bad.
1450static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1451 SkTDArray<SkUnichar>* glyphToUnicode) {
1452 glyphToUnicode->setCount(glyphCount);
1453 SkUnichar* out = glyphToUnicode->begin();
1454 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1455 UniChar unichar = 0;
1456 while (glyphCount > 0) {
1457 CGGlyph glyph;
1458 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1459 out[glyph] = unichar;
1460 --glyphCount;
1461 }
1462 if (++unichar == 0) {
1463 break;
1464 }
1465 }
1466}
1467
1468// Construct Glyph to Unicode table.
1469// Unicode code points that require conjugate pairs in utf16 are not
1470// supported.
1471static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1472 SkTDArray<SkUnichar>* glyphToUnicode) {
1473 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1474 if (!charSet) {
1475 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1476 return;
1477 }
1478
1479 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1480 charSet));
1481 if (!bitmap) {
1482 return;
1483 }
1484 CFIndex length = CFDataGetLength(bitmap);
1485 if (!length) {
1486 return;
1487 }
1488 if (length > 8192) {
1489 // TODO: Add support for Unicode above 0xFFFF
1490 // Consider only the BMP portion of the Unicode character points.
1491 // The bitmap may contain other planes, up to plane 16.
1492 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1493 length = 8192;
1494 }
1495 const UInt8* bits = CFDataGetBytePtr(bitmap);
1496 glyphToUnicode->setCount(glyphCount);
1497 SkUnichar* out = glyphToUnicode->begin();
1498 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1499 for (int i = 0; i < length; i++) {
1500 int mask = bits[i];
1501 if (!mask) {
1502 continue;
1503 }
1504 for (int j = 0; j < 8; j++) {
1505 CGGlyph glyph;
1506 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1507 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1508 out[glyph] = unichar;
1509 }
1510 }
1511 }
1512}
1513
1514static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1515 CGSize advance;
1516 advance.width = 0;
1517 CGGlyph glyph = gId;
1518 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1519 *data = sk_float_round2int(advance.width);
1520 return true;
1521}
1522
1523// we might move this into our CGUtils...
1524static void CFStringToSkString(CFStringRef src, SkString* dst) {
1525 // Reserve enough room for the worst-case string,
1526 // plus 1 byte for the trailing null.
1527 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1528 kCFStringEncodingUTF8) + 1;
1529 dst->resize(length);
1530 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1531 // Resize to the actual UTF-8 length used, stripping the null character.
1532 dst->resize(strlen(dst->c_str()));
1533}
1534
reed@google.com2689f612013-03-20 20:01:47 +00001535SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001536 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1537 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001538 uint32_t glyphIDsCount) const {
1539
1540 CTFontRef originalCTFont = fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001541 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
1542 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
1543 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1544
1545 {
1546 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
1547 CFStringToSkString(fontName, &info->fFontName);
1548 }
1549
1550 info->fMultiMaster = false;
1551 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1552 info->fLastGlyphID = SkToU16(glyphCount - 1);
1553 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1554
1555 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1556 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1557 }
1558
1559 info->fStyle = 0;
1560
1561 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1562 // fonts always have both glyf and loca tables. At the least, this is what
1563 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1564 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001565 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001566 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1567 info->fItalicAngle = 0;
1568 info->fAscent = 0;
1569 info->fDescent = 0;
1570 info->fStemV = 0;
1571 info->fCapHeight = 0;
1572 info->fBBox = SkIRect::MakeEmpty();
1573 return info;
1574 }
1575
1576 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1577 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1578 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1579 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1580 }
1581 if (symbolicTraits & kCTFontItalicTrait) {
1582 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1583 }
1584 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001585 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1586 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1587 } else if (stylisticClass & kCTFontScriptsClass) {
1588 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1589 }
1590 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1591 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1592 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1593 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1594 CGRect bbox = CTFontGetBoundingBox(ctFont);
1595
1596 SkRect r;
1597 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1598 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1599 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1600 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1601
1602 r.roundOut(&(info->fBBox));
1603
1604 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1605 // This probably isn't very good with an italic font.
1606 int16_t min_width = SHRT_MAX;
1607 info->fStemV = 0;
1608 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1609 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1610 CGGlyph glyphs[count];
1611 CGRect boundingRects[count];
1612 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1613 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1614 glyphs, boundingRects, count);
1615 for (size_t i = 0; i < count; i++) {
1616 int16_t width = (int16_t) boundingRects[i].size.width;
1617 if (width > 0 && width < min_width) {
1618 min_width = width;
1619 info->fStemV = min_width;
1620 }
1621 }
1622 }
1623
1624 if (false) { // TODO: haven't figured out how to know if font is embeddable
1625 // (information is in the OS/2 table)
1626 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
1627 } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
1628 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1629 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1630 info->fGlyphWidths->fAdvance.append(1, &min_width);
1631 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1632 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1633 } else {
1634 info->fGlyphWidths.reset(
1635 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
1636 glyphCount,
1637 glyphIDs,
1638 glyphIDsCount,
1639 &getWidthAdvance));
1640 }
1641 }
1642 return info;
1643}
1644
1645///////////////////////////////////////////////////////////////////////////////
1646
reed@google.comcc9aad52013-03-21 19:28:10 +00001647static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1648 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001649 AutoCFRelease<CFNumberRef> fontFormatRef(
1650 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1651 if (!fontFormatRef) {
1652 return 0;
1653 }
1654
1655 SInt32 fontFormatValue;
1656 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1657 return 0;
1658 }
1659
1660 switch (fontFormatValue) {
1661 case kCTFontFormatOpenTypePostScript:
1662 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1663 case kCTFontFormatOpenTypeTrueType:
1664 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1665 case kCTFontFormatTrueType:
1666 return SkSFNTHeader::fontType_MacTrueType::TAG;
1667 case kCTFontFormatPostScript:
1668 return SkSFNTHeader::fontType_PostScript::TAG;
1669 case kCTFontFormatBitmap:
1670 return SkSFNTHeader::fontType_MacTrueType::TAG;
1671 case kCTFontFormatUnrecognized:
1672 default:
1673 //CT seems to be unreliable in being able to obtain the type,
1674 //even if all we want is the first four bytes of the font resource.
1675 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1676 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1677 }
1678}
1679
reed@google.comcc9aad52013-03-21 19:28:10 +00001680SkStream* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
1681 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001682 if (0 == fontType) {
1683 return NULL;
1684 }
1685
1686 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001687 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001688 SkTDArray<SkFontTableTag> tableTags;
1689 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001690 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001691
1692 // calc total size for font, save sizes
1693 SkTDArray<size_t> tableSizes;
1694 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1695 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001696 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001697 totalSize += (tableSize + 3) & ~3;
1698 *tableSizes.append() = tableSize;
1699 }
1700
1701 // reserve memory for stream, and zero it (tables must be zero padded)
1702 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1703 char* dataStart = (char*)stream->getMemoryBase();
1704 sk_bzero(dataStart, totalSize);
1705 char* dataPtr = dataStart;
1706
1707 // compute font header entries
1708 uint16_t entrySelector = 0;
1709 uint16_t searchRange = 1;
1710 while (searchRange < numTables >> 1) {
1711 entrySelector++;
1712 searchRange <<= 1;
1713 }
1714 searchRange <<= 4;
1715 uint16_t rangeShift = (numTables << 4) - searchRange;
1716
1717 // write font header
1718 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1719 header->fontType = fontType;
1720 header->numTables = SkEndian_SwapBE16(numTables);
1721 header->searchRange = SkEndian_SwapBE16(searchRange);
1722 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1723 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1724 dataPtr += sizeof(SkSFNTHeader);
1725
1726 // write tables
1727 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1728 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1729 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1730 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001731 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001732 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1733 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1734 tableSize));
1735 entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
1736 entry->logicalLength = SkEndian_SwapBE32(tableSize);
1737
1738 dataPtr += (tableSize + 3) & ~3;
1739 ++entry;
1740 }
1741
1742 return stream;
1743}
1744
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001745///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001746///////////////////////////////////////////////////////////////////////////////
1747
1748int SkTypeface_Mac::onGetUPEM() const {
1749 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1750 return CGFontGetUnitsPerEm(cgFont);
1751}
1752
bungeman@google.com839702b2013-08-07 17:09:22 +00001753SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001754 SkTypeface::LocalizedStrings* nameIter =
1755 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
1756 if (NULL == nameIter) {
1757 AutoCFRelease<CFStringRef> cfLanguage;
1758 AutoCFRelease<CFStringRef> cfFamilyName(
1759 CTFontCopyLocalizedName(fFontRef, kCTFontFamilyNameKey, &cfLanguage));
1760
1761 SkString skLanguage;
1762 SkString skFamilyName;
1763 if (cfLanguage.get()) {
1764 CFStringToSkString(cfLanguage.get(), &skLanguage);
1765 } else {
1766 skLanguage = "und"; //undetermined
1767 }
1768 if (cfFamilyName.get()) {
1769 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1770 }
1771
1772 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1773 }
1774 return nameIter;
1775}
1776
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001777// If, as is the case with web fonts, the CTFont data isn't available,
1778// the CGFont data may work. While the CGFont may always provide the
1779// right result, leave the CTFont code path to minimize disruption.
1780static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1781 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1782 kCTFontTableOptionNoOptions);
1783 if (NULL == data) {
1784 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
1785 data = CGFontCopyTableForTag(cgFont, tag);
1786 }
1787 return data;
1788}
1789
1790int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1791 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1792 kCTFontTableOptionNoOptions));
1793 if (NULL == cfArray) {
1794 return 0;
1795 }
1796 int count = CFArrayGetCount(cfArray);
1797 if (tags) {
1798 for (int i = 0; i < count; ++i) {
1799 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1800 tags[i] = static_cast<SkFontTableTag>(fontTag);
1801 }
1802 }
1803 return count;
1804}
1805
1806size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1807 size_t length, void* dstData) const {
1808 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
1809 if (NULL == srcData) {
1810 return 0;
1811 }
1812
1813 size_t srcSize = CFDataGetLength(srcData);
1814 if (offset >= srcSize) {
1815 return 0;
1816 }
1817 if (length > srcSize - offset) {
1818 length = srcSize - offset;
1819 }
1820 if (dstData) {
1821 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1822 }
1823 return length;
1824}
1825
1826SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001827 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001828}
1829
1830void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001831 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1832 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1833 {
1834 rec->fMaskFormat = SkMask::kA8_Format;
1835 // Render the glyphs as close as possible to what was requested.
1836 // The above turns off subpixel rendering, but the user requested it.
1837 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1838 // See comments below for more details.
1839 rec->setHinting(SkPaint::kNormal_Hinting);
1840 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001841
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001842 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
1843 SkScalerContext::kAutohinting_Flag |
1844 SkScalerContext::kLCD_BGROrder_Flag |
1845 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001846
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001847 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001848
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001849 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001850
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001851 // Only two levels of hinting are supported.
1852 // kNo_Hinting means avoid CoreGraphics outline dilation.
1853 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1854 // If there is no lcd support, hinting (dilation) cannot be supported.
1855 SkPaint::Hinting hinting = rec->getHinting();
1856 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1857 hinting = SkPaint::kNo_Hinting;
1858 } else if (SkPaint::kFull_Hinting == hinting) {
1859 hinting = SkPaint::kNormal_Hinting;
1860 }
1861 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001862
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001863 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1864 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1865 // There is no current means to honor a request for unhinted lcd,
1866 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001867
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001868 // Hinting and smoothing should be orthogonal, but currently they are not.
1869 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
1870 // output is drawn from auto-dilated outlines (the amount of which is
1871 // determined by AppleFontSmoothing). Its regular anti-aliased output is
1872 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001873
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001874 // The behavior of Skia is as follows:
1875 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
1876 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
1877 // channel. This matches [LCD][yes-hint] in weight.
1878 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
1879 // Currenly side with LCD, effectively ignoring the hinting setting.
1880 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001881
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001882 if (isLCDFormat(rec->fMaskFormat)) {
1883 if (lcdSupport) {
1884 //CoreGraphics creates 555 masks for smoothed text anyway.
1885 rec->fMaskFormat = SkMask::kLCD16_Format;
1886 rec->setHinting(SkPaint::kNormal_Hinting);
1887 } else {
1888 rec->fMaskFormat = SkMask::kA8_Format;
1889 }
1890 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001891
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001892 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1893 // All other masks can use regular gamma.
1894 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
1895#ifndef SK_GAMMA_APPLY_TO_A8
1896 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00001897#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001898 } else {
1899 //CoreGraphics dialates smoothed text as needed.
1900 rec->setContrast(0);
1901 }
1902}
1903
1904// we take ownership of the ref
1905static const char* get_str(CFStringRef ref, SkString* str) {
1906 CFStringToSkString(ref, str);
1907 CFSafeRelease(ref);
1908 return str->c_str();
1909}
1910
reed@google.com5526ede2013-03-25 13:03:37 +00001911void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
1912 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001913 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001914
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001915 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
1916 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
1917 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
reed@google.com5526ede2013-03-25 13:03:37 +00001918 // TODO: need to add support for local-streams (here and openStream)
1919 *isLocalStream = false;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001920}
reed@google.com5526ede2013-03-25 13:03:37 +00001921
reed@google.combcb42ae2013-07-02 13:56:39 +00001922int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
1923 uint16_t glyphs[], int glyphCount) const {
1924 // UniChar is utf16
1925 SkAutoSTMalloc<1024, UniChar> charStorage;
1926 const UniChar* src;
1927 switch (encoding) {
1928 case kUTF8_Encoding: {
1929 const char* u8 = (const char*)chars;
1930 const UniChar* u16 = src = charStorage.reset(2 * glyphCount);
1931 for (int i = 0; i < glyphCount; ++i) {
1932 SkUnichar uni = SkUTF8_NextUnichar(&u8);
1933 int n = SkUTF16_FromUnichar(uni, (uint16_t*)u16);
1934 u16 += n;
1935 }
1936 break;
1937 }
1938 case kUTF16_Encoding:
1939 src = (const UniChar*)chars;
1940 break;
1941 case kUTF32_Encoding: {
1942 const SkUnichar* u32 = (const SkUnichar*)chars;
1943 const UniChar* u16 = src = charStorage.reset(2 * glyphCount);
1944 for (int i = 0; i < glyphCount; ++i) {
1945 int n = SkUTF16_FromUnichar(u32[i], (uint16_t*)u16);
1946 u16 += n;
1947 }
1948 break;
1949 }
1950 }
1951
1952 // Our caller may not want glyphs for output, but we need to give that
1953 // storage to CT, so we can walk it looking for the first non-zero.
1954 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
1955 uint16_t* macGlyphs = glyphs;
1956 if (NULL == macGlyphs) {
1957 macGlyphs = glyphStorage.reset(glyphCount);
1958 }
1959
1960 if (CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, glyphCount)) {
1961 return glyphCount;
1962 }
1963 // If we got false, then we need to manually look for first failure
1964 for (int i = 0; i < glyphCount; ++i) {
1965 if (0 == macGlyphs[i]) {
1966 return i;
1967 }
1968 }
1969 // odd to get here, as we expected CT to have returned true up front.
1970 return glyphCount;
1971}
1972
1973int SkTypeface_Mac::onCountGlyphs() const {
1974 return CTFontGetGlyphCount(fFontRef);
1975}
1976
reed@google.com95625db2013-03-25 20:44:02 +00001977///////////////////////////////////////////////////////////////////////////////
1978///////////////////////////////////////////////////////////////////////////////
reed@google.com95625db2013-03-25 20:44:02 +00001979#if 1
reed@google.com83787c52013-03-26 17:19:15 +00001980
1981static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
1982 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
1983 if (NULL == ref.get()) {
1984 return false;
1985 }
1986 CFStringToSkString(ref, value);
1987 return true;
1988}
1989
1990static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
1991 CFNumberRef num;
1992 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
1993 && CFNumberIsFloatType(num)
1994 && CFNumberGetValue(num, kCFNumberFloatType, value);
1995}
1996
reed@google.com95625db2013-03-25 20:44:02 +00001997#include "SkFontMgr.h"
1998
reed@google.com83787c52013-03-26 17:19:15 +00001999static int unit_weight_to_fontstyle(float unit) {
2000 float value;
2001 if (unit < 0) {
2002 value = 100 + (1 + unit) * 300;
2003 } else {
2004 value = 400 + unit * 500;
2005 }
2006 return sk_float_round2int(value);
2007}
2008
2009static int unit_width_to_fontstyle(float unit) {
2010 float value;
2011 if (unit < 0) {
2012 value = 1 + (1 + unit) * 4;
2013 } else {
2014 value = 5 + unit * 4;
2015 }
2016 return sk_float_round2int(value);
2017}
2018
reed@google.com964988f2013-03-29 14:57:22 +00002019static inline int sqr(int value) {
2020 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2021 return value * value;
2022}
2023
2024// We normalize each axis (weight, width, italic) to be base-900
2025static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2026 return sqr(a.weight() - b.weight()) +
2027 sqr((a.width() - b.width()) * 100) +
2028 sqr((a.isItalic() != b.isItalic()) * 900);
2029}
2030
reed@google.com83787c52013-03-26 17:19:15 +00002031static SkFontStyle desc2fontstyle(CTFontDescriptorRef desc) {
2032 AutoCFRelease<CFDictionaryRef> dict(
2033 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc,
2034 kCTFontTraitsAttribute));
2035 if (NULL == dict.get()) {
2036 return SkFontStyle();
2037 }
2038
2039 float weight, width, slant;
2040 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
2041 weight = 0;
2042 }
2043 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
2044 width = 0;
2045 }
2046 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
2047 slant = 0;
2048 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002049
reed@google.com83787c52013-03-26 17:19:15 +00002050 return SkFontStyle(unit_weight_to_fontstyle(weight),
2051 unit_width_to_fontstyle(width),
2052 slant ? SkFontStyle::kItalic_Slant
2053 : SkFontStyle::kUpright_Slant);
2054}
2055
reed@google.comdea7ee02013-03-28 14:12:10 +00002056struct NameFontStyleRec {
2057 SkString fFamilyName;
2058 SkFontStyle fFontStyle;
2059};
2060
2061static bool nameFontStyleProc(SkTypeface* face, SkTypeface::Style,
2062 void* ctx) {
2063 SkTypeface_Mac* macFace = (SkTypeface_Mac*)face;
2064 const NameFontStyleRec* rec = (const NameFontStyleRec*)ctx;
2065
2066 return macFace->fFontStyle == rec->fFontStyle &&
2067 macFace->fName == rec->fFamilyName;
2068}
2069
reed@google.comce8b3de2013-03-26 19:30:16 +00002070static SkTypeface* createFromDesc(CFStringRef cfFamilyName,
2071 CTFontDescriptorRef desc) {
reed@google.comdea7ee02013-03-28 14:12:10 +00002072 NameFontStyleRec rec;
2073 CFStringToSkString(cfFamilyName, &rec.fFamilyName);
2074 rec.fFontStyle = desc2fontstyle(desc);
2075
2076 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(nameFontStyleProc,
2077 &rec);
2078 if (face) {
2079 return face;
2080 }
2081
reed@google.comce8b3de2013-03-26 19:30:16 +00002082 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFamilyName, 1, NULL));
2083 CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc);
2084 if (NULL == ctFont) {
2085 return NULL;
2086 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002087
reed@google.comce8b3de2013-03-26 19:30:16 +00002088 SkString str;
2089 CFStringToSkString(cfFamilyName, &str);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002090
reed@google.comce8b3de2013-03-26 19:30:16 +00002091 bool isFixedPitch;
2092 (void)computeStyleBits(ctFont, &isFixedPitch);
2093 SkFontID fontID = CTFontRef_to_SkFontID(ctFont);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002094
reed@google.comdea7ee02013-03-28 14:12:10 +00002095 face = SkNEW_ARGS(SkTypeface_Mac, (rec.fFontStyle, fontID, isFixedPitch,
2096 ctFont, str.c_str()));
2097 SkTypefaceCache::Add(face, face->style());
2098 return face;
reed@google.comce8b3de2013-03-26 19:30:16 +00002099}
2100
reed@google.com83787c52013-03-26 17:19:15 +00002101class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002102public:
reed@google.com83787c52013-03-26 17:19:15 +00002103 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
2104 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, NULL))
reed@google.comdea7ee02013-03-28 14:12:10 +00002105 , fFamilyName(familyName)
2106 , fCount(0) {
reed@google.com83787c52013-03-26 17:19:15 +00002107 CFRetain(familyName);
reed@google.comdea7ee02013-03-28 14:12:10 +00002108 if (NULL == fArray) {
2109 fArray = CFArrayCreate(NULL, NULL, 0, NULL);
2110 }
2111 fCount = CFArrayGetCount(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002112 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002113
reed@google.com83787c52013-03-26 17:19:15 +00002114 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002115 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002116 CFRelease(fFamilyName);
2117 }
2118
2119 virtual int count() SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002120 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002121 }
2122
2123 virtual void getStyle(int index, SkFontStyle* style,
2124 SkString* name) SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002125 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002126 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2127 if (style) {
2128 *style = desc2fontstyle(desc);
2129 }
2130 if (name) {
2131 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2132 name->reset();
2133 }
2134 }
2135 }
2136
2137 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
2138 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2139 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002140
reed@google.com83787c52013-03-26 17:19:15 +00002141 return createFromDesc(fFamilyName, desc);
2142 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002143
reed@google.com964988f2013-03-29 14:57:22 +00002144 virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
2145 if (0 == fCount) {
2146 return NULL;
2147 }
2148 return createFromDesc(fFamilyName, findMatchingDesc(pattern));
2149 }
2150
reed@google.com83787c52013-03-26 17:19:15 +00002151private:
2152 CFArrayRef fArray;
2153 CFStringRef fFamilyName;
reed@google.comdea7ee02013-03-28 14:12:10 +00002154 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002155
2156 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2157 int bestMetric = SK_MaxS32;
2158 CTFontDescriptorRef bestDesc = NULL;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002159
reed@google.com964988f2013-03-29 14:57:22 +00002160 for (int i = 0; i < fCount; ++i) {
2161 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
2162 int metric = compute_metric(pattern, desc2fontstyle(desc));
2163 if (0 == metric) {
2164 return desc;
2165 }
2166 if (metric < bestMetric) {
2167 bestMetric = metric;
2168 bestDesc = desc;
2169 }
2170 }
2171 SkASSERT(bestDesc);
2172 return bestDesc;
2173 }
reed@google.com83787c52013-03-26 17:19:15 +00002174};
2175
2176class SkFontMgr_Mac : public SkFontMgr {
2177 int fCount;
2178 CFArrayRef fNames;
2179
2180 CFStringRef stringAt(int index) const {
2181 SkASSERT((unsigned)index < (unsigned)fCount);
2182 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2183 }
2184
2185 void lazyInit() {
2186 if (NULL == fNames) {
reed@google.com3dcbd462013-03-27 13:56:34 +00002187 fNames = SkCTFontManagerCopyAvailableFontFamilyNames();
reed@google.com83787c52013-03-26 17:19:15 +00002188 fCount = fNames ? CFArrayGetCount(fNames) : 0;
2189 }
2190 }
2191
reed@google.com964988f2013-03-29 14:57:22 +00002192 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2193 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2194 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2195 &kCFTypeDictionaryKeyCallBacks,
2196 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002197
reed@google.com964988f2013-03-29 14:57:22 +00002198 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002199
reed@google.com964988f2013-03-29 14:57:22 +00002200 AutoCFRelease<CTFontDescriptorRef> desc(
2201 CTFontDescriptorCreateWithAttributes(cfAttr));
2202 return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc));
2203 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002204
reed@google.com83787c52013-03-26 17:19:15 +00002205public:
2206 SkFontMgr_Mac() : fCount(0), fNames(NULL) {}
2207
2208 virtual ~SkFontMgr_Mac() {
2209 CFSafeRelease(fNames);
2210 }
reed@google.com95625db2013-03-25 20:44:02 +00002211
2212protected:
2213 virtual int onCountFamilies() SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002214 this->lazyInit();
2215 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002216 }
2217
2218 virtual void onGetFamilyName(int index, SkString* familyName) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002219 this->lazyInit();
2220 if ((unsigned)index < (unsigned)fCount) {
2221 CFStringToSkString(this->stringAt(index), familyName);
2222 } else {
2223 familyName->reset();
2224 }
reed@google.com95625db2013-03-25 20:44:02 +00002225 }
2226
2227 virtual SkFontStyleSet* onCreateStyleSet(int index) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002228 this->lazyInit();
2229 if ((unsigned)index >= (unsigned)fCount) {
2230 return NULL;
2231 }
reed@google.com964988f2013-03-29 14:57:22 +00002232 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002233 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002234
reed@google.com964988f2013-03-29 14:57:22 +00002235 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) SK_OVERRIDE {
2236 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2237 return CreateSet(cfName);
2238 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002239
reed@google.com95625db2013-03-25 20:44:02 +00002240 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
2241 const SkFontStyle&) SK_OVERRIDE {
2242 return NULL;
2243 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002244
reed@google.com95625db2013-03-25 20:44:02 +00002245 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2246 const SkFontStyle&) SK_OVERRIDE {
2247 return NULL;
2248 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002249
reed@google.com95625db2013-03-25 20:44:02 +00002250 virtual SkTypeface* onCreateFromData(SkData* data,
2251 int ttcIndex) SK_OVERRIDE {
2252 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
2253 if (NULL == pr) {
2254 return NULL;
2255 }
2256 return create_from_dataProvider(pr);
2257 }
2258
2259 virtual SkTypeface* onCreateFromStream(SkStream* stream,
2260 int ttcIndex) SK_OVERRIDE {
2261 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
2262 if (NULL == pr) {
2263 return NULL;
2264 }
2265 return create_from_dataProvider(pr);
2266 }
2267
2268 virtual SkTypeface* onCreateFromFile(const char path[],
2269 int ttcIndex) SK_OVERRIDE {
2270 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2271 if (NULL == pr) {
2272 return NULL;
2273 }
2274 return create_from_dataProvider(pr);
2275 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002276
reed@google.com7fdcd442013-07-30 21:25:49 +00002277 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
2278 unsigned styleBits) SK_OVERRIDE {
2279 return create_typeface(NULL, familyName, (SkTypeface::Style)styleBits);
2280 }
reed@google.com95625db2013-03-25 20:44:02 +00002281};
2282
reed@google.com7fdcd442013-07-30 21:25:49 +00002283///////////////////////////////////////////////////////////////////////////////
2284
2285#ifndef SK_FONTHOST_USES_FONTMGR
2286
2287SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
2288 const char familyName[],
2289 SkTypeface::Style style) {
2290 return create_typeface(familyFace, familyName, style);
2291}
2292
2293SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
2294 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
2295 if (NULL == provider) {
2296 return NULL;
2297 }
2298 return create_from_dataProvider(provider);
2299}
2300
2301SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
2302 AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
2303 if (NULL == provider) {
2304 return NULL;
2305 }
2306 return create_from_dataProvider(provider);
2307}
2308
2309#endif
2310
reed@google.com95625db2013-03-25 20:44:02 +00002311SkFontMgr* SkFontMgr::Factory() {
2312 return SkNEW(SkFontMgr_Mac);
2313}
2314#endif