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