blob: d204b1361dfeadefe3daed9422a6e049d3767eb0 [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>
16#include <CoreGraphics/CoreGraphics.h>
17#include <CoreFoundation/CoreFoundation.h>
18#endif
19
20#include "SkFontHost.h"
21#include "SkCGUtils.h"
22#include "SkColorPriv.h"
23#include "SkDescriptor.h"
24#include "SkEndian.h"
25#include "SkFontDescriptor.h"
26#include "SkFloatingPoint.h"
27#include "SkGlyph.h"
28#include "SkMaskGamma.h"
29#include "SkSFNTHeader.h"
30#include "SkOTTable_glyf.h"
31#include "SkOTTable_head.h"
32#include "SkOTTable_hhea.h"
33#include "SkOTTable_loca.h"
34#include "SkOTUtils.h"
35#include "SkPaint.h"
36#include "SkPath.h"
37#include "SkString.h"
38#include "SkStream.h"
39#include "SkThread.h"
40#include "SkTypeface_mac.h"
41#include "SkUtils.h"
42#include "SkTypefaceCache.h"
reed@google.comce8b3de2013-03-26 19:30:16 +000043#include "SkFontMgr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000044
45class SkScalerContext_Mac;
46
47// Being templated and taking const T* prevents calling
48// CFSafeRelease(autoCFRelease) through implicit conversion.
49template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
50 if (cfTypeRef) {
51 CFRelease(cfTypeRef);
52 }
53}
54
55// Being templated and taking const T* prevents calling
56// CFSafeRetain(autoCFRelease) through implicit conversion.
57template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
58 if (cfTypeRef) {
59 CFRetain(cfTypeRef);
60 }
61}
62
63/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
64template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
65public:
66 explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
67 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
68
69 void reset(CFRef that = NULL) {
70 CFSafeRetain(that);
71 CFSafeRelease(fCFRef);
72 fCFRef = that;
73 }
74
75 AutoCFRelease& operator =(CFRef that) {
76 reset(that);
77 return *this;
78 }
79
80 operator CFRef() const { return fCFRef; }
81 CFRef get() const { return fCFRef; }
82
83private:
84 CFRef fCFRef;
85};
86
87template<typename T> class AutoCGTable : SkNoncopyable {
88public:
89 AutoCGTable(CGFontRef font)
90 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
91 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
92 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
93 { }
94
95 const T* operator->() const { return fData; }
96
97private:
98 AutoCFRelease<CFDataRef> fCFData;
99public:
100 const T* fData;
101};
102
103// inline versions of these rect helpers
104
105static bool CGRectIsEmpty_inline(const CGRect& rect) {
106 return rect.size.width <= 0 || rect.size.height <= 0;
107}
108
109static void CGRectInset_inline(CGRect* rect, CGFloat dx, CGFloat dy) {
110 rect->origin.x += dx;
111 rect->origin.y += dy;
112 rect->size.width -= dx * 2;
113 rect->size.height -= dy * 2;
114}
115
116static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
117 return rect.origin.x;
118}
119
120static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
121 return rect.origin.x + rect.size.width;
122}
123
124static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
125 return rect.origin.y;
126}
127
128static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
129 return rect.origin.y + rect.size.height;
130}
131
132static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
133 return rect.size.width;
134}
135
136///////////////////////////////////////////////////////////////////////////////
137
138static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
139 size_t width, size_t height, size_t rowBytes) {
140 SkASSERT(width);
141 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
142
143 if (width >= 32) {
144 while (height) {
145 sk_memset32(ptr, value, width);
146 ptr = (uint32_t*)((char*)ptr + rowBytes);
147 height -= 1;
148 }
149 return;
150 }
151
152 rowBytes -= width * sizeof(uint32_t);
153
154 if (width >= 8) {
155 while (height) {
156 int w = width;
157 do {
158 *ptr++ = value; *ptr++ = value;
159 *ptr++ = value; *ptr++ = value;
160 *ptr++ = value; *ptr++ = value;
161 *ptr++ = value; *ptr++ = value;
162 w -= 8;
163 } while (w >= 8);
164 while (--w >= 0) {
165 *ptr++ = value;
166 }
167 ptr = (uint32_t*)((char*)ptr + rowBytes);
168 height -= 1;
169 }
170 } else {
171 while (height) {
172 int w = width;
173 do {
174 *ptr++ = value;
175 } while (--w > 0);
176 ptr = (uint32_t*)((char*)ptr + rowBytes);
177 height -= 1;
178 }
179 }
180}
181
182#include <sys/utsname.h>
183
184typedef uint32_t CGRGBPixel;
185
186static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
187 return pixel & 0xFF;
188}
189
190// The calls to support subpixel are present in 10.5, but are not included in
191// the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are
192// included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for
193// instance, is present in the 10.5 CoreGraphics libary, use:
194// cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/
195// cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/
196// nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts
197
198#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
199CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value);
200CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value);
201CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value);
202CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value);
203CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value);
204#endif
205
206static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
207
208// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
209static int readVersion() {
210 struct utsname info;
211 if (uname(&info) != 0) {
212 SkDebugf("uname failed\n");
213 return 0;
214 }
215 if (strcmp(info.sysname, "Darwin") != 0) {
216 SkDebugf("unexpected uname sysname %s\n", info.sysname);
217 return 0;
218 }
219 char* dot = strchr(info.release, '.');
220 if (!dot) {
221 SkDebugf("expected dot in uname release %s\n", info.release);
222 return 0;
223 }
224 int version = atoi(info.release);
225 if (version == 0) {
226 SkDebugf("could not parse uname release %s\n", info.release);
227 }
228 return version;
229}
230
231static int darwinVersion() {
232 static int darwin_version = readVersion();
233 return darwin_version;
234}
235
236static bool isLeopard() {
237 return darwinVersion() == 9;
238}
239
240static bool isSnowLeopard() {
241 return darwinVersion() == 10;
242}
243
244static bool isLion() {
245 return darwinVersion() == 11;
246}
247
248static bool isMountainLion() {
249 return darwinVersion() == 12;
250}
251
252static bool isLCDFormat(unsigned format) {
253 return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
254}
255
256static CGFloat ScalarToCG(SkScalar scalar) {
257 if (sizeof(CGFloat) == sizeof(float)) {
258 return SkScalarToFloat(scalar);
259 } else {
260 SkASSERT(sizeof(CGFloat) == sizeof(double));
261 return (CGFloat) SkScalarToDouble(scalar);
262 }
263}
264
265static SkScalar CGToScalar(CGFloat cgFloat) {
266 if (sizeof(CGFloat) == sizeof(float)) {
267 return SkFloatToScalar(cgFloat);
268 } else {
269 SkASSERT(sizeof(CGFloat) == sizeof(double));
270 return SkDoubleToScalar(cgFloat);
271 }
272}
273
274static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
275 SkScalar sx = SK_Scalar1,
276 SkScalar sy = SK_Scalar1) {
277 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
278 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
279 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
280 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
281 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
282 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
283}
284
285static SkScalar getFontScale(CGFontRef cgFont) {
286 int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
287 return SkScalarInvert(SkIntToScalar(unitsPerEm));
288}
289
290///////////////////////////////////////////////////////////////////////////////
291
292#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
293#define BITMAP_INFO_GRAY (kCGImageAlphaNone)
294
295/**
296 * There does not appear to be a publicly accessable API for determining if lcd
297 * font smoothing will be applied if we request it. The main issue is that if
298 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
299 */
300static bool supports_LCD() {
301 static int gSupportsLCD = -1;
302 if (gSupportsLCD >= 0) {
303 return (bool) gSupportsLCD;
304 }
305 uint32_t rgb = 0;
306 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
307 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
308 colorspace, BITMAP_INFO_RGB));
309 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
310 CGContextSetShouldSmoothFonts(cgContext, true);
311 CGContextSetShouldAntialias(cgContext, true);
312 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
313 CGContextSetGrayFillColor(cgContext, 1, 1);
314 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
315 uint32_t r = (rgb >> 16) & 0xFF;
316 uint32_t g = (rgb >> 8) & 0xFF;
317 uint32_t b = (rgb >> 0) & 0xFF;
318 gSupportsLCD = (r != g || r != b);
319 return (bool) gSupportsLCD;
320}
321
322class Offscreen {
323public:
324 Offscreen();
325
326 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
327 CGGlyph glyphID, size_t* rowBytesPtr,
328 bool generateA8FromLCD);
329
330private:
331 enum {
332 kSize = 32 * 32 * sizeof(CGRGBPixel)
333 };
334 SkAutoSMalloc<kSize> fImageStorage;
335 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
336
337 // cached state
338 AutoCFRelease<CGContextRef> fCG;
339 SkISize fSize;
340 bool fDoAA;
341 bool fDoLCD;
342
343 static int RoundSize(int dimension) {
344 return SkNextPow2(dimension);
345 }
346};
347
348Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL) {
349 fSize.set(0, 0);
350}
351
352///////////////////////////////////////////////////////////////////////////////
353
bungeman@google.comfe747652013-03-25 19:36:11 +0000354static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000355 unsigned style = SkTypeface::kNormal;
356 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
357
358 if (traits & kCTFontBoldTrait) {
359 style |= SkTypeface::kBold;
360 }
361 if (traits & kCTFontItalicTrait) {
362 style |= SkTypeface::kItalic;
363 }
bungeman@google.comfe747652013-03-25 19:36:11 +0000364 if (isFixedPitch) {
365 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000366 }
367 return (SkTypeface::Style)style;
368}
369
370static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
371 SkFontID id = 0;
372// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
373// bracket this to be Mac only.
374#ifdef SK_BUILD_FOR_MAC
375 ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
376 id = (SkFontID)ats;
377 if (id != 0) {
378 id &= 0x3FFFFFFF; // make top two bits 00
379 return id;
380 }
381#endif
382 // CTFontGetPlatformFont returns NULL if the font is local
383 // (e.g., was created by a CSS3 @font-face rule).
384 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
385 AutoCGTable<SkOTTableHead> headTable(cgFont);
386 if (headTable.fData) {
387 id = (SkFontID) headTable->checksumAdjustment;
388 id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
389 }
390 // well-formed fonts have checksums, but as a last resort, use the pointer.
391 if (id == 0) {
392 id = (SkFontID) (uintptr_t) fontRef;
393 id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
394 }
395 return id;
396}
397
reed@google.comce8b3de2013-03-26 19:30:16 +0000398static SkFontStyle stylebits2fontstyle(SkTypeface::Style styleBits) {
399 return SkFontStyle((styleBits & SkTypeface::kBold)
400 ? SkFontStyle::kBold_Weight
401 : SkFontStyle::kNormal_Weight,
402 SkFontStyle::kNormal_Width,
403 (styleBits & SkTypeface::kItalic)
404 ? SkFontStyle::kItalic_Slant
405 : SkFontStyle::kUpright_Slant);
406}
407
408#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
409
410static SkTypeface::Style fontstyle2stylebits(const SkFontStyle& fs) {
411 unsigned style = 0;
412 if (fs.width() >= WEIGHT_THRESHOLD) {
413 style |= SkTypeface::kBold;
414 }
415 if (fs.isItalic()) {
416 style |= SkTypeface::kItalic;
417 }
418 return (SkTypeface::Style)style;
419}
420
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000421class SkTypeface_Mac : public SkTypeface {
422public:
bungeman@google.comfe747652013-03-25 19:36:11 +0000423 SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isFixedPitch,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000424 CTFontRef fontRef, const char name[])
reed@google.comce8b3de2013-03-26 19:30:16 +0000425 : SkTypeface(style, fontID, isFixedPitch)
426 , fName(name)
427 , fFontRef(fontRef) // caller has already called CFRetain for us
428 , fFontStyle(stylebits2fontstyle(style))
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000429 {
430 SkASSERT(fontRef);
431 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000432
reed@google.comce8b3de2013-03-26 19:30:16 +0000433 SkTypeface_Mac(const SkFontStyle& fs, SkFontID fontID, bool isFixedPitch,
434 CTFontRef fontRef, const char name[])
435 : SkTypeface(fontstyle2stylebits(fs), fontID, isFixedPitch)
436 , fName(name)
437 , fFontRef(fontRef) // caller has already called CFRetain for us
438 , fFontStyle(fs)
439 {
440 SkASSERT(fontRef);
441 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000442
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000443 SkString fName;
444 AutoCFRelease<CTFontRef> fFontRef;
reed@google.comce8b3de2013-03-26 19:30:16 +0000445 SkFontStyle fFontStyle;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000446
447protected:
448 friend class SkFontHost; // to access our protected members for deprecated methods
449
450 virtual int onGetUPEM() const SK_OVERRIDE;
reed@google.comcc9aad52013-03-21 19:28:10 +0000451 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000452 virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
453 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
454 size_t length, void* data) const SK_OVERRIDE;
455 virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
456 virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
reed@google.com5526ede2013-03-25 13:03:37 +0000457 virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
reed@google.com2689f612013-03-20 20:01:47 +0000458 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
459 SkAdvancedTypefaceMetrics::PerGlyphInfo,
460 const uint32_t*, uint32_t) const SK_OVERRIDE;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000461
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000462private:
reed@google.comce8b3de2013-03-26 19:30:16 +0000463
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000464 typedef SkTypeface INHERITED;
465};
466
467static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
468 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000469 bool isFixedPitch;
470 SkTypeface::Style style = computeStyleBits(fontRef, &isFixedPitch);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000471 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
472
bungeman@google.comfe747652013-03-25 19:36:11 +0000473 return new SkTypeface_Mac(style, fontID, isFixedPitch, fontRef, name);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000474}
475
476static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
477 CTFontRef ctFont = NULL;
478
479 CTFontSymbolicTraits ctFontTraits = 0;
480 if (theStyle & SkTypeface::kBold) {
481 ctFontTraits |= kCTFontBoldTrait;
482 }
483 if (theStyle & SkTypeface::kItalic) {
484 ctFontTraits |= kCTFontItalicTrait;
485 }
486
487 // Create the font info
488 AutoCFRelease<CFStringRef> cfFontName(
489 CFStringCreateWithCString(NULL, familyName, kCFStringEncodingUTF8));
490
491 AutoCFRelease<CFNumberRef> cfFontTraits(
492 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
493
494 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
495 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
496 &kCFTypeDictionaryKeyCallBacks,
497 &kCFTypeDictionaryValueCallBacks));
498
499 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
500 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
501 &kCFTypeDictionaryKeyCallBacks,
502 &kCFTypeDictionaryValueCallBacks));
503
504 // Create the font
505 if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
506 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
507
508 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
509 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
510
511 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
512 CTFontDescriptorCreateWithAttributes(cfAttributes));
513
514 if (ctFontDesc != NULL) {
515 if (isLeopard()) {
516 // CTFontCreateWithFontDescriptor on Leopard ignores the name
517 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFontName, 1, NULL));
518 ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, ctFontDesc);
519 } else {
520 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
521 }
522 }
523 }
524
525 return ctFont ? NewFromFontRef(ctFont, familyName) : NULL;
526}
527
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000528static SkTypeface* GetDefaultFace() {
529 SK_DECLARE_STATIC_MUTEX(gMutex);
530 SkAutoMutexAcquire ma(gMutex);
531
532 static SkTypeface* gDefaultFace;
533
534 if (NULL == gDefaultFace) {
535 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal);
536 SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal);
537 }
538 return gDefaultFace;
539}
540
541///////////////////////////////////////////////////////////////////////////////
542
543extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
544CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
545 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
546 return macface ? macface->fFontRef.get() : NULL;
547}
548
549/* This function is visible on the outside. It first searches the cache, and if
550 * not found, returns a new entry (after adding it to the cache).
551 */
552SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
553 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
554 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
555 if (face) {
556 face->ref();
557 } else {
558 face = NewFromFontRef(fontRef, NULL);
559 SkTypefaceCache::Add(face, face->style());
560 // NewFromFontRef doesn't retain the parameter, but the typeface it
561 // creates does release it in its destructor, so we balance that with
562 // a retain call here.
563 CFRetain(fontRef);
564 }
565 SkASSERT(face->getRefCnt() > 1);
566 return face;
567}
568
569struct NameStyleRec {
570 const char* fName;
571 SkTypeface::Style fStyle;
572};
573
574static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style,
575 void* ctx) {
576 const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face);
577 const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx);
578
579 return rec->fStyle == style && mface->fName.equals(rec->fName);
580}
581
582static const char* map_css_names(const char* name) {
583 static const struct {
584 const char* fFrom; // name the caller specified
585 const char* fTo; // "canonical" name we map to
586 } gPairs[] = {
587 { "sans-serif", "Helvetica" },
588 { "serif", "Times" },
589 { "monospace", "Courier" }
590 };
591
592 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
593 if (strcmp(name, gPairs[i].fFrom) == 0) {
594 return gPairs[i].fTo;
595 }
596 }
597 return name; // no change
598}
599
600SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
601 const char familyName[],
602 SkTypeface::Style style) {
603 if (familyName) {
604 familyName = map_css_names(familyName);
605 }
606
607 // Clone an existing typeface
608 // TODO: only clone if style matches the familyFace's style...
609 if (familyName == NULL && familyFace != NULL) {
610 familyFace->ref();
611 return const_cast<SkTypeface*>(familyFace);
612 }
613
614 if (!familyName || !*familyName) {
615 familyName = FONT_DEFAULT_NAME;
616 }
617
618 NameStyleRec rec = { familyName, style };
619 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec);
620
621 if (NULL == face) {
622 face = NewFromName(familyName, style);
623 if (face) {
624 SkTypefaceCache::Add(face, style);
625 } else {
626 face = GetDefaultFace();
627 face->ref();
628 }
629 }
630 return face;
631}
632
633static void flip(SkMatrix* matrix) {
634 matrix->setSkewX(-matrix->getSkewX());
635 matrix->setSkewY(-matrix->getSkewY());
636}
637
638///////////////////////////////////////////////////////////////////////////////
639
640struct GlyphRect {
641 int16_t fMinX;
642 int16_t fMinY;
643 int16_t fMaxX;
644 int16_t fMaxY;
645};
646
647class SkScalerContext_Mac : public SkScalerContext {
648public:
reed@google.com0da48612013-03-19 16:06:52 +0000649 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
650 virtual ~SkScalerContext_Mac();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000651
652
653protected:
654 unsigned generateGlyphCount(void) SK_OVERRIDE;
655 uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
656 void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
657 void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
658 void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
659 void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
660 void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
661
662private:
663 static void CTPathElement(void *info, const CGPathElement *element);
664 uint16_t getFBoundingBoxesGlyphOffset();
665 void getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const;
666 bool generateBBoxes();
667
668 CGAffineTransform fTransform;
669 SkMatrix fUnitMatrix; // without font size
670 SkMatrix fVerticalMatrix; // unit rotated
671 SkMatrix fMatrix; // with font size
672 SkMatrix fFBoundingBoxesMatrix; // lion-specific fix
673 Offscreen fOffscreen;
674 AutoCFRelease<CTFontRef> fCTFont;
675 AutoCFRelease<CTFontRef> fCTVerticalFont; // for vertical advance
676 AutoCFRelease<CGFontRef> fCGFont;
677 GlyphRect* fFBoundingBoxes;
678 uint16_t fFBoundingBoxesGlyphOffset;
679 uint16_t fGlyphCount;
680 bool fGeneratedFBoundingBoxes;
681 bool fDoSubPosition;
682 bool fVertical;
683
684 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000685
686 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000687};
688
reed@google.com0da48612013-03-19 16:06:52 +0000689SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
690 const SkDescriptor* desc)
691 : INHERITED(typeface, desc)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000692 , fFBoundingBoxes(NULL)
693 , fFBoundingBoxesGlyphOffset(0)
694 , fGeneratedFBoundingBoxes(false)
695{
reed@google.com2689f612013-03-20 20:01:47 +0000696 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000697 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
698
699 // Get the state we need
700 fRec.getSingleMatrix(&fMatrix);
701 fUnitMatrix = fMatrix;
702
703 // extract the font size out of the matrix, but leave the skewing for italic
704 SkScalar reciprocal = SkScalarInvert(fRec.fTextSize);
705 fUnitMatrix.preScale(reciprocal, reciprocal);
706
707 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
708
709 fTransform = MatrixToCGAffineTransform(fMatrix);
710
711 CGAffineTransform transform;
712 CGFloat unitFontSize;
713 if (isLeopard()) {
714 // passing 1 for pointSize to Leopard sets the font size to 1 pt.
715 // pass the CoreText size explicitly
716 transform = MatrixToCGAffineTransform(fUnitMatrix);
717 unitFontSize = SkScalarToFloat(fRec.fTextSize);
718 } else {
719 // since our matrix includes everything, we pass 1 for pointSize
720 transform = fTransform;
721 unitFontSize = 1;
722 }
723 flip(&fUnitMatrix); // flip to fix up bounds later
724 fVertical = SkToBool(fRec.fFlags & kVertical_Flag);
725 AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
726 if (fVertical) {
727 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
728 kCFAllocatorDefault, 0,
729 &kCFTypeDictionaryKeyCallBacks,
730 &kCFTypeDictionaryValueCallBacks));
731 if (cfAttributes) {
732 CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
733 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
734 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
735 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
736 ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
737 }
738 }
739 fCTFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform, ctFontDesc);
740 fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL);
741 if (fVertical) {
742 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
743 transform = CGAffineTransformConcat(rotateLeft, transform);
744 fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform, NULL);
745 fVerticalMatrix = fUnitMatrix;
746 if (isSnowLeopard()) {
747 SkScalar scale = SkScalarMul(fRec.fTextSize, getFontScale(fCGFont));
748 fVerticalMatrix.preScale(scale, scale);
749 } else {
750 fVerticalMatrix.preRotate(SkIntToScalar(90));
751 }
752 fVerticalMatrix.postScale(SK_Scalar1, -SK_Scalar1);
753 }
754 fGlyphCount = SkToU16(numGlyphs);
755 fDoSubPosition = SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
756}
757
758SkScalerContext_Mac::~SkScalerContext_Mac() {
759 delete[] fFBoundingBoxes;
760}
761
762CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
763 CGGlyph glyphID, size_t* rowBytesPtr,
764 bool generateA8FromLCD) {
765 if (!fRGBSpace) {
766 //It doesn't appear to matter what color space is specified.
767 //Regular blends and antialiased text are always (s*a + d*(1-a))
768 //and smoothed text is always g=2.0.
769 fRGBSpace = CGColorSpaceCreateDeviceRGB();
770 }
771
772 // default to kBW_Format
773 bool doAA = false;
774 bool doLCD = false;
775
776 if (SkMask::kBW_Format != glyph.fMaskFormat) {
777 doLCD = true;
778 doAA = true;
779 }
780
781 // FIXME: lcd smoothed un-hinted rasterization unsupported.
782 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
783 doLCD = false;
784 doAA = true;
785 }
786
787 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
788 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
789 if (fSize.fWidth < glyph.fWidth) {
790 fSize.fWidth = RoundSize(glyph.fWidth);
791 }
792 if (fSize.fHeight < glyph.fHeight) {
793 fSize.fHeight = RoundSize(glyph.fHeight);
794 }
795
796 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
797 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
798 fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
799 rowBytes, fRGBSpace, BITMAP_INFO_RGB);
800
801 // skia handles quantization itself, so we disable this for cg to get
802 // full fractional data from them.
803 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
804 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
805
806 CGContextSetTextDrawingMode(fCG, kCGTextFill);
807 CGContextSetFont(fCG, context.fCGFont);
808 CGContextSetFontSize(fCG, 1);
809 CGContextSetTextMatrix(fCG, context.fTransform);
810
811 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition);
812 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition);
813
814 // Draw white on black to create mask.
815 // TODO: Draw black on white and invert, CG has a special case codepath.
816 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
817
818 // force our checks below to happen
819 fDoAA = !doAA;
820 fDoLCD = !doLCD;
821 }
822
823 if (fDoAA != doAA) {
824 CGContextSetShouldAntialias(fCG, doAA);
825 fDoAA = doAA;
826 }
827 if (fDoLCD != doLCD) {
828 CGContextSetShouldSmoothFonts(fCG, doLCD);
829 fDoLCD = doLCD;
830 }
831
832 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
833 // skip rows based on the glyph's height
834 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
835
836 // erase to black
837 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
838
839 float subX = 0;
840 float subY = 0;
841 if (context.fDoSubPosition) {
842 subX = SkFixedToFloat(glyph.getSubXFixed());
843 subY = SkFixedToFloat(glyph.getSubYFixed());
844 }
845 if (context.fVertical) {
846 SkIPoint offset;
847 context.getVerticalOffset(glyphID, &offset);
848 subX += offset.fX;
849 subY += offset.fY;
850 }
851 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
852 glyph.fTop + glyph.fHeight - subY,
853 &glyphID, 1);
854
855 SkASSERT(rowBytesPtr);
856 *rowBytesPtr = rowBytes;
857 return image;
858}
859
860void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const {
861 CGSize vertOffset;
862 CTFontGetVerticalTranslationsForGlyphs(fCTVerticalFont, &glyphID, &vertOffset, 1);
863 const SkPoint trans = {CGToScalar(vertOffset.width),
864 CGToScalar(vertOffset.height)};
865 SkPoint floatOffset;
866 fVerticalMatrix.mapPoints(&floatOffset, &trans, 1);
867 if (!isSnowLeopard()) {
868 // SnowLeopard fails to apply the font's matrix to the vertical metrics,
869 // but Lion and Leopard do. The unit matrix describes the font's matrix at
870 // point size 1. There may be some way to avoid mapping here by setting up
871 // fVerticalMatrix differently, but this works for now.
872 fUnitMatrix.mapPoints(&floatOffset, 1);
873 }
874 offset->fX = SkScalarRound(floatOffset.fX);
875 offset->fY = SkScalarRound(floatOffset.fY);
876}
877
878uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
879 if (fFBoundingBoxesGlyphOffset) {
880 return fFBoundingBoxesGlyphOffset;
881 }
882 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
883 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
884 if (hheaTable.fData) {
885 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
886 }
887 return fFBoundingBoxesGlyphOffset;
888}
reed@android.com0680d6c2008-12-19 19:46:15 +0000889
reed@android.comfeda2f92010-05-19 13:47:05 +0000890/*
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000891 * Lion has a bug in CTFontGetBoundingRectsForGlyphs which returns a bad value
892 * in theBounds.origin.x for fonts whose numOfLogHorMetrics is less than its
893 * glyph count. This workaround reads the glyph bounds from the font directly.
894 *
895 * The table is computed only if the font is a TrueType font, if the glyph
896 * value is >= fFBoundingBoxesGlyphOffset. (called only if fFBoundingBoxesGlyphOffset < fGlyphCount).
897 *
898 * TODO: A future optimization will compute fFBoundingBoxes once per CGFont, and
899 * compute fFBoundingBoxesMatrix once per font context.
900 */
901bool SkScalerContext_Mac::generateBBoxes() {
902 if (fGeneratedFBoundingBoxes) {
903 return NULL != fFBoundingBoxes;
904 }
905 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000906
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000907 AutoCGTable<SkOTTableHead> headTable(fCGFont);
908 if (!headTable.fData) {
909 return false;
910 }
911
912 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
913 if (!locaTable.fData) {
914 return false;
915 }
916
917 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
918 if (!glyfTable.fData) {
919 return false;
920 }
921
922 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
923 fFBoundingBoxes = new GlyphRect[entries];
924
925 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
926 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
927 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
928 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
929 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
930 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
931 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
932 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
933 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
934 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
935 }
936 fFBoundingBoxesMatrix = fMatrix;
937 flip(&fFBoundingBoxesMatrix);
938 SkScalar fontScale = getFontScale(fCGFont);
939 fFBoundingBoxesMatrix.preScale(fontScale, fontScale);
940 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
969void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
970 CGSize advance;
971 CGRect bounds;
972 CGGlyph cgGlyph;
973
974 // Get the state we need
975 cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
976
977 if (fVertical) {
978 if (!isSnowLeopard()) {
979 // Lion and Leopard respect the vertical font metrics.
980 CTFontGetBoundingRectsForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
981 &cgGlyph, &bounds, 1);
982 } else {
983 // Snow Leopard and earlier respect the vertical font metrics for
984 // advances, but not bounds, so use the default box and adjust it below.
985 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
986 &cgGlyph, &bounds, 1);
987 }
988 CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
989 &cgGlyph, &advance, 1);
990 } else {
991 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
992 &cgGlyph, &bounds, 1);
993 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontDefaultOrientation,
994 &cgGlyph, &advance, 1);
995 }
996
997 // BUG?
998 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
999 // it should be empty. So, if we see a zero-advance, we check if it has an
1000 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1001 // is rare, so we won't incur a big performance cost for this extra check.
1002 if (0 == advance.width && 0 == advance.height) {
1003 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
1004 if (NULL == path || CGPathIsEmpty(path)) {
1005 bounds = CGRectMake(0, 0, 0, 0);
1006 }
1007 }
1008
1009 glyph->zeroMetrics();
1010 glyph->fAdvanceX = SkFloatToFixed_Check(advance.width);
1011 glyph->fAdvanceY = -SkFloatToFixed_Check(advance.height);
1012
1013 if (CGRectIsEmpty_inline(bounds)) {
1014 return;
1015 }
1016
1017 if (isLeopard() && !fVertical) {
1018 // Leopard does not consider the matrix skew in its bounds.
1019 // Run the bounding rectangle through the skew matrix to determine
1020 // the true bounds. However, this doesn't work if the font is vertical.
1021 // FIXME (Leopard): If the font has synthetic italic (e.g., matrix skew)
1022 // and the font is vertical, the bounds need to be recomputed.
1023 SkRect glyphBounds = SkRect::MakeXYWH(
1024 bounds.origin.x, bounds.origin.y,
1025 bounds.size.width, bounds.size.height);
1026 fUnitMatrix.mapRect(&glyphBounds);
1027 bounds.origin.x = glyphBounds.fLeft;
1028 bounds.origin.y = glyphBounds.fTop;
1029 bounds.size.width = glyphBounds.width();
1030 bounds.size.height = glyphBounds.height();
1031 }
1032 // Adjust the bounds
1033 //
1034 // CTFontGetBoundingRectsForGlyphs ignores the font transform, so we need
1035 // to transform the bounding box ourselves.
1036 //
1037 // The bounds are also expanded by 1 pixel, to give CG room for anti-aliasing.
1038 CGRectInset_inline(&bounds, -1, -1);
1039
1040 // Get the metrics
1041 bool lionAdjustedMetrics = false;
1042 if (isLion() || isMountainLion()) {
1043 if (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()){
1044 lionAdjustedMetrics = true;
1045 SkRect adjust;
1046 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1047 adjust.set(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1048 fFBoundingBoxesMatrix.mapRect(&adjust);
1049 bounds.origin.x = SkScalarToFloat(adjust.fLeft) - 1;
1050 bounds.origin.y = SkScalarToFloat(adjust.fTop) - 1;
1051 }
1052 // Lion returns fractions in the bounds
1053 glyph->fWidth = SkToU16(sk_float_ceil2int(bounds.size.width));
1054 glyph->fHeight = SkToU16(sk_float_ceil2int(bounds.size.height));
1055 } else {
1056 glyph->fWidth = SkToU16(sk_float_round2int(bounds.size.width));
1057 glyph->fHeight = SkToU16(sk_float_round2int(bounds.size.height));
1058 }
1059 glyph->fTop = SkToS16(-sk_float_round2int(CGRectGetMaxY_inline(bounds)));
1060 glyph->fLeft = SkToS16(sk_float_round2int(CGRectGetMinX_inline(bounds)));
1061 SkIPoint offset;
1062 if (fVertical && (isSnowLeopard() || lionAdjustedMetrics)) {
1063 // SnowLeopard doesn't respect vertical metrics, so compute them manually.
1064 // Also compute them for Lion when the metrics were computed by hand.
1065 getVerticalOffset(cgGlyph, &offset);
1066 glyph->fLeft += offset.fX;
1067 glyph->fTop += offset.fY;
1068 }
1069}
1070
1071#include "SkColorPriv.h"
1072
1073static void build_power_table(uint8_t table[], float ee) {
1074 for (int i = 0; i < 256; i++) {
1075 float x = i / 255.f;
1076 x = sk_float_pow(x, ee);
1077 int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
1078 table[i] = SkToU8(xx);
1079 }
1080}
1081
1082/**
1083 * This will invert the gamma applied by CoreGraphics, so we can get linear
1084 * values.
1085 *
1086 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1087 * The color space used does not appear to affect this choice.
1088 */
1089static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1090 static bool gInited;
1091 static uint8_t gTableCoreGraphicsSmoothing[256];
1092 if (!gInited) {
1093 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1094 gInited = true;
1095 }
1096 return gTableCoreGraphicsSmoothing;
1097}
1098
1099static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1100 while (count > 0) {
1101 uint8_t mask = 0;
1102 for (int i = 7; i >= 0; --i) {
1103 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1104 if (0 == --count) {
1105 break;
1106 }
1107 }
1108 *dst++ = mask;
1109 }
1110}
1111
1112template<bool APPLY_PREBLEND>
1113static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1114 U8CPU r = (rgb >> 16) & 0xFF;
1115 U8CPU g = (rgb >> 8) & 0xFF;
1116 U8CPU b = (rgb >> 0) & 0xFF;
1117 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1118}
1119template<bool APPLY_PREBLEND>
1120static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1121 const SkGlyph& glyph, const uint8_t* table8) {
1122 const int width = glyph.fWidth;
1123 size_t dstRB = glyph.rowBytes();
1124 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1125
1126 for (int y = 0; y < glyph.fHeight; y++) {
1127 for (int i = 0; i < width; ++i) {
1128 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1129 }
1130 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1131 dst += dstRB;
1132 }
1133}
1134
1135template<bool APPLY_PREBLEND>
1136static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1137 const uint8_t* tableG,
1138 const uint8_t* tableB) {
1139 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1140 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1141 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1142 return SkPack888ToRGB16(r, g, b);
1143}
1144template<bool APPLY_PREBLEND>
1145static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1146 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1147 const int width = glyph.fWidth;
1148 size_t dstRB = glyph.rowBytes();
1149 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1150
1151 for (int y = 0; y < glyph.fHeight; y++) {
1152 for (int i = 0; i < width; i++) {
1153 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1154 }
1155 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1156 dst = (uint16_t*)((char*)dst + dstRB);
1157 }
1158}
1159
1160template<bool APPLY_PREBLEND>
1161static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
1162 const uint8_t* tableG,
1163 const uint8_t* tableB) {
1164 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1165 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1166 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1167 return SkPackARGB32(0xFF, r, g, b);
1168}
1169template<bool APPLY_PREBLEND>
1170static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1171 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1172 const int width = glyph.fWidth;
1173 size_t dstRB = glyph.rowBytes();
1174 uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
1175 for (int y = 0; y < glyph.fHeight; y++) {
1176 for (int i = 0; i < width; i++) {
1177 dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1178 }
1179 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1180 dst = (uint32_t*)((char*)dst + dstRB);
1181 }
1182}
1183
1184template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1185 return (T*)((char*)ptr + byteOffset);
1186}
1187
1188void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
1189 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
1190
1191 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1192 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1193
1194 // Draw the glyph
1195 size_t cgRowBytes;
1196 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
1197 if (cgPixels == NULL) {
1198 return;
1199 }
1200
1201 //TODO: see if drawing black on white and inverting is faster (at least in
1202 //lcd case) as core graphics appears to have special case code for drawing
1203 //black text.
1204
1205 // Fix the glyph
1206 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1207 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1208 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1209
1210 //Note that the following cannot really be integrated into the
1211 //pre-blend, since we may not be applying the pre-blend; when we aren't
1212 //applying the pre-blend it means that a filter wants linear anyway.
1213 //Other code may also be applying the pre-blend, so we'd need another
1214 //one with this and one without.
1215 CGRGBPixel* addr = cgPixels;
1216 for (int y = 0; y < glyph.fHeight; ++y) {
1217 for (int x = 0; x < glyph.fWidth; ++x) {
1218 int r = (addr[x] >> 16) & 0xFF;
1219 int g = (addr[x] >> 8) & 0xFF;
1220 int b = (addr[x] >> 0) & 0xFF;
1221 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1222 }
1223 addr = SkTAddByteOffset(addr, cgRowBytes);
1224 }
1225 }
1226
1227 // Convert glyph to mask
1228 switch (glyph.fMaskFormat) {
1229 case SkMask::kLCD32_Format: {
1230 if (fPreBlend.isApplicable()) {
1231 rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
1232 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1233 } else {
1234 rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
1235 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1236 }
1237 } break;
1238 case SkMask::kLCD16_Format: {
1239 if (fPreBlend.isApplicable()) {
1240 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1241 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1242 } else {
1243 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1244 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1245 }
1246 } break;
1247 case SkMask::kA8_Format: {
1248 if (fPreBlend.isApplicable()) {
1249 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1250 } else {
1251 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1252 }
1253 } break;
1254 case SkMask::kBW_Format: {
1255 const int width = glyph.fWidth;
1256 size_t dstRB = glyph.rowBytes();
1257 uint8_t* dst = (uint8_t*)glyph.fImage;
1258 for (int y = 0; y < glyph.fHeight; y++) {
1259 cgpixels_to_bits(dst, cgPixels, width);
1260 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1261 dst += dstRB;
1262 }
1263 } break;
1264 default:
1265 SkDEBUGFAIL("unexpected mask format");
1266 break;
1267 }
1268}
1269
1270/*
1271 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1272 * seems sufficient, and possibly even correct, to allow the hinted outline
1273 * to be subpixel positioned.
1274 */
1275#define kScaleForSubPixelPositionHinting (4.0f)
1276
1277void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
1278 CTFontRef font = fCTFont;
1279 SkScalar scaleX = SK_Scalar1;
1280 SkScalar scaleY = SK_Scalar1;
1281
1282 /*
1283 * For subpixel positioning, we want to return an unhinted outline, so it
1284 * can be positioned nicely at fractional offsets. However, we special-case
1285 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1286 * we want to retain hinting in the direction orthogonal to the baseline.
1287 * e.g. for horizontal baseline, we want to retain hinting in Y.
1288 * The way we remove hinting is to scale the font by some value (4) in that
1289 * direction, ask for the path, and then scale the path back down.
1290 */
1291 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1292 SkMatrix m;
1293 fRec.getSingleMatrix(&m);
1294
1295 // start out by assuming that we want no hining in X and Y
1296 scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
1297 // now see if we need to restore hinting for axis-aligned baselines
1298 switch (SkComputeAxisAlignmentForHText(m)) {
1299 case kX_SkAxisAlignment:
1300 scaleY = SK_Scalar1; // want hinting in the Y direction
1301 break;
1302 case kY_SkAxisAlignment:
1303 scaleX = SK_Scalar1; // want hinting in the X direction
1304 break;
1305 default:
1306 break;
1307 }
1308
1309 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
1310 // need to release font when we're done
1311 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
1312 }
1313
1314 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
1315 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
1316
1317 path->reset();
1318 if (cgPath != NULL) {
1319 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1320 }
1321
1322 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1323 SkMatrix m;
1324 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1325 path->transform(m);
1326 // balance the call to CTFontCreateCopyWithAttributes
1327 CFSafeRelease(font);
1328 }
1329 if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
1330 SkIPoint offset;
1331 getVerticalOffset(cgGlyph, &offset);
1332 path->offset(SkIntToScalar(offset.fX), SkIntToScalar(offset.fY));
1333 }
1334}
1335
1336void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
1337 SkPaint::FontMetrics* my) {
1338 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1339
1340 SkPaint::FontMetrics theMetrics;
1341 theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1342 theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1343 theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1344 theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1345 theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1346 theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1347 theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1348 theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
1349 theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
1350
1351 if (mx != NULL) {
1352 *mx = theMetrics;
1353 }
1354 if (my != NULL) {
1355 *my = theMetrics;
1356 }
1357}
1358
1359void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1360 SkPath* skPath = (SkPath*)info;
1361
1362 // Process the path element
1363 switch (element->type) {
1364 case kCGPathElementMoveToPoint:
1365 skPath->moveTo(element->points[0].x, -element->points[0].y);
1366 break;
1367
1368 case kCGPathElementAddLineToPoint:
1369 skPath->lineTo(element->points[0].x, -element->points[0].y);
1370 break;
1371
1372 case kCGPathElementAddQuadCurveToPoint:
1373 skPath->quadTo(element->points[0].x, -element->points[0].y,
1374 element->points[1].x, -element->points[1].y);
1375 break;
1376
1377 case kCGPathElementAddCurveToPoint:
1378 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1379 element->points[1].x, -element->points[1].y,
1380 element->points[2].x, -element->points[2].y);
1381 break;
1382
1383 case kCGPathElementCloseSubpath:
1384 skPath->close();
1385 break;
1386
1387 default:
1388 SkDEBUGFAIL("Unknown path element!");
1389 break;
1390 }
1391}
1392
1393
1394///////////////////////////////////////////////////////////////////////////////
1395
1396// Returns NULL on failure
1397// Call must still manage its ownership of provider
1398static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1399 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
1400 if (NULL == cg) {
1401 return NULL;
1402 }
1403 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
1404 return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
1405}
1406
1407SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
1408 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
1409 if (NULL == provider) {
1410 return NULL;
1411 }
1412 return create_from_dataProvider(provider);
1413}
1414
1415SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
1416 AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
1417 if (NULL == provider) {
1418 return NULL;
1419 }
1420 return create_from_dataProvider(provider);
1421}
1422
1423// Web fonts added to the the CTFont registry do not return their character set.
1424// Iterate through the font in this case. The existing caller caches the result,
1425// so the performance impact isn't too bad.
1426static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1427 SkTDArray<SkUnichar>* glyphToUnicode) {
1428 glyphToUnicode->setCount(glyphCount);
1429 SkUnichar* out = glyphToUnicode->begin();
1430 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1431 UniChar unichar = 0;
1432 while (glyphCount > 0) {
1433 CGGlyph glyph;
1434 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1435 out[glyph] = unichar;
1436 --glyphCount;
1437 }
1438 if (++unichar == 0) {
1439 break;
1440 }
1441 }
1442}
1443
1444// Construct Glyph to Unicode table.
1445// Unicode code points that require conjugate pairs in utf16 are not
1446// supported.
1447static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1448 SkTDArray<SkUnichar>* glyphToUnicode) {
1449 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1450 if (!charSet) {
1451 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1452 return;
1453 }
1454
1455 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1456 charSet));
1457 if (!bitmap) {
1458 return;
1459 }
1460 CFIndex length = CFDataGetLength(bitmap);
1461 if (!length) {
1462 return;
1463 }
1464 if (length > 8192) {
1465 // TODO: Add support for Unicode above 0xFFFF
1466 // Consider only the BMP portion of the Unicode character points.
1467 // The bitmap may contain other planes, up to plane 16.
1468 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1469 length = 8192;
1470 }
1471 const UInt8* bits = CFDataGetBytePtr(bitmap);
1472 glyphToUnicode->setCount(glyphCount);
1473 SkUnichar* out = glyphToUnicode->begin();
1474 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1475 for (int i = 0; i < length; i++) {
1476 int mask = bits[i];
1477 if (!mask) {
1478 continue;
1479 }
1480 for (int j = 0; j < 8; j++) {
1481 CGGlyph glyph;
1482 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1483 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1484 out[glyph] = unichar;
1485 }
1486 }
1487 }
1488}
1489
1490static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1491 CGSize advance;
1492 advance.width = 0;
1493 CGGlyph glyph = gId;
1494 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1495 *data = sk_float_round2int(advance.width);
1496 return true;
1497}
1498
1499// we might move this into our CGUtils...
1500static void CFStringToSkString(CFStringRef src, SkString* dst) {
1501 // Reserve enough room for the worst-case string,
1502 // plus 1 byte for the trailing null.
1503 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1504 kCFStringEncodingUTF8) + 1;
1505 dst->resize(length);
1506 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1507 // Resize to the actual UTF-8 length used, stripping the null character.
1508 dst->resize(strlen(dst->c_str()));
1509}
1510
reed@google.com2689f612013-03-20 20:01:47 +00001511SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001512 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1513 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001514 uint32_t glyphIDsCount) const {
1515
1516 CTFontRef originalCTFont = fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001517 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
1518 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
1519 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1520
1521 {
1522 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
1523 CFStringToSkString(fontName, &info->fFontName);
1524 }
1525
1526 info->fMultiMaster = false;
1527 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1528 info->fLastGlyphID = SkToU16(glyphCount - 1);
1529 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1530
1531 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1532 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1533 }
1534
1535 info->fStyle = 0;
1536
1537 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1538 // fonts always have both glyf and loca tables. At the least, this is what
1539 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1540 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001541 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001542 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1543 info->fItalicAngle = 0;
1544 info->fAscent = 0;
1545 info->fDescent = 0;
1546 info->fStemV = 0;
1547 info->fCapHeight = 0;
1548 info->fBBox = SkIRect::MakeEmpty();
1549 return info;
1550 }
1551
1552 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1553 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1554 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1555 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1556 }
1557 if (symbolicTraits & kCTFontItalicTrait) {
1558 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1559 }
1560 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001561 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1562 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1563 } else if (stylisticClass & kCTFontScriptsClass) {
1564 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1565 }
1566 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1567 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1568 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1569 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1570 CGRect bbox = CTFontGetBoundingBox(ctFont);
1571
1572 SkRect r;
1573 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1574 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1575 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1576 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1577
1578 r.roundOut(&(info->fBBox));
1579
1580 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1581 // This probably isn't very good with an italic font.
1582 int16_t min_width = SHRT_MAX;
1583 info->fStemV = 0;
1584 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1585 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1586 CGGlyph glyphs[count];
1587 CGRect boundingRects[count];
1588 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1589 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1590 glyphs, boundingRects, count);
1591 for (size_t i = 0; i < count; i++) {
1592 int16_t width = (int16_t) boundingRects[i].size.width;
1593 if (width > 0 && width < min_width) {
1594 min_width = width;
1595 info->fStemV = min_width;
1596 }
1597 }
1598 }
1599
1600 if (false) { // TODO: haven't figured out how to know if font is embeddable
1601 // (information is in the OS/2 table)
1602 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
1603 } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
1604 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1605 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1606 info->fGlyphWidths->fAdvance.append(1, &min_width);
1607 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1608 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1609 } else {
1610 info->fGlyphWidths.reset(
1611 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
1612 glyphCount,
1613 glyphIDs,
1614 glyphIDsCount,
1615 &getWidthAdvance));
1616 }
1617 }
1618 return info;
1619}
1620
1621///////////////////////////////////////////////////////////////////////////////
1622
reed@google.comcc9aad52013-03-21 19:28:10 +00001623static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1624 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001625 AutoCFRelease<CFNumberRef> fontFormatRef(
1626 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1627 if (!fontFormatRef) {
1628 return 0;
1629 }
1630
1631 SInt32 fontFormatValue;
1632 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1633 return 0;
1634 }
1635
1636 switch (fontFormatValue) {
1637 case kCTFontFormatOpenTypePostScript:
1638 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1639 case kCTFontFormatOpenTypeTrueType:
1640 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1641 case kCTFontFormatTrueType:
1642 return SkSFNTHeader::fontType_MacTrueType::TAG;
1643 case kCTFontFormatPostScript:
1644 return SkSFNTHeader::fontType_PostScript::TAG;
1645 case kCTFontFormatBitmap:
1646 return SkSFNTHeader::fontType_MacTrueType::TAG;
1647 case kCTFontFormatUnrecognized:
1648 default:
1649 //CT seems to be unreliable in being able to obtain the type,
1650 //even if all we want is the first four bytes of the font resource.
1651 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1652 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1653 }
1654}
1655
reed@google.comcc9aad52013-03-21 19:28:10 +00001656SkStream* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
1657 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001658 if (0 == fontType) {
1659 return NULL;
1660 }
1661
1662 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001663 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001664 SkTDArray<SkFontTableTag> tableTags;
1665 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001666 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001667
1668 // calc total size for font, save sizes
1669 SkTDArray<size_t> tableSizes;
1670 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1671 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001672 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001673 totalSize += (tableSize + 3) & ~3;
1674 *tableSizes.append() = tableSize;
1675 }
1676
1677 // reserve memory for stream, and zero it (tables must be zero padded)
1678 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1679 char* dataStart = (char*)stream->getMemoryBase();
1680 sk_bzero(dataStart, totalSize);
1681 char* dataPtr = dataStart;
1682
1683 // compute font header entries
1684 uint16_t entrySelector = 0;
1685 uint16_t searchRange = 1;
1686 while (searchRange < numTables >> 1) {
1687 entrySelector++;
1688 searchRange <<= 1;
1689 }
1690 searchRange <<= 4;
1691 uint16_t rangeShift = (numTables << 4) - searchRange;
1692
1693 // write font header
1694 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1695 header->fontType = fontType;
1696 header->numTables = SkEndian_SwapBE16(numTables);
1697 header->searchRange = SkEndian_SwapBE16(searchRange);
1698 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1699 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1700 dataPtr += sizeof(SkSFNTHeader);
1701
1702 // write tables
1703 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1704 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1705 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1706 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001707 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001708 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1709 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1710 tableSize));
1711 entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
1712 entry->logicalLength = SkEndian_SwapBE32(tableSize);
1713
1714 dataPtr += (tableSize + 3) & ~3;
1715 ++entry;
1716 }
1717
1718 return stream;
1719}
1720
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001721///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001722///////////////////////////////////////////////////////////////////////////////
1723
1724int SkTypeface_Mac::onGetUPEM() const {
1725 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1726 return CGFontGetUnitsPerEm(cgFont);
1727}
1728
1729// If, as is the case with web fonts, the CTFont data isn't available,
1730// the CGFont data may work. While the CGFont may always provide the
1731// right result, leave the CTFont code path to minimize disruption.
1732static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1733 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1734 kCTFontTableOptionNoOptions);
1735 if (NULL == data) {
1736 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
1737 data = CGFontCopyTableForTag(cgFont, tag);
1738 }
1739 return data;
1740}
1741
1742int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1743 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1744 kCTFontTableOptionNoOptions));
1745 if (NULL == cfArray) {
1746 return 0;
1747 }
1748 int count = CFArrayGetCount(cfArray);
1749 if (tags) {
1750 for (int i = 0; i < count; ++i) {
1751 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1752 tags[i] = static_cast<SkFontTableTag>(fontTag);
1753 }
1754 }
1755 return count;
1756}
1757
1758size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1759 size_t length, void* dstData) const {
1760 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
1761 if (NULL == srcData) {
1762 return 0;
1763 }
1764
1765 size_t srcSize = CFDataGetLength(srcData);
1766 if (offset >= srcSize) {
1767 return 0;
1768 }
1769 if (length > srcSize - offset) {
1770 length = srcSize - offset;
1771 }
1772 if (dstData) {
1773 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1774 }
1775 return length;
1776}
1777
1778SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001779 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001780}
1781
1782void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
1783 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
1784 SkScalerContext::kAutohinting_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001785
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001786 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001787
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001788 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001789
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001790 // Only two levels of hinting are supported.
1791 // kNo_Hinting means avoid CoreGraphics outline dilation.
1792 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1793 // If there is no lcd support, hinting (dilation) cannot be supported.
1794 SkPaint::Hinting hinting = rec->getHinting();
1795 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1796 hinting = SkPaint::kNo_Hinting;
1797 } else if (SkPaint::kFull_Hinting == hinting) {
1798 hinting = SkPaint::kNormal_Hinting;
1799 }
1800 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001801
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001802 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1803 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1804 // There is no current means to honor a request for unhinted lcd,
1805 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001806
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001807 // Hinting and smoothing should be orthogonal, but currently they are not.
1808 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
1809 // output is drawn from auto-dilated outlines (the amount of which is
1810 // determined by AppleFontSmoothing). Its regular anti-aliased output is
1811 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001812
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001813 // The behavior of Skia is as follows:
1814 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
1815 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
1816 // channel. This matches [LCD][yes-hint] in weight.
1817 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
1818 // Currenly side with LCD, effectively ignoring the hinting setting.
1819 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001820
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001821 if (isLCDFormat(rec->fMaskFormat)) {
1822 if (lcdSupport) {
1823 //CoreGraphics creates 555 masks for smoothed text anyway.
1824 rec->fMaskFormat = SkMask::kLCD16_Format;
1825 rec->setHinting(SkPaint::kNormal_Hinting);
1826 } else {
1827 rec->fMaskFormat = SkMask::kA8_Format;
1828 }
1829 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001830
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001831 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1832 // All other masks can use regular gamma.
1833 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
1834#ifndef SK_GAMMA_APPLY_TO_A8
1835 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00001836#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001837 } else {
1838 //CoreGraphics dialates smoothed text as needed.
1839 rec->setContrast(0);
1840 }
1841}
1842
1843// we take ownership of the ref
1844static const char* get_str(CFStringRef ref, SkString* str) {
1845 CFStringToSkString(ref, str);
1846 CFSafeRelease(ref);
1847 return str->c_str();
1848}
1849
reed@google.com5526ede2013-03-25 13:03:37 +00001850void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
1851 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001852 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001853
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001854 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
1855 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
1856 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
reed@google.com5526ede2013-03-25 13:03:37 +00001857 // TODO: need to add support for local-streams (here and openStream)
1858 *isLocalStream = false;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001859}
reed@google.com5526ede2013-03-25 13:03:37 +00001860
reed@google.com95625db2013-03-25 20:44:02 +00001861///////////////////////////////////////////////////////////////////////////////
1862///////////////////////////////////////////////////////////////////////////////
reed@google.com95625db2013-03-25 20:44:02 +00001863#if 1
reed@google.com83787c52013-03-26 17:19:15 +00001864
1865static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
1866 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
1867 if (NULL == ref.get()) {
1868 return false;
1869 }
1870 CFStringToSkString(ref, value);
1871 return true;
1872}
1873
1874static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
1875 CFNumberRef num;
1876 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
1877 && CFNumberIsFloatType(num)
1878 && CFNumberGetValue(num, kCFNumberFloatType, value);
1879}
1880
reed@google.com95625db2013-03-25 20:44:02 +00001881#include "SkFontMgr.h"
1882
reed@google.com83787c52013-03-26 17:19:15 +00001883static int unit_weight_to_fontstyle(float unit) {
1884 float value;
1885 if (unit < 0) {
1886 value = 100 + (1 + unit) * 300;
1887 } else {
1888 value = 400 + unit * 500;
1889 }
1890 return sk_float_round2int(value);
1891}
1892
1893static int unit_width_to_fontstyle(float unit) {
1894 float value;
1895 if (unit < 0) {
1896 value = 1 + (1 + unit) * 4;
1897 } else {
1898 value = 5 + unit * 4;
1899 }
1900 return sk_float_round2int(value);
1901}
1902
1903static SkFontStyle desc2fontstyle(CTFontDescriptorRef desc) {
1904 AutoCFRelease<CFDictionaryRef> dict(
1905 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc,
1906 kCTFontTraitsAttribute));
1907 if (NULL == dict.get()) {
1908 return SkFontStyle();
1909 }
1910
1911 float weight, width, slant;
1912 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
1913 weight = 0;
1914 }
1915 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
1916 width = 0;
1917 }
1918 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
1919 slant = 0;
1920 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00001921
reed@google.com83787c52013-03-26 17:19:15 +00001922 return SkFontStyle(unit_weight_to_fontstyle(weight),
1923 unit_width_to_fontstyle(width),
1924 slant ? SkFontStyle::kItalic_Slant
1925 : SkFontStyle::kUpright_Slant);
1926}
1927
reed@google.comce8b3de2013-03-26 19:30:16 +00001928static SkTypeface* createFromDesc(CFStringRef cfFamilyName,
1929 CTFontDescriptorRef desc) {
1930 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFamilyName, 1, NULL));
1931 CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc);
1932 if (NULL == ctFont) {
1933 return NULL;
1934 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00001935
reed@google.comce8b3de2013-03-26 19:30:16 +00001936 SkString str;
1937 CFStringToSkString(cfFamilyName, &str);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00001938
reed@google.comce8b3de2013-03-26 19:30:16 +00001939 bool isFixedPitch;
1940 (void)computeStyleBits(ctFont, &isFixedPitch);
1941 SkFontID fontID = CTFontRef_to_SkFontID(ctFont);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00001942
reed@google.comce8b3de2013-03-26 19:30:16 +00001943 return new SkTypeface_Mac(desc2fontstyle(desc), fontID, isFixedPitch,
1944 ctFont, str.c_str());
1945}
1946
reed@google.com83787c52013-03-26 17:19:15 +00001947class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00001948public:
reed@google.com83787c52013-03-26 17:19:15 +00001949 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
1950 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, NULL))
1951 , fFamilyName(familyName) {
1952 CFRetain(familyName);
1953 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00001954
reed@google.com83787c52013-03-26 17:19:15 +00001955 virtual ~SkFontStyleSet_Mac() {
1956 CFSafeRelease(fArray);
1957 CFRelease(fFamilyName);
1958 }
1959
1960 virtual int count() SK_OVERRIDE {
1961 return CFArrayGetCount(fArray);
1962 }
1963
1964 virtual void getStyle(int index, SkFontStyle* style,
1965 SkString* name) SK_OVERRIDE {
1966 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
1967 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
1968 if (style) {
1969 *style = desc2fontstyle(desc);
1970 }
1971 if (name) {
1972 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
1973 name->reset();
1974 }
1975 }
1976 }
1977
1978 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
1979 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
1980 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00001981
reed@google.com83787c52013-03-26 17:19:15 +00001982 return createFromDesc(fFamilyName, desc);
1983 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00001984
reed@google.com83787c52013-03-26 17:19:15 +00001985private:
1986 CFArrayRef fArray;
1987 CFStringRef fFamilyName;
1988};
1989
1990class SkFontMgr_Mac : public SkFontMgr {
1991 int fCount;
1992 CFArrayRef fNames;
1993
1994 CFStringRef stringAt(int index) const {
1995 SkASSERT((unsigned)index < (unsigned)fCount);
1996 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
1997 }
1998
1999 void lazyInit() {
2000 if (NULL == fNames) {
2001 fNames = CTFontManagerCopyAvailableFontFamilyNames();
2002 fCount = fNames ? CFArrayGetCount(fNames) : 0;
2003 }
2004 }
2005
2006public:
2007 SkFontMgr_Mac() : fCount(0), fNames(NULL) {}
2008
2009 virtual ~SkFontMgr_Mac() {
2010 CFSafeRelease(fNames);
2011 }
reed@google.com95625db2013-03-25 20:44:02 +00002012
2013protected:
2014 virtual int onCountFamilies() SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002015 this->lazyInit();
2016 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002017 }
2018
2019 virtual void onGetFamilyName(int index, SkString* familyName) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002020 this->lazyInit();
2021 if ((unsigned)index < (unsigned)fCount) {
2022 CFStringToSkString(this->stringAt(index), familyName);
2023 } else {
2024 familyName->reset();
2025 }
reed@google.com95625db2013-03-25 20:44:02 +00002026 }
2027
2028 virtual SkFontStyleSet* onCreateStyleSet(int index) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002029 this->lazyInit();
2030 if ((unsigned)index >= (unsigned)fCount) {
2031 return NULL;
2032 }
2033
2034 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2035 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2036 &kCFTypeDictionaryKeyCallBacks,
2037 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002038
reed@google.com83787c52013-03-26 17:19:15 +00002039 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute,
2040 this->stringAt(index));
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002041
reed@google.com83787c52013-03-26 17:19:15 +00002042 AutoCFRelease<CTFontDescriptorRef> desc(
2043 CTFontDescriptorCreateWithAttributes(cfAttr));
2044 return SkNEW_ARGS(SkFontStyleSet_Mac, (this->stringAt(index), desc));
reed@google.com95625db2013-03-25 20:44:02 +00002045 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002046
reed@google.com95625db2013-03-25 20:44:02 +00002047 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
2048 const SkFontStyle&) SK_OVERRIDE {
2049 return NULL;
2050 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002051
reed@google.com95625db2013-03-25 20:44:02 +00002052 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2053 const SkFontStyle&) SK_OVERRIDE {
2054 return NULL;
2055 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002056
reed@google.com95625db2013-03-25 20:44:02 +00002057 virtual SkTypeface* onCreateFromData(SkData* data,
2058 int ttcIndex) SK_OVERRIDE {
2059 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
2060 if (NULL == pr) {
2061 return NULL;
2062 }
2063 return create_from_dataProvider(pr);
2064 }
2065
2066 virtual SkTypeface* onCreateFromStream(SkStream* stream,
2067 int ttcIndex) SK_OVERRIDE {
2068 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
2069 if (NULL == pr) {
2070 return NULL;
2071 }
2072 return create_from_dataProvider(pr);
2073 }
2074
2075 virtual SkTypeface* onCreateFromFile(const char path[],
2076 int ttcIndex) SK_OVERRIDE {
2077 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2078 if (NULL == pr) {
2079 return NULL;
2080 }
2081 return create_from_dataProvider(pr);
2082 }
2083};
2084
2085SkFontMgr* SkFontMgr::Factory() {
2086 return SkNEW(SkFontMgr_Mac);
2087}
2088#endif