blob: 31a77fa2012106900dedee14a652e22da14d3c99 [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
reed@google.comf77b35d2013-05-02 20:39:44 +000046//#define HACK_COLORGLYPHS
bungeman@google.comcefd9812013-05-15 15:07:32 +000047//#define SK_IGNORE_MAC_TEXT_BOUNDS_FIX
reed@google.comf77b35d2013-05-02 20:39:44 +000048
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000049class SkScalerContext_Mac;
50
reed@google.com3dcbd462013-03-27 13:56:34 +000051// CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
52// provide a wrapper here that will return an empty array if need be.
53static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() {
54#ifdef SK_BUILD_FOR_IOS
55 return CFArrayCreate(NULL, NULL, 0, NULL);
56#else
57 return CTFontManagerCopyAvailableFontFamilyNames();
58#endif
59}
60
61
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000062// Being templated and taking const T* prevents calling
63// CFSafeRelease(autoCFRelease) through implicit conversion.
64template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
65 if (cfTypeRef) {
66 CFRelease(cfTypeRef);
67 }
68}
69
70// Being templated and taking const T* prevents calling
71// CFSafeRetain(autoCFRelease) through implicit conversion.
72template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
73 if (cfTypeRef) {
74 CFRetain(cfTypeRef);
75 }
76}
77
78/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
79template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
80public:
81 explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
82 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
83
84 void reset(CFRef that = NULL) {
85 CFSafeRetain(that);
86 CFSafeRelease(fCFRef);
87 fCFRef = that;
88 }
89
90 AutoCFRelease& operator =(CFRef that) {
91 reset(that);
92 return *this;
93 }
94
95 operator CFRef() const { return fCFRef; }
96 CFRef get() const { return fCFRef; }
97
98private:
99 CFRef fCFRef;
100};
101
reed@google.com964988f2013-03-29 14:57:22 +0000102static CFStringRef make_CFString(const char str[]) {
103 return CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
104}
105
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000106template<typename T> class AutoCGTable : SkNoncopyable {
107public:
108 AutoCGTable(CGFontRef font)
109 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
110 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
111 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
112 { }
113
114 const T* operator->() const { return fData; }
115
116private:
117 AutoCFRelease<CFDataRef> fCFData;
118public:
119 const T* fData;
120};
121
122// inline versions of these rect helpers
123
124static bool CGRectIsEmpty_inline(const CGRect& rect) {
125 return rect.size.width <= 0 || rect.size.height <= 0;
126}
127
bungeman@google.comcefd9812013-05-15 15:07:32 +0000128#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000129static void CGRectInset_inline(CGRect* rect, CGFloat dx, CGFloat dy) {
130 rect->origin.x += dx;
131 rect->origin.y += dy;
132 rect->size.width -= dx * 2;
133 rect->size.height -= dy * 2;
134}
bungeman@google.comcefd9812013-05-15 15:07:32 +0000135#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000136
137static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
138 return rect.origin.x;
139}
140
141static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
142 return rect.origin.x + rect.size.width;
143}
144
145static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
146 return rect.origin.y;
147}
148
149static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
150 return rect.origin.y + rect.size.height;
151}
152
153static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
154 return rect.size.width;
155}
156
157///////////////////////////////////////////////////////////////////////////////
158
159static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
160 size_t width, size_t height, size_t rowBytes) {
161 SkASSERT(width);
162 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
163
164 if (width >= 32) {
165 while (height) {
166 sk_memset32(ptr, value, width);
167 ptr = (uint32_t*)((char*)ptr + rowBytes);
168 height -= 1;
169 }
170 return;
171 }
172
173 rowBytes -= width * sizeof(uint32_t);
174
175 if (width >= 8) {
176 while (height) {
177 int w = width;
178 do {
179 *ptr++ = value; *ptr++ = value;
180 *ptr++ = value; *ptr++ = value;
181 *ptr++ = value; *ptr++ = value;
182 *ptr++ = value; *ptr++ = value;
183 w -= 8;
184 } while (w >= 8);
185 while (--w >= 0) {
186 *ptr++ = value;
187 }
188 ptr = (uint32_t*)((char*)ptr + rowBytes);
189 height -= 1;
190 }
191 } else {
192 while (height) {
193 int w = width;
194 do {
195 *ptr++ = value;
196 } while (--w > 0);
197 ptr = (uint32_t*)((char*)ptr + rowBytes);
198 height -= 1;
199 }
200 }
201}
202
203#include <sys/utsname.h>
204
205typedef uint32_t CGRGBPixel;
206
207static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
208 return pixel & 0xFF;
209}
210
211// The calls to support subpixel are present in 10.5, but are not included in
212// the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are
213// included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for
214// instance, is present in the 10.5 CoreGraphics libary, use:
215// cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/
216// cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/
217// nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts
218
219#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
220CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value);
221CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value);
222CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value);
223CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value);
224CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value);
225#endif
226
227static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
228
229// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
230static int readVersion() {
231 struct utsname info;
232 if (uname(&info) != 0) {
233 SkDebugf("uname failed\n");
234 return 0;
235 }
236 if (strcmp(info.sysname, "Darwin") != 0) {
237 SkDebugf("unexpected uname sysname %s\n", info.sysname);
238 return 0;
239 }
240 char* dot = strchr(info.release, '.');
241 if (!dot) {
242 SkDebugf("expected dot in uname release %s\n", info.release);
243 return 0;
244 }
245 int version = atoi(info.release);
246 if (version == 0) {
247 SkDebugf("could not parse uname release %s\n", info.release);
248 }
249 return version;
250}
251
252static int darwinVersion() {
253 static int darwin_version = readVersion();
254 return darwin_version;
255}
256
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000257static bool isSnowLeopard() {
258 return darwinVersion() == 10;
259}
260
261static bool isLion() {
262 return darwinVersion() == 11;
263}
264
265static bool isMountainLion() {
266 return darwinVersion() == 12;
267}
268
269static bool isLCDFormat(unsigned format) {
270 return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
271}
272
273static CGFloat ScalarToCG(SkScalar scalar) {
274 if (sizeof(CGFloat) == sizeof(float)) {
275 return SkScalarToFloat(scalar);
276 } else {
277 SkASSERT(sizeof(CGFloat) == sizeof(double));
278 return (CGFloat) SkScalarToDouble(scalar);
279 }
280}
281
282static SkScalar CGToScalar(CGFloat cgFloat) {
283 if (sizeof(CGFloat) == sizeof(float)) {
284 return SkFloatToScalar(cgFloat);
285 } else {
286 SkASSERT(sizeof(CGFloat) == sizeof(double));
287 return SkDoubleToScalar(cgFloat);
288 }
289}
290
291static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
292 SkScalar sx = SK_Scalar1,
293 SkScalar sy = SK_Scalar1) {
294 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
295 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
296 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
297 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
298 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
299 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
300}
301
bungeman@google.comcefd9812013-05-15 15:07:32 +0000302#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000303static SkScalar getFontScale(CGFontRef cgFont) {
304 int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
305 return SkScalarInvert(SkIntToScalar(unitsPerEm));
306}
bungeman@google.comcefd9812013-05-15 15:07:32 +0000307#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000308
309///////////////////////////////////////////////////////////////////////////////
310
311#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
312#define BITMAP_INFO_GRAY (kCGImageAlphaNone)
313
314/**
315 * There does not appear to be a publicly accessable API for determining if lcd
316 * font smoothing will be applied if we request it. The main issue is that if
317 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
318 */
319static bool supports_LCD() {
320 static int gSupportsLCD = -1;
321 if (gSupportsLCD >= 0) {
322 return (bool) gSupportsLCD;
323 }
324 uint32_t rgb = 0;
325 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
326 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
327 colorspace, BITMAP_INFO_RGB));
328 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
329 CGContextSetShouldSmoothFonts(cgContext, true);
330 CGContextSetShouldAntialias(cgContext, true);
331 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
332 CGContextSetGrayFillColor(cgContext, 1, 1);
333 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
334 uint32_t r = (rgb >> 16) & 0xFF;
335 uint32_t g = (rgb >> 8) & 0xFF;
336 uint32_t b = (rgb >> 0) & 0xFF;
337 gSupportsLCD = (r != g || r != b);
338 return (bool) gSupportsLCD;
339}
340
341class Offscreen {
342public:
343 Offscreen();
344
345 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
346 CGGlyph glyphID, size_t* rowBytesPtr,
347 bool generateA8FromLCD);
348
349private:
350 enum {
351 kSize = 32 * 32 * sizeof(CGRGBPixel)
352 };
353 SkAutoSMalloc<kSize> fImageStorage;
354 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
355
356 // cached state
357 AutoCFRelease<CGContextRef> fCG;
358 SkISize fSize;
359 bool fDoAA;
360 bool fDoLCD;
361
362 static int RoundSize(int dimension) {
363 return SkNextPow2(dimension);
364 }
365};
366
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +0000367Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL),
robertphillips@google.com87379e12013-03-29 12:11:10 +0000368 fDoAA(false), fDoLCD(false) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000369 fSize.set(0, 0);
370}
371
372///////////////////////////////////////////////////////////////////////////////
373
bungeman@google.comfe747652013-03-25 19:36:11 +0000374static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000375 unsigned style = SkTypeface::kNormal;
376 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
377
378 if (traits & kCTFontBoldTrait) {
379 style |= SkTypeface::kBold;
380 }
381 if (traits & kCTFontItalicTrait) {
382 style |= SkTypeface::kItalic;
383 }
bungeman@google.comfe747652013-03-25 19:36:11 +0000384 if (isFixedPitch) {
385 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000386 }
387 return (SkTypeface::Style)style;
388}
389
390static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
391 SkFontID id = 0;
392// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
393// bracket this to be Mac only.
394#ifdef SK_BUILD_FOR_MAC
395 ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
396 id = (SkFontID)ats;
397 if (id != 0) {
398 id &= 0x3FFFFFFF; // make top two bits 00
399 return id;
400 }
401#endif
402 // CTFontGetPlatformFont returns NULL if the font is local
403 // (e.g., was created by a CSS3 @font-face rule).
404 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
405 AutoCGTable<SkOTTableHead> headTable(cgFont);
406 if (headTable.fData) {
407 id = (SkFontID) headTable->checksumAdjustment;
408 id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
409 }
410 // well-formed fonts have checksums, but as a last resort, use the pointer.
411 if (id == 0) {
412 id = (SkFontID) (uintptr_t) fontRef;
413 id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
414 }
415 return id;
416}
417
reed@google.comce8b3de2013-03-26 19:30:16 +0000418static SkFontStyle stylebits2fontstyle(SkTypeface::Style styleBits) {
419 return SkFontStyle((styleBits & SkTypeface::kBold)
420 ? SkFontStyle::kBold_Weight
421 : SkFontStyle::kNormal_Weight,
422 SkFontStyle::kNormal_Width,
423 (styleBits & SkTypeface::kItalic)
424 ? SkFontStyle::kItalic_Slant
425 : SkFontStyle::kUpright_Slant);
426}
427
428#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
429
430static SkTypeface::Style fontstyle2stylebits(const SkFontStyle& fs) {
431 unsigned style = 0;
432 if (fs.width() >= WEIGHT_THRESHOLD) {
433 style |= SkTypeface::kBold;
434 }
435 if (fs.isItalic()) {
436 style |= SkTypeface::kItalic;
437 }
438 return (SkTypeface::Style)style;
439}
440
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000441class SkTypeface_Mac : public SkTypeface {
442public:
bungeman@google.comfe747652013-03-25 19:36:11 +0000443 SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isFixedPitch,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000444 CTFontRef fontRef, const char name[])
reed@google.comce8b3de2013-03-26 19:30:16 +0000445 : SkTypeface(style, fontID, isFixedPitch)
446 , fName(name)
447 , fFontRef(fontRef) // caller has already called CFRetain for us
448 , fFontStyle(stylebits2fontstyle(style))
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000449 {
450 SkASSERT(fontRef);
451 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000452
reed@google.comce8b3de2013-03-26 19:30:16 +0000453 SkTypeface_Mac(const SkFontStyle& fs, SkFontID fontID, bool isFixedPitch,
454 CTFontRef fontRef, const char name[])
455 : SkTypeface(fontstyle2stylebits(fs), fontID, isFixedPitch)
456 , fName(name)
457 , fFontRef(fontRef) // caller has already called CFRetain for us
458 , fFontStyle(fs)
459 {
460 SkASSERT(fontRef);
461 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000462
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000463 SkString fName;
464 AutoCFRelease<CTFontRef> fFontRef;
reed@google.comce8b3de2013-03-26 19:30:16 +0000465 SkFontStyle fFontStyle;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000466
467protected:
468 friend class SkFontHost; // to access our protected members for deprecated methods
469
470 virtual int onGetUPEM() const SK_OVERRIDE;
reed@google.comcc9aad52013-03-21 19:28:10 +0000471 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000472 virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
473 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
474 size_t length, void* data) const SK_OVERRIDE;
475 virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
476 virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
reed@google.com5526ede2013-03-25 13:03:37 +0000477 virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
reed@google.com2689f612013-03-20 20:01:47 +0000478 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
479 SkAdvancedTypefaceMetrics::PerGlyphInfo,
480 const uint32_t*, uint32_t) const SK_OVERRIDE;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000481
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000482private:
reed@google.comce8b3de2013-03-26 19:30:16 +0000483
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000484 typedef SkTypeface INHERITED;
485};
486
487static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
488 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000489 bool isFixedPitch;
490 SkTypeface::Style style = computeStyleBits(fontRef, &isFixedPitch);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000491 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
492
bungeman@google.comfe747652013-03-25 19:36:11 +0000493 return new SkTypeface_Mac(style, fontID, isFixedPitch, fontRef, name);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000494}
495
496static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
497 CTFontRef ctFont = NULL;
498
499 CTFontSymbolicTraits ctFontTraits = 0;
500 if (theStyle & SkTypeface::kBold) {
501 ctFontTraits |= kCTFontBoldTrait;
502 }
503 if (theStyle & SkTypeface::kItalic) {
504 ctFontTraits |= kCTFontItalicTrait;
505 }
506
507 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000508 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000509
510 AutoCFRelease<CFNumberRef> cfFontTraits(
511 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
512
513 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
514 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
515 &kCFTypeDictionaryKeyCallBacks,
516 &kCFTypeDictionaryValueCallBacks));
517
518 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
519 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
520 &kCFTypeDictionaryKeyCallBacks,
521 &kCFTypeDictionaryValueCallBacks));
522
523 // Create the font
524 if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
525 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
526
527 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
528 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
529
530 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
531 CTFontDescriptorCreateWithAttributes(cfAttributes));
532
533 if (ctFontDesc != NULL) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000534 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000535 }
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
bungeman@google.comcefd9812013-05-15 15:07:32 +0000646#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000647static void flip(SkMatrix* matrix) {
648 matrix->setSkewX(-matrix->getSkewX());
649 matrix->setSkewY(-matrix->getSkewY());
650}
bungeman@google.comcefd9812013-05-15 15:07:32 +0000651#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000652
653///////////////////////////////////////////////////////////////////////////////
654
bungeman@google.comcefd9812013-05-15 15:07:32 +0000655/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000656struct GlyphRect {
657 int16_t fMinX;
658 int16_t fMinY;
659 int16_t fMaxX;
660 int16_t fMaxY;
661};
662
663class SkScalerContext_Mac : public SkScalerContext {
664public:
reed@google.com0da48612013-03-19 16:06:52 +0000665 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000666
667protected:
668 unsigned generateGlyphCount(void) SK_OVERRIDE;
669 uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
670 void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
671 void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
672 void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
673 void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
674 void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
675
676private:
677 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000678
bungeman@google.comcefd9812013-05-15 15:07:32 +0000679#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000680 void getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000681#else
682 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
683 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
684#endif
685
686 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
687 *
688 * For use with (and must be called before) generateBBoxes.
689 */
690 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000691
bungeman@google.comcefd9812013-05-15 15:07:32 +0000692 /** Initializes fFBoundingBoxes and returns true on success.
693 *
694 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
695 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
696 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
697 * font directly.
698 *
699 * This routine initializes fFBoundingBoxes to an array of
700 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
701 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
702 *
703 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
704 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
705 *
706 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
707 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000708 bool generateBBoxes();
709
bungeman@google.comcefd9812013-05-15 15:07:32 +0000710 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
711 *
712 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
713 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
714 */
715 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000716
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000717 Offscreen fOffscreen;
718 AutoCFRelease<CTFontRef> fCTFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000719
bungeman@google.comcefd9812013-05-15 15:07:32 +0000720 /** Vertical variant of fCTFont.
721 *
722 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
723 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
724 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
725 * Use fCTVerticalFont with kCTFontVerticalOrientation to get metrics in the same space.
726 */
727 AutoCFRelease<CTFontRef> fCTVerticalFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000728
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000729 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000730 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000731 uint16_t fFBoundingBoxesGlyphOffset;
732 uint16_t fGlyphCount;
733 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000734 const bool fDoSubPosition;
735 const bool fVertical;
736
737#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
738 SkMatrix fVerticalMatrix; // unit rotated
739 SkMatrix fMatrix; // with font size
740 SkMatrix fFBoundingBoxesMatrix; // lion-specific fix
741 SkMatrix fUnitMatrix; // without font size
742#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000743
744 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000745
746 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000747};
748
reed@google.com0da48612013-03-19 16:06:52 +0000749SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
750 const SkDescriptor* desc)
751 : INHERITED(typeface, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000752 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000753 , fFBoundingBoxesGlyphOffset(0)
754 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000755 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
756 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
757
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000758{
reed@google.com2689f612013-03-20 20:01:47 +0000759 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000760 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000761 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
762 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000763
bungeman@google.comcefd9812013-05-15 15:07:32 +0000764#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000765 // Get the state we need
766 fRec.getSingleMatrix(&fMatrix);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000767 CGAffineTransform transform = MatrixToCGAffineTransform(fMatrix);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000768
769 // extract the font size out of the matrix, but leave the skewing for italic
770 SkScalar reciprocal = SkScalarInvert(fRec.fTextSize);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000771 fUnitMatrix = fMatrix;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000772 fUnitMatrix.preScale(reciprocal, reciprocal);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000773 flip(&fUnitMatrix); // flip to fix up bounds later
bungeman@google.comcefd9812013-05-15 15:07:32 +0000774#else
775 fRec.getSingleMatrix(&fFUnitMatrix);
776 CGAffineTransform transform = MatrixToCGAffineTransform(fFUnitMatrix);
777#endif
778
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000779 AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
780 if (fVertical) {
781 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
782 kCFAllocatorDefault, 0,
783 &kCFTypeDictionaryKeyCallBacks,
784 &kCFTypeDictionaryValueCallBacks));
785 if (cfAttributes) {
786 CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
787 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
788 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
789 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
790 ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
791 }
792 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000793 // Since our matrix includes everything, we pass 1 for size.
794 fCTFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, ctFontDesc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000795 fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL);
796 if (fVertical) {
797 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
798 transform = CGAffineTransformConcat(rotateLeft, transform);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000799 fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, NULL);
800#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000801 fVerticalMatrix = fUnitMatrix;
802 if (isSnowLeopard()) {
803 SkScalar scale = SkScalarMul(fRec.fTextSize, getFontScale(fCGFont));
804 fVerticalMatrix.preScale(scale, scale);
805 } else {
806 fVerticalMatrix.preRotate(SkIntToScalar(90));
807 }
808 fVerticalMatrix.postScale(SK_Scalar1, -SK_Scalar1);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000809#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000810 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000811#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
812#else
813 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
814 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
815#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000816}
817
818CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
819 CGGlyph glyphID, size_t* rowBytesPtr,
820 bool generateA8FromLCD) {
821 if (!fRGBSpace) {
822 //It doesn't appear to matter what color space is specified.
823 //Regular blends and antialiased text are always (s*a + d*(1-a))
824 //and smoothed text is always g=2.0.
825 fRGBSpace = CGColorSpaceCreateDeviceRGB();
826 }
827
828 // default to kBW_Format
829 bool doAA = false;
830 bool doLCD = false;
831
832 if (SkMask::kBW_Format != glyph.fMaskFormat) {
833 doLCD = true;
834 doAA = true;
835 }
836
837 // FIXME: lcd smoothed un-hinted rasterization unsupported.
838 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
839 doLCD = false;
840 doAA = true;
841 }
842
843 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
844 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
845 if (fSize.fWidth < glyph.fWidth) {
846 fSize.fWidth = RoundSize(glyph.fWidth);
847 }
848 if (fSize.fHeight < glyph.fHeight) {
849 fSize.fHeight = RoundSize(glyph.fHeight);
850 }
851
852 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
853 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
854 fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
855 rowBytes, fRGBSpace, BITMAP_INFO_RGB);
856
857 // skia handles quantization itself, so we disable this for cg to get
858 // full fractional data from them.
859 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
860 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
861
862 CGContextSetTextDrawingMode(fCG, kCGTextFill);
863 CGContextSetFont(fCG, context.fCGFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000864 CGContextSetFontSize(fCG, 1 /*CTFontGetSize(context.fCTFont)*/);
865 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000866
bungeman@google.comcefd9812013-05-15 15:07:32 +0000867#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000868 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition);
869 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000870#else
871 // Because CG always draws from the horizontal baseline,
872 // if there is a non-integral translation from the horizontal origin to the vertical origin,
873 // then CG cannot draw the glyph in the correct location without subpixel positioning.
874 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical);
875 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || context.fVertical);
876#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000877
878 // Draw white on black to create mask.
879 // TODO: Draw black on white and invert, CG has a special case codepath.
880 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
881
882 // force our checks below to happen
883 fDoAA = !doAA;
884 fDoLCD = !doLCD;
885 }
886
887 if (fDoAA != doAA) {
888 CGContextSetShouldAntialias(fCG, doAA);
889 fDoAA = doAA;
890 }
891 if (fDoLCD != doLCD) {
892 CGContextSetShouldSmoothFonts(fCG, doLCD);
893 fDoLCD = doLCD;
894 }
895
896 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
897 // skip rows based on the glyph's height
898 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
899
900 // erase to black
901 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
902
903 float subX = 0;
904 float subY = 0;
905 if (context.fDoSubPosition) {
906 subX = SkFixedToFloat(glyph.getSubXFixed());
907 subY = SkFixedToFloat(glyph.getSubYFixed());
908 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000909
bungeman@google.comcefd9812013-05-15 15:07:32 +0000910 // CGContextShowGlyphsAtPoint always draws using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000911 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000912#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000913 SkIPoint offset;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000914#else
915 SkPoint offset;
916#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000917 context.getVerticalOffset(glyphID, &offset);
918 subX += offset.fX;
919 subY += offset.fY;
920 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000921
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000922 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
923 glyph.fTop + glyph.fHeight - subY,
924 &glyphID, 1);
925
926 SkASSERT(rowBytesPtr);
927 *rowBytesPtr = rowBytes;
928 return image;
929}
930
bungeman@google.comcefd9812013-05-15 15:07:32 +0000931#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000932void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const {
933 CGSize vertOffset;
934 CTFontGetVerticalTranslationsForGlyphs(fCTVerticalFont, &glyphID, &vertOffset, 1);
935 const SkPoint trans = {CGToScalar(vertOffset.width),
936 CGToScalar(vertOffset.height)};
937 SkPoint floatOffset;
938 fVerticalMatrix.mapPoints(&floatOffset, &trans, 1);
939 if (!isSnowLeopard()) {
940 // SnowLeopard fails to apply the font's matrix to the vertical metrics,
941 // but Lion and Leopard do. The unit matrix describes the font's matrix at
942 // point size 1. There may be some way to avoid mapping here by setting up
943 // fVerticalMatrix differently, but this works for now.
944 fUnitMatrix.mapPoints(&floatOffset, 1);
945 }
946 offset->fX = SkScalarRound(floatOffset.fX);
947 offset->fY = SkScalarRound(floatOffset.fY);
948}
bungeman@google.comcefd9812013-05-15 15:07:32 +0000949#else
950void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
951 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
952 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
953 CGSize cgVertOffset;
954 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
955
956 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
957 if (isSnowLeopard()) {
958 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
959 fFUnitMatrix.mapPoints(&skVertOffset, 1);
960 } else {
961 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
962 skVertOffset.fY = -skVertOffset.fY;
963 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000964
bungeman@google.comcefd9812013-05-15 15:07:32 +0000965 *offset = skVertOffset;
966}
967#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000968
969uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
970 if (fFBoundingBoxesGlyphOffset) {
971 return fFBoundingBoxesGlyphOffset;
972 }
973 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
974 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
975 if (hheaTable.fData) {
976 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
977 }
978 return fFBoundingBoxesGlyphOffset;
979}
reed@android.com0680d6c2008-12-19 19:46:15 +0000980
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000981bool SkScalerContext_Mac::generateBBoxes() {
982 if (fGeneratedFBoundingBoxes) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000983 return NULL != fFBoundingBoxes.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000984 }
985 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000986
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000987 AutoCGTable<SkOTTableHead> headTable(fCGFont);
988 if (!headTable.fData) {
989 return false;
990 }
991
992 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
993 if (!locaTable.fData) {
994 return false;
995 }
996
997 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
998 if (!glyfTable.fData) {
999 return false;
1000 }
1001
1002 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +00001003 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001004
1005 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
1006 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
1007 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
1008 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
1009 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
1010 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
1011 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
1012 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
1013 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
1014 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
1015 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001016#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001017 fFBoundingBoxesMatrix = fMatrix;
1018 flip(&fFBoundingBoxesMatrix);
1019 SkScalar fontScale = getFontScale(fCGFont);
1020 fFBoundingBoxesMatrix.preScale(fontScale, fontScale);
bungeman@google.comcefd9812013-05-15 15:07:32 +00001021#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001022 return true;
1023}
1024
1025unsigned SkScalerContext_Mac::generateGlyphCount(void) {
1026 return fGlyphCount;
1027}
1028
1029uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
1030 CGGlyph cgGlyph;
1031 UniChar theChar;
1032
1033 // Validate our parameters and state
1034 SkASSERT(uni <= 0x0000FFFF);
1035 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
1036
1037 // Get the glyph
1038 theChar = (UniChar) uni;
1039
1040 if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1)) {
1041 cgGlyph = 0;
1042 }
1043
1044 return cgGlyph;
1045}
1046
1047void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
1048 this->generateMetrics(glyph);
1049}
1050
bungeman@google.comcefd9812013-05-15 15:07:32 +00001051#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001052void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
1053 CGSize advance;
1054 CGRect bounds;
1055 CGGlyph cgGlyph;
1056
1057 // Get the state we need
1058 cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
1059
1060 if (fVertical) {
1061 if (!isSnowLeopard()) {
1062 // Lion and Leopard respect the vertical font metrics.
1063 CTFontGetBoundingRectsForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
1064 &cgGlyph, &bounds, 1);
1065 } else {
1066 // Snow Leopard and earlier respect the vertical font metrics for
1067 // advances, but not bounds, so use the default box and adjust it below.
1068 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
1069 &cgGlyph, &bounds, 1);
1070 }
1071 CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
1072 &cgGlyph, &advance, 1);
1073 } else {
1074 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
1075 &cgGlyph, &bounds, 1);
1076 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontDefaultOrientation,
1077 &cgGlyph, &advance, 1);
1078 }
1079
1080 // BUG?
1081 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1082 // it should be empty. So, if we see a zero-advance, we check if it has an
1083 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1084 // is rare, so we won't incur a big performance cost for this extra check.
1085 if (0 == advance.width && 0 == advance.height) {
1086 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
1087 if (NULL == path || CGPathIsEmpty(path)) {
1088 bounds = CGRectMake(0, 0, 0, 0);
1089 }
1090 }
1091
1092 glyph->zeroMetrics();
1093 glyph->fAdvanceX = SkFloatToFixed_Check(advance.width);
1094 glyph->fAdvanceY = -SkFloatToFixed_Check(advance.height);
1095
1096 if (CGRectIsEmpty_inline(bounds)) {
1097 return;
1098 }
1099
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001100 // Adjust the bounds
1101 //
1102 // CTFontGetBoundingRectsForGlyphs ignores the font transform, so we need
1103 // to transform the bounding box ourselves.
1104 //
1105 // The bounds are also expanded by 1 pixel, to give CG room for anti-aliasing.
1106 CGRectInset_inline(&bounds, -1, -1);
1107
1108 // Get the metrics
1109 bool lionAdjustedMetrics = false;
1110 if (isLion() || isMountainLion()) {
1111 if (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()){
1112 lionAdjustedMetrics = true;
1113 SkRect adjust;
1114 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1115 adjust.set(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1116 fFBoundingBoxesMatrix.mapRect(&adjust);
1117 bounds.origin.x = SkScalarToFloat(adjust.fLeft) - 1;
1118 bounds.origin.y = SkScalarToFloat(adjust.fTop) - 1;
1119 }
1120 // Lion returns fractions in the bounds
1121 glyph->fWidth = SkToU16(sk_float_ceil2int(bounds.size.width));
1122 glyph->fHeight = SkToU16(sk_float_ceil2int(bounds.size.height));
1123 } else {
1124 glyph->fWidth = SkToU16(sk_float_round2int(bounds.size.width));
1125 glyph->fHeight = SkToU16(sk_float_round2int(bounds.size.height));
1126 }
1127 glyph->fTop = SkToS16(-sk_float_round2int(CGRectGetMaxY_inline(bounds)));
1128 glyph->fLeft = SkToS16(sk_float_round2int(CGRectGetMinX_inline(bounds)));
1129 SkIPoint offset;
1130 if (fVertical && (isSnowLeopard() || lionAdjustedMetrics)) {
1131 // SnowLeopard doesn't respect vertical metrics, so compute them manually.
1132 // Also compute them for Lion when the metrics were computed by hand.
1133 getVerticalOffset(cgGlyph, &offset);
1134 glyph->fLeft += offset.fX;
1135 glyph->fTop += offset.fY;
1136 }
reed@google.comf77b35d2013-05-02 20:39:44 +00001137#ifdef HACK_COLORGLYPHS
1138 glyph->fMaskFormat = SkMask::kARGB32_Format;
1139#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001140}
bungeman@google.comcefd9812013-05-15 15:07:32 +00001141#else
1142void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
1143 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
1144 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001145
bungeman@google.comcefd9812013-05-15 15:07:32 +00001146 // The following block produces cgAdvance in CG units (pixels, y up).
1147 CGSize cgAdvance;
1148 if (fVertical) {
1149 CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
1150 &cgGlyph, &cgAdvance, 1);
1151 } else {
1152 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1153 &cgGlyph, &cgAdvance, 1);
1154 }
1155 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
1156 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001157
bungeman@google.comcefd9812013-05-15 15:07:32 +00001158 // The following produces skBounds in SkGlyph units (pixels, y down),
1159 // or returns early if skBounds would be empty.
1160 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001161
bungeman@google.comcefd9812013-05-15 15:07:32 +00001162 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
1163 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
1164 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
1165 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
1166 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
1167 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
1168 // to center the glyph along the vertical baseline and also perform some mysterious shift
1169 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
1170 // these steps.
1171 //
1172 // It is not known which is correct (or if either is correct). However, we must always draw
1173 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
1174 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001175
bungeman@google.comcefd9812013-05-15 15:07:32 +00001176 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
1177 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001178
bungeman@google.comcefd9812013-05-15 15:07:32 +00001179 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
1180 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
1181 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
1182 // font directly.
1183 if ((isLion() || isMountainLion()) &&
1184 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1185 {
1186 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1187 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1188 return;
1189 }
1190 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1191 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1192 fFUnitMatrix.mapRect(&skBounds);
1193
1194 } else {
1195 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1196 CGRect cgBounds;
1197 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1198 &cgGlyph, &cgBounds, 1);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001199
bungeman@google.comcefd9812013-05-15 15:07:32 +00001200 // BUG?
1201 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1202 // it should be empty. So, if we see a zero-advance, we check if it has an
1203 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1204 // is rare, so we won't incur a big performance cost for this extra check.
1205 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
1206 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
1207 if (NULL == path || CGPathIsEmpty(path)) {
1208 return;
1209 }
1210 }
1211
1212 if (CGRectIsEmpty_inline(cgBounds)) {
1213 return;
1214 }
1215
1216 // Convert cgBounds to SkGlyph units (pixels, y down).
1217 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1218 cgBounds.size.width, cgBounds.size.height);
1219 }
1220
1221 if (fVertical) {
1222 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1223 // Convert these horizontal bounds into vertical bounds.
1224 SkPoint offset;
1225 getVerticalOffset(cgGlyph, &offset);
1226 skBounds.offset(offset);
1227 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001228
bungeman@google.comcefd9812013-05-15 15:07:32 +00001229 // Currently the bounds are based on being rendered at (0,0).
1230 // The top left must not move, since that is the base from which subpixel positioning is offset.
1231 if (fDoSubPosition) {
1232 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1233 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1234 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001235
bungeman@google.comcefd9812013-05-15 15:07:32 +00001236 SkIRect skIBounds;
1237 skBounds.roundOut(&skIBounds);
1238 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1239 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1240 // is not currently known, as CG dilates the outlines by some percentage.
1241 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1242 skIBounds.outset(1, 1);
1243 glyph->fLeft = SkToS16(skIBounds.fLeft);
1244 glyph->fTop = SkToS16(skIBounds.fTop);
1245 glyph->fWidth = SkToU16(skIBounds.width());
1246 glyph->fHeight = SkToU16(skIBounds.height());
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001247
bungeman@google.comcefd9812013-05-15 15:07:32 +00001248#ifdef HACK_COLORGLYPHS
1249 glyph->fMaskFormat = SkMask::kARGB32_Format;
1250#endif
1251}
1252
1253#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001254#include "SkColorPriv.h"
1255
1256static void build_power_table(uint8_t table[], float ee) {
1257 for (int i = 0; i < 256; i++) {
1258 float x = i / 255.f;
1259 x = sk_float_pow(x, ee);
1260 int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
1261 table[i] = SkToU8(xx);
1262 }
1263}
1264
1265/**
1266 * This will invert the gamma applied by CoreGraphics, so we can get linear
1267 * values.
1268 *
1269 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1270 * The color space used does not appear to affect this choice.
1271 */
1272static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1273 static bool gInited;
1274 static uint8_t gTableCoreGraphicsSmoothing[256];
1275 if (!gInited) {
1276 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1277 gInited = true;
1278 }
1279 return gTableCoreGraphicsSmoothing;
1280}
1281
1282static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1283 while (count > 0) {
1284 uint8_t mask = 0;
1285 for (int i = 7; i >= 0; --i) {
1286 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1287 if (0 == --count) {
1288 break;
1289 }
1290 }
1291 *dst++ = mask;
1292 }
1293}
1294
1295template<bool APPLY_PREBLEND>
1296static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1297 U8CPU r = (rgb >> 16) & 0xFF;
1298 U8CPU g = (rgb >> 8) & 0xFF;
1299 U8CPU b = (rgb >> 0) & 0xFF;
1300 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1301}
1302template<bool APPLY_PREBLEND>
1303static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1304 const SkGlyph& glyph, const uint8_t* table8) {
1305 const int width = glyph.fWidth;
1306 size_t dstRB = glyph.rowBytes();
1307 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1308
1309 for (int y = 0; y < glyph.fHeight; y++) {
1310 for (int i = 0; i < width; ++i) {
1311 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1312 }
1313 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1314 dst += dstRB;
1315 }
1316}
1317
1318template<bool APPLY_PREBLEND>
1319static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1320 const uint8_t* tableG,
1321 const uint8_t* tableB) {
1322 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1323 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1324 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1325 return SkPack888ToRGB16(r, g, b);
1326}
1327template<bool APPLY_PREBLEND>
1328static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1329 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1330 const int width = glyph.fWidth;
1331 size_t dstRB = glyph.rowBytes();
1332 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1333
1334 for (int y = 0; y < glyph.fHeight; y++) {
1335 for (int i = 0; i < width; i++) {
1336 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1337 }
1338 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1339 dst = (uint16_t*)((char*)dst + dstRB);
1340 }
1341}
1342
1343template<bool APPLY_PREBLEND>
1344static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
1345 const uint8_t* tableG,
1346 const uint8_t* tableB) {
1347 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1348 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1349 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1350 return SkPackARGB32(0xFF, r, g, b);
1351}
1352template<bool APPLY_PREBLEND>
1353static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1354 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1355 const int width = glyph.fWidth;
1356 size_t dstRB = glyph.rowBytes();
1357 uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
1358 for (int y = 0; y < glyph.fHeight; y++) {
1359 for (int i = 0; i < width; i++) {
1360 dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1361 }
1362 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1363 dst = (uint32_t*)((char*)dst + dstRB);
1364 }
1365}
1366
reed@google.comf77b35d2013-05-02 20:39:44 +00001367#ifdef HACK_COLORGLYPHS
1368// hack to colorize the output for testing kARGB32_Format
1369static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb, const SkGlyph& glyph,
1370 int x, int y) {
1371 U8CPU r = (rgb >> 16) & 0xFF;
1372 U8CPU g = (rgb >> 8) & 0xFF;
1373 U8CPU b = (rgb >> 0) & 0xFF;
1374 unsigned a = SkComputeLuminance(r, g, b);
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +00001375
reed@google.comf77b35d2013-05-02 20:39:44 +00001376 // compute gradient from x,y
1377 r = x * 255 / glyph.fWidth;
1378 g = 0;
1379 b = (glyph.fHeight - y) * 255 / glyph.fHeight;
1380 return SkPreMultiplyARGB(a, r, g, b); // red
1381}
1382#endif
1383
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001384template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1385 return (T*)((char*)ptr + byteOffset);
1386}
1387
1388void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
1389 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
1390
1391 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1392 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1393
1394 // Draw the glyph
1395 size_t cgRowBytes;
1396 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
1397 if (cgPixels == NULL) {
1398 return;
1399 }
1400
1401 //TODO: see if drawing black on white and inverting is faster (at least in
1402 //lcd case) as core graphics appears to have special case code for drawing
1403 //black text.
1404
1405 // Fix the glyph
1406 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1407 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1408 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1409
1410 //Note that the following cannot really be integrated into the
1411 //pre-blend, since we may not be applying the pre-blend; when we aren't
1412 //applying the pre-blend it means that a filter wants linear anyway.
1413 //Other code may also be applying the pre-blend, so we'd need another
1414 //one with this and one without.
1415 CGRGBPixel* addr = cgPixels;
1416 for (int y = 0; y < glyph.fHeight; ++y) {
1417 for (int x = 0; x < glyph.fWidth; ++x) {
1418 int r = (addr[x] >> 16) & 0xFF;
1419 int g = (addr[x] >> 8) & 0xFF;
1420 int b = (addr[x] >> 0) & 0xFF;
1421 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1422 }
1423 addr = SkTAddByteOffset(addr, cgRowBytes);
1424 }
1425 }
1426
1427 // Convert glyph to mask
1428 switch (glyph.fMaskFormat) {
1429 case SkMask::kLCD32_Format: {
1430 if (fPreBlend.isApplicable()) {
1431 rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
1432 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1433 } else {
1434 rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
1435 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1436 }
1437 } break;
1438 case SkMask::kLCD16_Format: {
1439 if (fPreBlend.isApplicable()) {
1440 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1441 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1442 } else {
1443 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1444 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1445 }
1446 } break;
1447 case SkMask::kA8_Format: {
1448 if (fPreBlend.isApplicable()) {
1449 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1450 } else {
1451 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1452 }
1453 } break;
1454 case SkMask::kBW_Format: {
1455 const int width = glyph.fWidth;
1456 size_t dstRB = glyph.rowBytes();
1457 uint8_t* dst = (uint8_t*)glyph.fImage;
1458 for (int y = 0; y < glyph.fHeight; y++) {
1459 cgpixels_to_bits(dst, cgPixels, width);
1460 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1461 dst += dstRB;
1462 }
1463 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001464#ifdef HACK_COLORGLYPHS
1465 case SkMask::kARGB32_Format: {
1466 const int width = glyph.fWidth;
1467 size_t dstRB = glyph.rowBytes();
1468 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1469 for (int y = 0; y < glyph.fHeight; y++) {
1470 for (int x = 0; x < width; ++x) {
1471 dst[x] = cgpixels_to_pmcolor(cgPixels[x], glyph, x, y);
1472 }
1473 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1474 dst = (SkPMColor*)((char*)dst + dstRB);
1475 }
1476 } break;
1477#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001478 default:
1479 SkDEBUGFAIL("unexpected mask format");
1480 break;
1481 }
1482}
1483
1484/*
1485 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1486 * seems sufficient, and possibly even correct, to allow the hinted outline
1487 * to be subpixel positioned.
1488 */
1489#define kScaleForSubPixelPositionHinting (4.0f)
1490
1491void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
1492 CTFontRef font = fCTFont;
1493 SkScalar scaleX = SK_Scalar1;
1494 SkScalar scaleY = SK_Scalar1;
1495
1496 /*
1497 * For subpixel positioning, we want to return an unhinted outline, so it
1498 * can be positioned nicely at fractional offsets. However, we special-case
1499 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1500 * we want to retain hinting in the direction orthogonal to the baseline.
1501 * e.g. for horizontal baseline, we want to retain hinting in Y.
1502 * The way we remove hinting is to scale the font by some value (4) in that
1503 * direction, ask for the path, and then scale the path back down.
1504 */
1505 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1506 SkMatrix m;
1507 fRec.getSingleMatrix(&m);
1508
1509 // start out by assuming that we want no hining in X and Y
1510 scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
1511 // now see if we need to restore hinting for axis-aligned baselines
1512 switch (SkComputeAxisAlignmentForHText(m)) {
1513 case kX_SkAxisAlignment:
1514 scaleY = SK_Scalar1; // want hinting in the Y direction
1515 break;
1516 case kY_SkAxisAlignment:
1517 scaleX = SK_Scalar1; // want hinting in the X direction
1518 break;
1519 default:
1520 break;
1521 }
1522
1523 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
1524 // need to release font when we're done
1525 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
1526 }
1527
1528 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
1529 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
1530
1531 path->reset();
1532 if (cgPath != NULL) {
1533 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1534 }
1535
bungeman@google.comcefd9812013-05-15 15:07:32 +00001536 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001537 SkMatrix m;
1538 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1539 path->transform(m);
1540 // balance the call to CTFontCreateCopyWithAttributes
1541 CFSafeRelease(font);
1542 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001543 if (fVertical) {
1544#if defined(SK_IGNORE_MAC_TEXT_BOUNDS_FIX)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001545 SkIPoint offset;
1546 getVerticalOffset(cgGlyph, &offset);
1547 path->offset(SkIntToScalar(offset.fX), SkIntToScalar(offset.fY));
bungeman@google.comcefd9812013-05-15 15:07:32 +00001548#else
1549 SkPoint offset;
1550 getVerticalOffset(cgGlyph, &offset);
1551 path->offset(offset.fX, offset.fY);
1552#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001553 }
1554}
1555
1556void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
1557 SkPaint::FontMetrics* my) {
1558 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1559
1560 SkPaint::FontMetrics theMetrics;
1561 theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1562 theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1563 theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1564 theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1565 theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1566 theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1567 theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1568 theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
1569 theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
1570
1571 if (mx != NULL) {
1572 *mx = theMetrics;
1573 }
1574 if (my != NULL) {
1575 *my = theMetrics;
1576 }
1577}
1578
1579void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1580 SkPath* skPath = (SkPath*)info;
1581
1582 // Process the path element
1583 switch (element->type) {
1584 case kCGPathElementMoveToPoint:
1585 skPath->moveTo(element->points[0].x, -element->points[0].y);
1586 break;
1587
1588 case kCGPathElementAddLineToPoint:
1589 skPath->lineTo(element->points[0].x, -element->points[0].y);
1590 break;
1591
1592 case kCGPathElementAddQuadCurveToPoint:
1593 skPath->quadTo(element->points[0].x, -element->points[0].y,
1594 element->points[1].x, -element->points[1].y);
1595 break;
1596
1597 case kCGPathElementAddCurveToPoint:
1598 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1599 element->points[1].x, -element->points[1].y,
1600 element->points[2].x, -element->points[2].y);
1601 break;
1602
1603 case kCGPathElementCloseSubpath:
1604 skPath->close();
1605 break;
1606
1607 default:
1608 SkDEBUGFAIL("Unknown path element!");
1609 break;
1610 }
1611}
1612
1613
1614///////////////////////////////////////////////////////////////////////////////
1615
1616// Returns NULL on failure
1617// Call must still manage its ownership of provider
1618static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1619 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
1620 if (NULL == cg) {
1621 return NULL;
1622 }
1623 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
1624 return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
1625}
1626
1627SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
1628 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
1629 if (NULL == provider) {
1630 return NULL;
1631 }
1632 return create_from_dataProvider(provider);
1633}
1634
1635SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
1636 AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
1637 if (NULL == provider) {
1638 return NULL;
1639 }
1640 return create_from_dataProvider(provider);
1641}
1642
1643// Web fonts added to the the CTFont registry do not return their character set.
1644// Iterate through the font in this case. The existing caller caches the result,
1645// so the performance impact isn't too bad.
1646static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1647 SkTDArray<SkUnichar>* glyphToUnicode) {
1648 glyphToUnicode->setCount(glyphCount);
1649 SkUnichar* out = glyphToUnicode->begin();
1650 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1651 UniChar unichar = 0;
1652 while (glyphCount > 0) {
1653 CGGlyph glyph;
1654 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1655 out[glyph] = unichar;
1656 --glyphCount;
1657 }
1658 if (++unichar == 0) {
1659 break;
1660 }
1661 }
1662}
1663
1664// Construct Glyph to Unicode table.
1665// Unicode code points that require conjugate pairs in utf16 are not
1666// supported.
1667static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1668 SkTDArray<SkUnichar>* glyphToUnicode) {
1669 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1670 if (!charSet) {
1671 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1672 return;
1673 }
1674
1675 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1676 charSet));
1677 if (!bitmap) {
1678 return;
1679 }
1680 CFIndex length = CFDataGetLength(bitmap);
1681 if (!length) {
1682 return;
1683 }
1684 if (length > 8192) {
1685 // TODO: Add support for Unicode above 0xFFFF
1686 // Consider only the BMP portion of the Unicode character points.
1687 // The bitmap may contain other planes, up to plane 16.
1688 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1689 length = 8192;
1690 }
1691 const UInt8* bits = CFDataGetBytePtr(bitmap);
1692 glyphToUnicode->setCount(glyphCount);
1693 SkUnichar* out = glyphToUnicode->begin();
1694 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1695 for (int i = 0; i < length; i++) {
1696 int mask = bits[i];
1697 if (!mask) {
1698 continue;
1699 }
1700 for (int j = 0; j < 8; j++) {
1701 CGGlyph glyph;
1702 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1703 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1704 out[glyph] = unichar;
1705 }
1706 }
1707 }
1708}
1709
1710static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1711 CGSize advance;
1712 advance.width = 0;
1713 CGGlyph glyph = gId;
1714 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1715 *data = sk_float_round2int(advance.width);
1716 return true;
1717}
1718
1719// we might move this into our CGUtils...
1720static void CFStringToSkString(CFStringRef src, SkString* dst) {
1721 // Reserve enough room for the worst-case string,
1722 // plus 1 byte for the trailing null.
1723 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1724 kCFStringEncodingUTF8) + 1;
1725 dst->resize(length);
1726 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1727 // Resize to the actual UTF-8 length used, stripping the null character.
1728 dst->resize(strlen(dst->c_str()));
1729}
1730
reed@google.com2689f612013-03-20 20:01:47 +00001731SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001732 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1733 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001734 uint32_t glyphIDsCount) const {
1735
1736 CTFontRef originalCTFont = fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001737 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
1738 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
1739 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1740
1741 {
1742 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
1743 CFStringToSkString(fontName, &info->fFontName);
1744 }
1745
1746 info->fMultiMaster = false;
1747 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1748 info->fLastGlyphID = SkToU16(glyphCount - 1);
1749 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1750
1751 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1752 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1753 }
1754
1755 info->fStyle = 0;
1756
1757 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1758 // fonts always have both glyf and loca tables. At the least, this is what
1759 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1760 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001761 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001762 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1763 info->fItalicAngle = 0;
1764 info->fAscent = 0;
1765 info->fDescent = 0;
1766 info->fStemV = 0;
1767 info->fCapHeight = 0;
1768 info->fBBox = SkIRect::MakeEmpty();
1769 return info;
1770 }
1771
1772 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1773 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1774 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1775 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1776 }
1777 if (symbolicTraits & kCTFontItalicTrait) {
1778 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1779 }
1780 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001781 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1782 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1783 } else if (stylisticClass & kCTFontScriptsClass) {
1784 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1785 }
1786 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1787 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1788 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1789 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1790 CGRect bbox = CTFontGetBoundingBox(ctFont);
1791
1792 SkRect r;
1793 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1794 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1795 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1796 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1797
1798 r.roundOut(&(info->fBBox));
1799
1800 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1801 // This probably isn't very good with an italic font.
1802 int16_t min_width = SHRT_MAX;
1803 info->fStemV = 0;
1804 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1805 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1806 CGGlyph glyphs[count];
1807 CGRect boundingRects[count];
1808 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1809 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1810 glyphs, boundingRects, count);
1811 for (size_t i = 0; i < count; i++) {
1812 int16_t width = (int16_t) boundingRects[i].size.width;
1813 if (width > 0 && width < min_width) {
1814 min_width = width;
1815 info->fStemV = min_width;
1816 }
1817 }
1818 }
1819
1820 if (false) { // TODO: haven't figured out how to know if font is embeddable
1821 // (information is in the OS/2 table)
1822 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
1823 } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
1824 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1825 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1826 info->fGlyphWidths->fAdvance.append(1, &min_width);
1827 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1828 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1829 } else {
1830 info->fGlyphWidths.reset(
1831 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
1832 glyphCount,
1833 glyphIDs,
1834 glyphIDsCount,
1835 &getWidthAdvance));
1836 }
1837 }
1838 return info;
1839}
1840
1841///////////////////////////////////////////////////////////////////////////////
1842
reed@google.comcc9aad52013-03-21 19:28:10 +00001843static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1844 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001845 AutoCFRelease<CFNumberRef> fontFormatRef(
1846 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1847 if (!fontFormatRef) {
1848 return 0;
1849 }
1850
1851 SInt32 fontFormatValue;
1852 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1853 return 0;
1854 }
1855
1856 switch (fontFormatValue) {
1857 case kCTFontFormatOpenTypePostScript:
1858 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1859 case kCTFontFormatOpenTypeTrueType:
1860 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1861 case kCTFontFormatTrueType:
1862 return SkSFNTHeader::fontType_MacTrueType::TAG;
1863 case kCTFontFormatPostScript:
1864 return SkSFNTHeader::fontType_PostScript::TAG;
1865 case kCTFontFormatBitmap:
1866 return SkSFNTHeader::fontType_MacTrueType::TAG;
1867 case kCTFontFormatUnrecognized:
1868 default:
1869 //CT seems to be unreliable in being able to obtain the type,
1870 //even if all we want is the first four bytes of the font resource.
1871 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1872 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1873 }
1874}
1875
reed@google.comcc9aad52013-03-21 19:28:10 +00001876SkStream* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
1877 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001878 if (0 == fontType) {
1879 return NULL;
1880 }
1881
1882 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001883 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001884 SkTDArray<SkFontTableTag> tableTags;
1885 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001886 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001887
1888 // calc total size for font, save sizes
1889 SkTDArray<size_t> tableSizes;
1890 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1891 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001892 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001893 totalSize += (tableSize + 3) & ~3;
1894 *tableSizes.append() = tableSize;
1895 }
1896
1897 // reserve memory for stream, and zero it (tables must be zero padded)
1898 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1899 char* dataStart = (char*)stream->getMemoryBase();
1900 sk_bzero(dataStart, totalSize);
1901 char* dataPtr = dataStart;
1902
1903 // compute font header entries
1904 uint16_t entrySelector = 0;
1905 uint16_t searchRange = 1;
1906 while (searchRange < numTables >> 1) {
1907 entrySelector++;
1908 searchRange <<= 1;
1909 }
1910 searchRange <<= 4;
1911 uint16_t rangeShift = (numTables << 4) - searchRange;
1912
1913 // write font header
1914 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1915 header->fontType = fontType;
1916 header->numTables = SkEndian_SwapBE16(numTables);
1917 header->searchRange = SkEndian_SwapBE16(searchRange);
1918 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1919 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1920 dataPtr += sizeof(SkSFNTHeader);
1921
1922 // write tables
1923 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1924 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1925 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1926 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001927 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001928 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1929 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1930 tableSize));
1931 entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
1932 entry->logicalLength = SkEndian_SwapBE32(tableSize);
1933
1934 dataPtr += (tableSize + 3) & ~3;
1935 ++entry;
1936 }
1937
1938 return stream;
1939}
1940
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001941///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001942///////////////////////////////////////////////////////////////////////////////
1943
1944int SkTypeface_Mac::onGetUPEM() const {
1945 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1946 return CGFontGetUnitsPerEm(cgFont);
1947}
1948
1949// If, as is the case with web fonts, the CTFont data isn't available,
1950// the CGFont data may work. While the CGFont may always provide the
1951// right result, leave the CTFont code path to minimize disruption.
1952static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1953 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1954 kCTFontTableOptionNoOptions);
1955 if (NULL == data) {
1956 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
1957 data = CGFontCopyTableForTag(cgFont, tag);
1958 }
1959 return data;
1960}
1961
1962int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1963 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1964 kCTFontTableOptionNoOptions));
1965 if (NULL == cfArray) {
1966 return 0;
1967 }
1968 int count = CFArrayGetCount(cfArray);
1969 if (tags) {
1970 for (int i = 0; i < count; ++i) {
1971 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1972 tags[i] = static_cast<SkFontTableTag>(fontTag);
1973 }
1974 }
1975 return count;
1976}
1977
1978size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1979 size_t length, void* dstData) const {
1980 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
1981 if (NULL == srcData) {
1982 return 0;
1983 }
1984
1985 size_t srcSize = CFDataGetLength(srcData);
1986 if (offset >= srcSize) {
1987 return 0;
1988 }
1989 if (length > srcSize - offset) {
1990 length = srcSize - offset;
1991 }
1992 if (dstData) {
1993 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1994 }
1995 return length;
1996}
1997
1998SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001999 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002000}
2001
2002void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002003 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2004 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2005 {
2006 rec->fMaskFormat = SkMask::kA8_Format;
2007 // Render the glyphs as close as possible to what was requested.
2008 // The above turns off subpixel rendering, but the user requested it.
2009 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
2010 // See comments below for more details.
2011 rec->setHinting(SkPaint::kNormal_Hinting);
2012 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00002013
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002014 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
2015 SkScalerContext::kAutohinting_Flag |
2016 SkScalerContext::kLCD_BGROrder_Flag |
2017 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002018
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002019 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002020
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002021 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002022
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002023 // Only two levels of hinting are supported.
2024 // kNo_Hinting means avoid CoreGraphics outline dilation.
2025 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
2026 // If there is no lcd support, hinting (dilation) cannot be supported.
2027 SkPaint::Hinting hinting = rec->getHinting();
2028 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
2029 hinting = SkPaint::kNo_Hinting;
2030 } else if (SkPaint::kFull_Hinting == hinting) {
2031 hinting = SkPaint::kNormal_Hinting;
2032 }
2033 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002034
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002035 // FIXME: lcd smoothed un-hinted rasterization unsupported.
2036 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
2037 // There is no current means to honor a request for unhinted lcd,
2038 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002039
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002040 // Hinting and smoothing should be orthogonal, but currently they are not.
2041 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
2042 // output is drawn from auto-dilated outlines (the amount of which is
2043 // determined by AppleFontSmoothing). Its regular anti-aliased output is
2044 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002045
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002046 // The behavior of Skia is as follows:
2047 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
2048 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
2049 // channel. This matches [LCD][yes-hint] in weight.
2050 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
2051 // Currenly side with LCD, effectively ignoring the hinting setting.
2052 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002053
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002054 if (isLCDFormat(rec->fMaskFormat)) {
2055 if (lcdSupport) {
2056 //CoreGraphics creates 555 masks for smoothed text anyway.
2057 rec->fMaskFormat = SkMask::kLCD16_Format;
2058 rec->setHinting(SkPaint::kNormal_Hinting);
2059 } else {
2060 rec->fMaskFormat = SkMask::kA8_Format;
2061 }
2062 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002063
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002064 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2065 // All other masks can use regular gamma.
2066 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
2067#ifndef SK_GAMMA_APPLY_TO_A8
2068 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002069#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002070 } else {
2071 //CoreGraphics dialates smoothed text as needed.
2072 rec->setContrast(0);
2073 }
2074}
2075
2076// we take ownership of the ref
2077static const char* get_str(CFStringRef ref, SkString* str) {
2078 CFStringToSkString(ref, str);
2079 CFSafeRelease(ref);
2080 return str->c_str();
2081}
2082
reed@google.com5526ede2013-03-25 13:03:37 +00002083void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2084 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002085 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002086
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002087 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
2088 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
2089 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
reed@google.com5526ede2013-03-25 13:03:37 +00002090 // TODO: need to add support for local-streams (here and openStream)
2091 *isLocalStream = false;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002092}
reed@google.com5526ede2013-03-25 13:03:37 +00002093
reed@google.com95625db2013-03-25 20:44:02 +00002094///////////////////////////////////////////////////////////////////////////////
2095///////////////////////////////////////////////////////////////////////////////
reed@google.com95625db2013-03-25 20:44:02 +00002096#if 1
reed@google.com83787c52013-03-26 17:19:15 +00002097
2098static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
2099 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
2100 if (NULL == ref.get()) {
2101 return false;
2102 }
2103 CFStringToSkString(ref, value);
2104 return true;
2105}
2106
2107static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
2108 CFNumberRef num;
2109 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
2110 && CFNumberIsFloatType(num)
2111 && CFNumberGetValue(num, kCFNumberFloatType, value);
2112}
2113
reed@google.com95625db2013-03-25 20:44:02 +00002114#include "SkFontMgr.h"
2115
reed@google.com83787c52013-03-26 17:19:15 +00002116static int unit_weight_to_fontstyle(float unit) {
2117 float value;
2118 if (unit < 0) {
2119 value = 100 + (1 + unit) * 300;
2120 } else {
2121 value = 400 + unit * 500;
2122 }
2123 return sk_float_round2int(value);
2124}
2125
2126static int unit_width_to_fontstyle(float unit) {
2127 float value;
2128 if (unit < 0) {
2129 value = 1 + (1 + unit) * 4;
2130 } else {
2131 value = 5 + unit * 4;
2132 }
2133 return sk_float_round2int(value);
2134}
2135
reed@google.com964988f2013-03-29 14:57:22 +00002136static inline int sqr(int value) {
2137 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2138 return value * value;
2139}
2140
2141// We normalize each axis (weight, width, italic) to be base-900
2142static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2143 return sqr(a.weight() - b.weight()) +
2144 sqr((a.width() - b.width()) * 100) +
2145 sqr((a.isItalic() != b.isItalic()) * 900);
2146}
2147
reed@google.com83787c52013-03-26 17:19:15 +00002148static SkFontStyle desc2fontstyle(CTFontDescriptorRef desc) {
2149 AutoCFRelease<CFDictionaryRef> dict(
2150 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc,
2151 kCTFontTraitsAttribute));
2152 if (NULL == dict.get()) {
2153 return SkFontStyle();
2154 }
2155
2156 float weight, width, slant;
2157 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
2158 weight = 0;
2159 }
2160 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
2161 width = 0;
2162 }
2163 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
2164 slant = 0;
2165 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002166
reed@google.com83787c52013-03-26 17:19:15 +00002167 return SkFontStyle(unit_weight_to_fontstyle(weight),
2168 unit_width_to_fontstyle(width),
2169 slant ? SkFontStyle::kItalic_Slant
2170 : SkFontStyle::kUpright_Slant);
2171}
2172
reed@google.comdea7ee02013-03-28 14:12:10 +00002173struct NameFontStyleRec {
2174 SkString fFamilyName;
2175 SkFontStyle fFontStyle;
2176};
2177
2178static bool nameFontStyleProc(SkTypeface* face, SkTypeface::Style,
2179 void* ctx) {
2180 SkTypeface_Mac* macFace = (SkTypeface_Mac*)face;
2181 const NameFontStyleRec* rec = (const NameFontStyleRec*)ctx;
2182
2183 return macFace->fFontStyle == rec->fFontStyle &&
2184 macFace->fName == rec->fFamilyName;
2185}
2186
reed@google.comce8b3de2013-03-26 19:30:16 +00002187static SkTypeface* createFromDesc(CFStringRef cfFamilyName,
2188 CTFontDescriptorRef desc) {
reed@google.comdea7ee02013-03-28 14:12:10 +00002189 NameFontStyleRec rec;
2190 CFStringToSkString(cfFamilyName, &rec.fFamilyName);
2191 rec.fFontStyle = desc2fontstyle(desc);
2192
2193 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(nameFontStyleProc,
2194 &rec);
2195 if (face) {
2196 return face;
2197 }
2198
reed@google.comce8b3de2013-03-26 19:30:16 +00002199 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFamilyName, 1, NULL));
2200 CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc);
2201 if (NULL == ctFont) {
2202 return NULL;
2203 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002204
reed@google.comce8b3de2013-03-26 19:30:16 +00002205 SkString str;
2206 CFStringToSkString(cfFamilyName, &str);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002207
reed@google.comce8b3de2013-03-26 19:30:16 +00002208 bool isFixedPitch;
2209 (void)computeStyleBits(ctFont, &isFixedPitch);
2210 SkFontID fontID = CTFontRef_to_SkFontID(ctFont);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002211
reed@google.comdea7ee02013-03-28 14:12:10 +00002212 face = SkNEW_ARGS(SkTypeface_Mac, (rec.fFontStyle, fontID, isFixedPitch,
2213 ctFont, str.c_str()));
2214 SkTypefaceCache::Add(face, face->style());
2215 return face;
reed@google.comce8b3de2013-03-26 19:30:16 +00002216}
2217
reed@google.com83787c52013-03-26 17:19:15 +00002218class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002219public:
reed@google.com83787c52013-03-26 17:19:15 +00002220 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
2221 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, NULL))
reed@google.comdea7ee02013-03-28 14:12:10 +00002222 , fFamilyName(familyName)
2223 , fCount(0) {
reed@google.com83787c52013-03-26 17:19:15 +00002224 CFRetain(familyName);
reed@google.comdea7ee02013-03-28 14:12:10 +00002225 if (NULL == fArray) {
2226 fArray = CFArrayCreate(NULL, NULL, 0, NULL);
2227 }
2228 fCount = CFArrayGetCount(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002229 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002230
reed@google.com83787c52013-03-26 17:19:15 +00002231 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002232 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002233 CFRelease(fFamilyName);
2234 }
2235
2236 virtual int count() SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002237 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002238 }
2239
2240 virtual void getStyle(int index, SkFontStyle* style,
2241 SkString* name) SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002242 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002243 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2244 if (style) {
2245 *style = desc2fontstyle(desc);
2246 }
2247 if (name) {
2248 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2249 name->reset();
2250 }
2251 }
2252 }
2253
2254 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
2255 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2256 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002257
reed@google.com83787c52013-03-26 17:19:15 +00002258 return createFromDesc(fFamilyName, desc);
2259 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002260
reed@google.com964988f2013-03-29 14:57:22 +00002261 virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
2262 if (0 == fCount) {
2263 return NULL;
2264 }
2265 return createFromDesc(fFamilyName, findMatchingDesc(pattern));
2266 }
2267
reed@google.com83787c52013-03-26 17:19:15 +00002268private:
2269 CFArrayRef fArray;
2270 CFStringRef fFamilyName;
reed@google.comdea7ee02013-03-28 14:12:10 +00002271 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002272
2273 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2274 int bestMetric = SK_MaxS32;
2275 CTFontDescriptorRef bestDesc = NULL;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002276
reed@google.com964988f2013-03-29 14:57:22 +00002277 for (int i = 0; i < fCount; ++i) {
2278 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
2279 int metric = compute_metric(pattern, desc2fontstyle(desc));
2280 if (0 == metric) {
2281 return desc;
2282 }
2283 if (metric < bestMetric) {
2284 bestMetric = metric;
2285 bestDesc = desc;
2286 }
2287 }
2288 SkASSERT(bestDesc);
2289 return bestDesc;
2290 }
reed@google.com83787c52013-03-26 17:19:15 +00002291};
2292
2293class SkFontMgr_Mac : public SkFontMgr {
2294 int fCount;
2295 CFArrayRef fNames;
2296
2297 CFStringRef stringAt(int index) const {
2298 SkASSERT((unsigned)index < (unsigned)fCount);
2299 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2300 }
2301
2302 void lazyInit() {
2303 if (NULL == fNames) {
reed@google.com3dcbd462013-03-27 13:56:34 +00002304 fNames = SkCTFontManagerCopyAvailableFontFamilyNames();
reed@google.com83787c52013-03-26 17:19:15 +00002305 fCount = fNames ? CFArrayGetCount(fNames) : 0;
2306 }
2307 }
2308
reed@google.com964988f2013-03-29 14:57:22 +00002309 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2310 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2311 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2312 &kCFTypeDictionaryKeyCallBacks,
2313 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002314
reed@google.com964988f2013-03-29 14:57:22 +00002315 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002316
reed@google.com964988f2013-03-29 14:57:22 +00002317 AutoCFRelease<CTFontDescriptorRef> desc(
2318 CTFontDescriptorCreateWithAttributes(cfAttr));
2319 return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc));
2320 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002321
reed@google.com83787c52013-03-26 17:19:15 +00002322public:
2323 SkFontMgr_Mac() : fCount(0), fNames(NULL) {}
2324
2325 virtual ~SkFontMgr_Mac() {
2326 CFSafeRelease(fNames);
2327 }
reed@google.com95625db2013-03-25 20:44:02 +00002328
2329protected:
2330 virtual int onCountFamilies() SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002331 this->lazyInit();
2332 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002333 }
2334
2335 virtual void onGetFamilyName(int index, SkString* familyName) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002336 this->lazyInit();
2337 if ((unsigned)index < (unsigned)fCount) {
2338 CFStringToSkString(this->stringAt(index), familyName);
2339 } else {
2340 familyName->reset();
2341 }
reed@google.com95625db2013-03-25 20:44:02 +00002342 }
2343
2344 virtual SkFontStyleSet* onCreateStyleSet(int index) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002345 this->lazyInit();
2346 if ((unsigned)index >= (unsigned)fCount) {
2347 return NULL;
2348 }
reed@google.com964988f2013-03-29 14:57:22 +00002349 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002350 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002351
reed@google.com964988f2013-03-29 14:57:22 +00002352 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) SK_OVERRIDE {
2353 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2354 return CreateSet(cfName);
2355 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002356
reed@google.com95625db2013-03-25 20:44:02 +00002357 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
2358 const SkFontStyle&) SK_OVERRIDE {
2359 return NULL;
2360 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002361
reed@google.com95625db2013-03-25 20:44:02 +00002362 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2363 const SkFontStyle&) SK_OVERRIDE {
2364 return NULL;
2365 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002366
reed@google.com95625db2013-03-25 20:44:02 +00002367 virtual SkTypeface* onCreateFromData(SkData* data,
2368 int ttcIndex) SK_OVERRIDE {
2369 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
2370 if (NULL == pr) {
2371 return NULL;
2372 }
2373 return create_from_dataProvider(pr);
2374 }
2375
2376 virtual SkTypeface* onCreateFromStream(SkStream* stream,
2377 int ttcIndex) SK_OVERRIDE {
2378 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
2379 if (NULL == pr) {
2380 return NULL;
2381 }
2382 return create_from_dataProvider(pr);
2383 }
2384
2385 virtual SkTypeface* onCreateFromFile(const char path[],
2386 int ttcIndex) SK_OVERRIDE {
2387 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2388 if (NULL == pr) {
2389 return NULL;
2390 }
2391 return create_from_dataProvider(pr);
2392 }
2393};
2394
2395SkFontMgr* SkFontMgr::Factory() {
2396 return SkNEW(SkFontMgr_Mac);
2397}
2398#endif