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