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