blob: ac01e5ba844baf640f58d5bd6b410203c5382476 [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
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000127static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
128 return rect.origin.x;
129}
130
131static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
132 return rect.origin.x + rect.size.width;
133}
134
135static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
136 return rect.origin.y;
137}
138
139static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
140 return rect.origin.y + rect.size.height;
141}
142
143static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
144 return rect.size.width;
145}
146
147///////////////////////////////////////////////////////////////////////////////
148
149static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
150 size_t width, size_t height, size_t rowBytes) {
151 SkASSERT(width);
152 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
153
154 if (width >= 32) {
155 while (height) {
156 sk_memset32(ptr, value, width);
157 ptr = (uint32_t*)((char*)ptr + rowBytes);
158 height -= 1;
159 }
160 return;
161 }
162
163 rowBytes -= width * sizeof(uint32_t);
164
165 if (width >= 8) {
166 while (height) {
167 int w = width;
168 do {
169 *ptr++ = value; *ptr++ = value;
170 *ptr++ = value; *ptr++ = value;
171 *ptr++ = value; *ptr++ = value;
172 *ptr++ = value; *ptr++ = value;
173 w -= 8;
174 } while (w >= 8);
175 while (--w >= 0) {
176 *ptr++ = value;
177 }
178 ptr = (uint32_t*)((char*)ptr + rowBytes);
179 height -= 1;
180 }
181 } else {
182 while (height) {
183 int w = width;
184 do {
185 *ptr++ = value;
186 } while (--w > 0);
187 ptr = (uint32_t*)((char*)ptr + rowBytes);
188 height -= 1;
189 }
190 }
191}
192
193#include <sys/utsname.h>
194
195typedef uint32_t CGRGBPixel;
196
197static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
198 return pixel & 0xFF;
199}
200
201// The calls to support subpixel are present in 10.5, but are not included in
202// the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are
203// included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for
204// instance, is present in the 10.5 CoreGraphics libary, use:
205// cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/
206// cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/
207// nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts
208
209#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
210CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value);
211CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value);
212CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value);
213CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value);
214CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value);
215#endif
216
217static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
218
219// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
220static int readVersion() {
221 struct utsname info;
222 if (uname(&info) != 0) {
223 SkDebugf("uname failed\n");
224 return 0;
225 }
226 if (strcmp(info.sysname, "Darwin") != 0) {
227 SkDebugf("unexpected uname sysname %s\n", info.sysname);
228 return 0;
229 }
230 char* dot = strchr(info.release, '.');
231 if (!dot) {
232 SkDebugf("expected dot in uname release %s\n", info.release);
233 return 0;
234 }
235 int version = atoi(info.release);
236 if (version == 0) {
237 SkDebugf("could not parse uname release %s\n", info.release);
238 }
239 return version;
240}
241
242static int darwinVersion() {
243 static int darwin_version = readVersion();
244 return darwin_version;
245}
246
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000247static bool isSnowLeopard() {
248 return darwinVersion() == 10;
249}
250
251static bool isLion() {
252 return darwinVersion() == 11;
253}
254
255static bool isMountainLion() {
256 return darwinVersion() == 12;
257}
258
259static bool isLCDFormat(unsigned format) {
260 return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
261}
262
263static CGFloat ScalarToCG(SkScalar scalar) {
264 if (sizeof(CGFloat) == sizeof(float)) {
265 return SkScalarToFloat(scalar);
266 } else {
267 SkASSERT(sizeof(CGFloat) == sizeof(double));
268 return (CGFloat) SkScalarToDouble(scalar);
269 }
270}
271
272static SkScalar CGToScalar(CGFloat cgFloat) {
273 if (sizeof(CGFloat) == sizeof(float)) {
274 return SkFloatToScalar(cgFloat);
275 } else {
276 SkASSERT(sizeof(CGFloat) == sizeof(double));
277 return SkDoubleToScalar(cgFloat);
278 }
279}
280
281static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
282 SkScalar sx = SK_Scalar1,
283 SkScalar sy = SK_Scalar1) {
284 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
285 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
286 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
287 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
288 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
289 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
290}
291
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000292///////////////////////////////////////////////////////////////////////////////
293
294#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
295#define BITMAP_INFO_GRAY (kCGImageAlphaNone)
296
297/**
298 * There does not appear to be a publicly accessable API for determining if lcd
299 * font smoothing will be applied if we request it. The main issue is that if
300 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
301 */
302static bool supports_LCD() {
303 static int gSupportsLCD = -1;
304 if (gSupportsLCD >= 0) {
305 return (bool) gSupportsLCD;
306 }
307 uint32_t rgb = 0;
308 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
309 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
310 colorspace, BITMAP_INFO_RGB));
311 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
312 CGContextSetShouldSmoothFonts(cgContext, true);
313 CGContextSetShouldAntialias(cgContext, true);
314 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
315 CGContextSetGrayFillColor(cgContext, 1, 1);
316 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
317 uint32_t r = (rgb >> 16) & 0xFF;
318 uint32_t g = (rgb >> 8) & 0xFF;
319 uint32_t b = (rgb >> 0) & 0xFF;
320 gSupportsLCD = (r != g || r != b);
321 return (bool) gSupportsLCD;
322}
323
324class Offscreen {
325public:
326 Offscreen();
327
328 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
329 CGGlyph glyphID, size_t* rowBytesPtr,
330 bool generateA8FromLCD);
331
332private:
333 enum {
334 kSize = 32 * 32 * sizeof(CGRGBPixel)
335 };
336 SkAutoSMalloc<kSize> fImageStorage;
337 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
338
339 // cached state
340 AutoCFRelease<CGContextRef> fCG;
341 SkISize fSize;
342 bool fDoAA;
343 bool fDoLCD;
344
345 static int RoundSize(int dimension) {
346 return SkNextPow2(dimension);
347 }
348};
349
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +0000350Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL),
robertphillips@google.com87379e12013-03-29 12:11:10 +0000351 fDoAA(false), fDoLCD(false) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000352 fSize.set(0, 0);
353}
354
355///////////////////////////////////////////////////////////////////////////////
356
bungeman@google.comfe747652013-03-25 19:36:11 +0000357static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000358 unsigned style = SkTypeface::kNormal;
359 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
360
361 if (traits & kCTFontBoldTrait) {
362 style |= SkTypeface::kBold;
363 }
364 if (traits & kCTFontItalicTrait) {
365 style |= SkTypeface::kItalic;
366 }
bungeman@google.comfe747652013-03-25 19:36:11 +0000367 if (isFixedPitch) {
368 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000369 }
370 return (SkTypeface::Style)style;
371}
372
373static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
374 SkFontID id = 0;
375// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
376// bracket this to be Mac only.
377#ifdef SK_BUILD_FOR_MAC
378 ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
379 id = (SkFontID)ats;
380 if (id != 0) {
381 id &= 0x3FFFFFFF; // make top two bits 00
382 return id;
383 }
384#endif
385 // CTFontGetPlatformFont returns NULL if the font is local
386 // (e.g., was created by a CSS3 @font-face rule).
387 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
388 AutoCGTable<SkOTTableHead> headTable(cgFont);
389 if (headTable.fData) {
390 id = (SkFontID) headTable->checksumAdjustment;
391 id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
392 }
393 // well-formed fonts have checksums, but as a last resort, use the pointer.
394 if (id == 0) {
395 id = (SkFontID) (uintptr_t) fontRef;
396 id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
397 }
398 return id;
399}
400
reed@google.comce8b3de2013-03-26 19:30:16 +0000401static SkFontStyle stylebits2fontstyle(SkTypeface::Style styleBits) {
402 return SkFontStyle((styleBits & SkTypeface::kBold)
403 ? SkFontStyle::kBold_Weight
404 : SkFontStyle::kNormal_Weight,
405 SkFontStyle::kNormal_Width,
406 (styleBits & SkTypeface::kItalic)
407 ? SkFontStyle::kItalic_Slant
408 : SkFontStyle::kUpright_Slant);
409}
410
411#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
412
413static SkTypeface::Style fontstyle2stylebits(const SkFontStyle& fs) {
414 unsigned style = 0;
415 if (fs.width() >= WEIGHT_THRESHOLD) {
416 style |= SkTypeface::kBold;
417 }
418 if (fs.isItalic()) {
419 style |= SkTypeface::kItalic;
420 }
421 return (SkTypeface::Style)style;
422}
423
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000424class SkTypeface_Mac : public SkTypeface {
425public:
bungeman@google.comfe747652013-03-25 19:36:11 +0000426 SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isFixedPitch,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000427 CTFontRef fontRef, const char name[])
reed@google.comce8b3de2013-03-26 19:30:16 +0000428 : SkTypeface(style, fontID, isFixedPitch)
429 , fName(name)
430 , fFontRef(fontRef) // caller has already called CFRetain for us
431 , fFontStyle(stylebits2fontstyle(style))
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000432 {
433 SkASSERT(fontRef);
434 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000435
reed@google.comce8b3de2013-03-26 19:30:16 +0000436 SkTypeface_Mac(const SkFontStyle& fs, SkFontID fontID, bool isFixedPitch,
437 CTFontRef fontRef, const char name[])
438 : SkTypeface(fontstyle2stylebits(fs), fontID, isFixedPitch)
439 , fName(name)
440 , fFontRef(fontRef) // caller has already called CFRetain for us
441 , fFontStyle(fs)
442 {
443 SkASSERT(fontRef);
444 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000445
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000446 SkString fName;
447 AutoCFRelease<CTFontRef> fFontRef;
reed@google.comce8b3de2013-03-26 19:30:16 +0000448 SkFontStyle fFontStyle;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000449
450protected:
451 friend class SkFontHost; // to access our protected members for deprecated methods
452
453 virtual int onGetUPEM() const SK_OVERRIDE;
reed@google.comcc9aad52013-03-21 19:28:10 +0000454 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000455 virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
456 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
457 size_t length, void* data) const SK_OVERRIDE;
458 virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
459 virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
reed@google.com5526ede2013-03-25 13:03:37 +0000460 virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
reed@google.com2689f612013-03-20 20:01:47 +0000461 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
462 SkAdvancedTypefaceMetrics::PerGlyphInfo,
463 const uint32_t*, uint32_t) const SK_OVERRIDE;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000464
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000465private:
reed@google.comce8b3de2013-03-26 19:30:16 +0000466
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000467 typedef SkTypeface INHERITED;
468};
469
470static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
471 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000472 bool isFixedPitch;
473 SkTypeface::Style style = computeStyleBits(fontRef, &isFixedPitch);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000474 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
475
bungeman@google.comfe747652013-03-25 19:36:11 +0000476 return new SkTypeface_Mac(style, fontID, isFixedPitch, fontRef, name);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000477}
478
479static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
480 CTFontRef ctFont = NULL;
481
482 CTFontSymbolicTraits ctFontTraits = 0;
483 if (theStyle & SkTypeface::kBold) {
484 ctFontTraits |= kCTFontBoldTrait;
485 }
486 if (theStyle & SkTypeface::kItalic) {
487 ctFontTraits |= kCTFontItalicTrait;
488 }
489
490 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000491 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000492
493 AutoCFRelease<CFNumberRef> cfFontTraits(
494 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
495
496 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
497 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
498 &kCFTypeDictionaryKeyCallBacks,
499 &kCFTypeDictionaryValueCallBacks));
500
501 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
502 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
503 &kCFTypeDictionaryKeyCallBacks,
504 &kCFTypeDictionaryValueCallBacks));
505
506 // Create the font
507 if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
508 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
509
510 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
511 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
512
513 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
514 CTFontDescriptorCreateWithAttributes(cfAttributes));
515
516 if (ctFontDesc != NULL) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000517 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000518 }
519 }
520
521 return ctFont ? NewFromFontRef(ctFont, familyName) : NULL;
522}
523
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000524static SkTypeface* GetDefaultFace() {
525 SK_DECLARE_STATIC_MUTEX(gMutex);
526 SkAutoMutexAcquire ma(gMutex);
527
528 static SkTypeface* gDefaultFace;
529
530 if (NULL == gDefaultFace) {
531 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal);
532 SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal);
533 }
534 return gDefaultFace;
535}
536
537///////////////////////////////////////////////////////////////////////////////
538
539extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
540CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
541 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
542 return macface ? macface->fFontRef.get() : NULL;
543}
544
545/* This function is visible on the outside. It first searches the cache, and if
546 * not found, returns a new entry (after adding it to the cache).
547 */
548SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
549 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
550 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
551 if (face) {
552 face->ref();
553 } else {
554 face = NewFromFontRef(fontRef, NULL);
555 SkTypefaceCache::Add(face, face->style());
556 // NewFromFontRef doesn't retain the parameter, but the typeface it
557 // creates does release it in its destructor, so we balance that with
558 // a retain call here.
559 CFRetain(fontRef);
560 }
561 SkASSERT(face->getRefCnt() > 1);
562 return face;
563}
564
565struct NameStyleRec {
566 const char* fName;
567 SkTypeface::Style fStyle;
568};
569
570static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style,
571 void* ctx) {
572 const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face);
573 const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx);
574
575 return rec->fStyle == style && mface->fName.equals(rec->fName);
576}
577
578static const char* map_css_names(const char* name) {
579 static const struct {
580 const char* fFrom; // name the caller specified
581 const char* fTo; // "canonical" name we map to
582 } gPairs[] = {
583 { "sans-serif", "Helvetica" },
584 { "serif", "Times" },
585 { "monospace", "Courier" }
586 };
587
588 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
589 if (strcmp(name, gPairs[i].fFrom) == 0) {
590 return gPairs[i].fTo;
591 }
592 }
593 return name; // no change
594}
595
596SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
597 const char familyName[],
598 SkTypeface::Style style) {
599 if (familyName) {
600 familyName = map_css_names(familyName);
601 }
602
603 // Clone an existing typeface
604 // TODO: only clone if style matches the familyFace's style...
605 if (familyName == NULL && familyFace != NULL) {
606 familyFace->ref();
607 return const_cast<SkTypeface*>(familyFace);
608 }
609
610 if (!familyName || !*familyName) {
611 familyName = FONT_DEFAULT_NAME;
612 }
613
614 NameStyleRec rec = { familyName, style };
615 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec);
616
617 if (NULL == face) {
618 face = NewFromName(familyName, style);
619 if (face) {
620 SkTypefaceCache::Add(face, style);
621 } else {
622 face = GetDefaultFace();
623 face->ref();
624 }
625 }
626 return face;
627}
628
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000629///////////////////////////////////////////////////////////////////////////////
630
bungeman@google.comcefd9812013-05-15 15:07:32 +0000631/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000632struct GlyphRect {
633 int16_t fMinX;
634 int16_t fMinY;
635 int16_t fMaxX;
636 int16_t fMaxY;
637};
638
639class SkScalerContext_Mac : public SkScalerContext {
640public:
reed@google.com0da48612013-03-19 16:06:52 +0000641 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000642
643protected:
644 unsigned generateGlyphCount(void) SK_OVERRIDE;
645 uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
646 void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
647 void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
648 void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
649 void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
650 void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
651
652private:
653 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000654
bungeman@google.comcefd9812013-05-15 15:07:32 +0000655 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
656 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000657
658 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
659 *
660 * For use with (and must be called before) generateBBoxes.
661 */
662 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000663
bungeman@google.comcefd9812013-05-15 15:07:32 +0000664 /** Initializes fFBoundingBoxes and returns true on success.
665 *
666 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
667 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
668 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
669 * font directly.
670 *
671 * This routine initializes fFBoundingBoxes to an array of
672 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
673 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
674 *
675 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
676 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
677 *
678 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
679 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000680 bool generateBBoxes();
681
bungeman@google.comcefd9812013-05-15 15:07:32 +0000682 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
683 *
684 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
685 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
686 */
687 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000688
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000689 Offscreen fOffscreen;
690 AutoCFRelease<CTFontRef> fCTFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000691
bungeman@google.comcefd9812013-05-15 15:07:32 +0000692 /** Vertical variant of fCTFont.
693 *
694 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
695 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
696 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
697 * Use fCTVerticalFont with kCTFontVerticalOrientation to get metrics in the same space.
698 */
699 AutoCFRelease<CTFontRef> fCTVerticalFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000700
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000701 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000702 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000703 uint16_t fFBoundingBoxesGlyphOffset;
704 uint16_t fGlyphCount;
705 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000706 const bool fDoSubPosition;
707 const bool fVertical;
708
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000709 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000710
711 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000712};
713
reed@google.com0da48612013-03-19 16:06:52 +0000714SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
715 const SkDescriptor* desc)
716 : INHERITED(typeface, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000717 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000718 , fFBoundingBoxesGlyphOffset(0)
719 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000720 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
721 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
722
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000723{
reed@google.com2689f612013-03-20 20:01:47 +0000724 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000725 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000726 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
727 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000728
bungeman@google.comcefd9812013-05-15 15:07:32 +0000729 fRec.getSingleMatrix(&fFUnitMatrix);
730 CGAffineTransform transform = MatrixToCGAffineTransform(fFUnitMatrix);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000731
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000732 AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
733 if (fVertical) {
734 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
735 kCFAllocatorDefault, 0,
736 &kCFTypeDictionaryKeyCallBacks,
737 &kCFTypeDictionaryValueCallBacks));
738 if (cfAttributes) {
739 CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
740 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
741 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
742 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
743 ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
744 }
745 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000746 // Since our matrix includes everything, we pass 1 for size.
747 fCTFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, ctFontDesc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000748 fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL);
749 if (fVertical) {
750 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
751 transform = CGAffineTransformConcat(rotateLeft, transform);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000752 fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000753 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000754
bungeman@google.comcefd9812013-05-15 15:07:32 +0000755 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
756 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000757}
758
759CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
760 CGGlyph glyphID, size_t* rowBytesPtr,
761 bool generateA8FromLCD) {
762 if (!fRGBSpace) {
763 //It doesn't appear to matter what color space is specified.
764 //Regular blends and antialiased text are always (s*a + d*(1-a))
765 //and smoothed text is always g=2.0.
766 fRGBSpace = CGColorSpaceCreateDeviceRGB();
767 }
768
769 // default to kBW_Format
770 bool doAA = false;
771 bool doLCD = false;
772
773 if (SkMask::kBW_Format != glyph.fMaskFormat) {
774 doLCD = true;
775 doAA = true;
776 }
777
778 // FIXME: lcd smoothed un-hinted rasterization unsupported.
779 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
780 doLCD = false;
781 doAA = true;
782 }
783
784 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
785 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
786 if (fSize.fWidth < glyph.fWidth) {
787 fSize.fWidth = RoundSize(glyph.fWidth);
788 }
789 if (fSize.fHeight < glyph.fHeight) {
790 fSize.fHeight = RoundSize(glyph.fHeight);
791 }
792
793 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
794 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
795 fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
796 rowBytes, fRGBSpace, BITMAP_INFO_RGB);
797
798 // skia handles quantization itself, so we disable this for cg to get
799 // full fractional data from them.
800 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
801 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
802
803 CGContextSetTextDrawingMode(fCG, kCGTextFill);
804 CGContextSetFont(fCG, context.fCGFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000805 CGContextSetFontSize(fCG, 1 /*CTFontGetSize(context.fCTFont)*/);
806 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000807
bungeman@google.comcefd9812013-05-15 15:07:32 +0000808 // Because CG always draws from the horizontal baseline,
809 // if there is a non-integral translation from the horizontal origin to the vertical origin,
810 // then CG cannot draw the glyph in the correct location without subpixel positioning.
811 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical);
812 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || context.fVertical);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000813
814 // Draw white on black to create mask.
815 // TODO: Draw black on white and invert, CG has a special case codepath.
816 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
817
818 // force our checks below to happen
819 fDoAA = !doAA;
820 fDoLCD = !doLCD;
821 }
822
823 if (fDoAA != doAA) {
824 CGContextSetShouldAntialias(fCG, doAA);
825 fDoAA = doAA;
826 }
827 if (fDoLCD != doLCD) {
828 CGContextSetShouldSmoothFonts(fCG, doLCD);
829 fDoLCD = doLCD;
830 }
831
832 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
833 // skip rows based on the glyph's height
834 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
835
836 // erase to black
837 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
838
839 float subX = 0;
840 float subY = 0;
841 if (context.fDoSubPosition) {
842 subX = SkFixedToFloat(glyph.getSubXFixed());
843 subY = SkFixedToFloat(glyph.getSubYFixed());
844 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000845
bungeman@google.comcefd9812013-05-15 15:07:32 +0000846 // CGContextShowGlyphsAtPoint always draws using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000847 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000848 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000849 context.getVerticalOffset(glyphID, &offset);
850 subX += offset.fX;
851 subY += offset.fY;
852 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000853
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000854 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
855 glyph.fTop + glyph.fHeight - subY,
856 &glyphID, 1);
857
858 SkASSERT(rowBytesPtr);
859 *rowBytesPtr = rowBytes;
860 return image;
861}
862
bungeman@google.comcefd9812013-05-15 15:07:32 +0000863void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
864 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
865 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
866 CGSize cgVertOffset;
867 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
868
869 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
870 if (isSnowLeopard()) {
871 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
872 fFUnitMatrix.mapPoints(&skVertOffset, 1);
873 } else {
874 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
875 skVertOffset.fY = -skVertOffset.fY;
876 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000877
bungeman@google.comcefd9812013-05-15 15:07:32 +0000878 *offset = skVertOffset;
879}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000880
881uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
882 if (fFBoundingBoxesGlyphOffset) {
883 return fFBoundingBoxesGlyphOffset;
884 }
885 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
886 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
887 if (hheaTable.fData) {
888 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
889 }
890 return fFBoundingBoxesGlyphOffset;
891}
reed@android.com0680d6c2008-12-19 19:46:15 +0000892
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000893bool SkScalerContext_Mac::generateBBoxes() {
894 if (fGeneratedFBoundingBoxes) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000895 return NULL != fFBoundingBoxes.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000896 }
897 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000898
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000899 AutoCGTable<SkOTTableHead> headTable(fCGFont);
900 if (!headTable.fData) {
901 return false;
902 }
903
904 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
905 if (!locaTable.fData) {
906 return false;
907 }
908
909 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
910 if (!glyfTable.fData) {
911 return false;
912 }
913
914 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000915 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000916
917 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
918 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
919 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
920 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
921 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
922 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
923 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
924 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
925 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
926 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
927 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000928
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000929 return true;
930}
931
932unsigned SkScalerContext_Mac::generateGlyphCount(void) {
933 return fGlyphCount;
934}
935
936uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
937 CGGlyph cgGlyph;
938 UniChar theChar;
939
940 // Validate our parameters and state
941 SkASSERT(uni <= 0x0000FFFF);
942 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
943
944 // Get the glyph
945 theChar = (UniChar) uni;
946
947 if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1)) {
948 cgGlyph = 0;
949 }
950
951 return cgGlyph;
952}
953
954void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
955 this->generateMetrics(glyph);
956}
957
bungeman@google.comcefd9812013-05-15 15:07:32 +0000958void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
959 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
960 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000961
bungeman@google.comcefd9812013-05-15 15:07:32 +0000962 // The following block produces cgAdvance in CG units (pixels, y up).
963 CGSize cgAdvance;
964 if (fVertical) {
965 CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
966 &cgGlyph, &cgAdvance, 1);
967 } else {
968 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
969 &cgGlyph, &cgAdvance, 1);
970 }
971 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
972 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000973
bungeman@google.comcefd9812013-05-15 15:07:32 +0000974 // The following produces skBounds in SkGlyph units (pixels, y down),
975 // or returns early if skBounds would be empty.
976 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000977
bungeman@google.comcefd9812013-05-15 15:07:32 +0000978 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
979 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
980 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
981 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
982 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
983 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
984 // to center the glyph along the vertical baseline and also perform some mysterious shift
985 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
986 // these steps.
987 //
988 // It is not known which is correct (or if either is correct). However, we must always draw
989 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
990 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000991
bungeman@google.comcefd9812013-05-15 15:07:32 +0000992 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
993 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000994
bungeman@google.comcefd9812013-05-15 15:07:32 +0000995 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
996 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
997 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
998 // font directly.
999 if ((isLion() || isMountainLion()) &&
1000 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1001 {
1002 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1003 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1004 return;
1005 }
1006 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1007 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1008 fFUnitMatrix.mapRect(&skBounds);
1009
1010 } else {
1011 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1012 CGRect cgBounds;
1013 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1014 &cgGlyph, &cgBounds, 1);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001015
bungeman@google.comcefd9812013-05-15 15:07:32 +00001016 // BUG?
1017 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1018 // it should be empty. So, if we see a zero-advance, we check if it has an
1019 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1020 // is rare, so we won't incur a big performance cost for this extra check.
1021 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
1022 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
1023 if (NULL == path || CGPathIsEmpty(path)) {
1024 return;
1025 }
1026 }
1027
1028 if (CGRectIsEmpty_inline(cgBounds)) {
1029 return;
1030 }
1031
1032 // Convert cgBounds to SkGlyph units (pixels, y down).
1033 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1034 cgBounds.size.width, cgBounds.size.height);
1035 }
1036
1037 if (fVertical) {
1038 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1039 // Convert these horizontal bounds into vertical bounds.
1040 SkPoint offset;
1041 getVerticalOffset(cgGlyph, &offset);
1042 skBounds.offset(offset);
1043 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001044
bungeman@google.comcefd9812013-05-15 15:07:32 +00001045 // Currently the bounds are based on being rendered at (0,0).
1046 // The top left must not move, since that is the base from which subpixel positioning is offset.
1047 if (fDoSubPosition) {
1048 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1049 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1050 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001051
bungeman@google.comcefd9812013-05-15 15:07:32 +00001052 SkIRect skIBounds;
1053 skBounds.roundOut(&skIBounds);
1054 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1055 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1056 // is not currently known, as CG dilates the outlines by some percentage.
1057 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1058 skIBounds.outset(1, 1);
1059 glyph->fLeft = SkToS16(skIBounds.fLeft);
1060 glyph->fTop = SkToS16(skIBounds.fTop);
1061 glyph->fWidth = SkToU16(skIBounds.width());
1062 glyph->fHeight = SkToU16(skIBounds.height());
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001063
bungeman@google.comcefd9812013-05-15 15:07:32 +00001064#ifdef HACK_COLORGLYPHS
1065 glyph->fMaskFormat = SkMask::kARGB32_Format;
1066#endif
1067}
1068
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001069#include "SkColorPriv.h"
1070
1071static void build_power_table(uint8_t table[], float ee) {
1072 for (int i = 0; i < 256; i++) {
1073 float x = i / 255.f;
1074 x = sk_float_pow(x, ee);
1075 int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
1076 table[i] = SkToU8(xx);
1077 }
1078}
1079
1080/**
1081 * This will invert the gamma applied by CoreGraphics, so we can get linear
1082 * values.
1083 *
1084 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1085 * The color space used does not appear to affect this choice.
1086 */
1087static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1088 static bool gInited;
1089 static uint8_t gTableCoreGraphicsSmoothing[256];
1090 if (!gInited) {
1091 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1092 gInited = true;
1093 }
1094 return gTableCoreGraphicsSmoothing;
1095}
1096
1097static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1098 while (count > 0) {
1099 uint8_t mask = 0;
1100 for (int i = 7; i >= 0; --i) {
1101 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1102 if (0 == --count) {
1103 break;
1104 }
1105 }
1106 *dst++ = mask;
1107 }
1108}
1109
1110template<bool APPLY_PREBLEND>
1111static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1112 U8CPU r = (rgb >> 16) & 0xFF;
1113 U8CPU g = (rgb >> 8) & 0xFF;
1114 U8CPU b = (rgb >> 0) & 0xFF;
1115 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1116}
1117template<bool APPLY_PREBLEND>
1118static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1119 const SkGlyph& glyph, const uint8_t* table8) {
1120 const int width = glyph.fWidth;
1121 size_t dstRB = glyph.rowBytes();
1122 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1123
1124 for (int y = 0; y < glyph.fHeight; y++) {
1125 for (int i = 0; i < width; ++i) {
1126 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1127 }
1128 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1129 dst += dstRB;
1130 }
1131}
1132
1133template<bool APPLY_PREBLEND>
1134static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1135 const uint8_t* tableG,
1136 const uint8_t* tableB) {
1137 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1138 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1139 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1140 return SkPack888ToRGB16(r, g, b);
1141}
1142template<bool APPLY_PREBLEND>
1143static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1144 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1145 const int width = glyph.fWidth;
1146 size_t dstRB = glyph.rowBytes();
1147 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1148
1149 for (int y = 0; y < glyph.fHeight; y++) {
1150 for (int i = 0; i < width; i++) {
1151 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1152 }
1153 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1154 dst = (uint16_t*)((char*)dst + dstRB);
1155 }
1156}
1157
1158template<bool APPLY_PREBLEND>
1159static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
1160 const uint8_t* tableG,
1161 const uint8_t* tableB) {
1162 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1163 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1164 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1165 return SkPackARGB32(0xFF, r, g, b);
1166}
1167template<bool APPLY_PREBLEND>
1168static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1169 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1170 const int width = glyph.fWidth;
1171 size_t dstRB = glyph.rowBytes();
1172 uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
1173 for (int y = 0; y < glyph.fHeight; y++) {
1174 for (int i = 0; i < width; i++) {
1175 dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1176 }
1177 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1178 dst = (uint32_t*)((char*)dst + dstRB);
1179 }
1180}
1181
reed@google.comf77b35d2013-05-02 20:39:44 +00001182#ifdef HACK_COLORGLYPHS
1183// hack to colorize the output for testing kARGB32_Format
1184static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb, const SkGlyph& glyph,
1185 int x, int y) {
1186 U8CPU r = (rgb >> 16) & 0xFF;
1187 U8CPU g = (rgb >> 8) & 0xFF;
1188 U8CPU b = (rgb >> 0) & 0xFF;
1189 unsigned a = SkComputeLuminance(r, g, b);
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +00001190
reed@google.comf77b35d2013-05-02 20:39:44 +00001191 // compute gradient from x,y
1192 r = x * 255 / glyph.fWidth;
1193 g = 0;
1194 b = (glyph.fHeight - y) * 255 / glyph.fHeight;
1195 return SkPreMultiplyARGB(a, r, g, b); // red
1196}
1197#endif
1198
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001199template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1200 return (T*)((char*)ptr + byteOffset);
1201}
1202
1203void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
1204 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
1205
1206 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1207 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1208
1209 // Draw the glyph
1210 size_t cgRowBytes;
1211 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
1212 if (cgPixels == NULL) {
1213 return;
1214 }
1215
1216 //TODO: see if drawing black on white and inverting is faster (at least in
1217 //lcd case) as core graphics appears to have special case code for drawing
1218 //black text.
1219
1220 // Fix the glyph
1221 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1222 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1223 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1224
1225 //Note that the following cannot really be integrated into the
1226 //pre-blend, since we may not be applying the pre-blend; when we aren't
1227 //applying the pre-blend it means that a filter wants linear anyway.
1228 //Other code may also be applying the pre-blend, so we'd need another
1229 //one with this and one without.
1230 CGRGBPixel* addr = cgPixels;
1231 for (int y = 0; y < glyph.fHeight; ++y) {
1232 for (int x = 0; x < glyph.fWidth; ++x) {
1233 int r = (addr[x] >> 16) & 0xFF;
1234 int g = (addr[x] >> 8) & 0xFF;
1235 int b = (addr[x] >> 0) & 0xFF;
1236 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1237 }
1238 addr = SkTAddByteOffset(addr, cgRowBytes);
1239 }
1240 }
1241
1242 // Convert glyph to mask
1243 switch (glyph.fMaskFormat) {
1244 case SkMask::kLCD32_Format: {
1245 if (fPreBlend.isApplicable()) {
1246 rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
1247 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1248 } else {
1249 rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
1250 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1251 }
1252 } break;
1253 case SkMask::kLCD16_Format: {
1254 if (fPreBlend.isApplicable()) {
1255 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1256 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1257 } else {
1258 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1259 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1260 }
1261 } break;
1262 case SkMask::kA8_Format: {
1263 if (fPreBlend.isApplicable()) {
1264 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1265 } else {
1266 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1267 }
1268 } break;
1269 case SkMask::kBW_Format: {
1270 const int width = glyph.fWidth;
1271 size_t dstRB = glyph.rowBytes();
1272 uint8_t* dst = (uint8_t*)glyph.fImage;
1273 for (int y = 0; y < glyph.fHeight; y++) {
1274 cgpixels_to_bits(dst, cgPixels, width);
1275 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1276 dst += dstRB;
1277 }
1278 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001279#ifdef HACK_COLORGLYPHS
1280 case SkMask::kARGB32_Format: {
1281 const int width = glyph.fWidth;
1282 size_t dstRB = glyph.rowBytes();
1283 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1284 for (int y = 0; y < glyph.fHeight; y++) {
1285 for (int x = 0; x < width; ++x) {
1286 dst[x] = cgpixels_to_pmcolor(cgPixels[x], glyph, x, y);
1287 }
1288 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1289 dst = (SkPMColor*)((char*)dst + dstRB);
1290 }
1291 } break;
1292#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001293 default:
1294 SkDEBUGFAIL("unexpected mask format");
1295 break;
1296 }
1297}
1298
1299/*
1300 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1301 * seems sufficient, and possibly even correct, to allow the hinted outline
1302 * to be subpixel positioned.
1303 */
1304#define kScaleForSubPixelPositionHinting (4.0f)
1305
1306void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
1307 CTFontRef font = fCTFont;
1308 SkScalar scaleX = SK_Scalar1;
1309 SkScalar scaleY = SK_Scalar1;
1310
1311 /*
1312 * For subpixel positioning, we want to return an unhinted outline, so it
1313 * can be positioned nicely at fractional offsets. However, we special-case
1314 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1315 * we want to retain hinting in the direction orthogonal to the baseline.
1316 * e.g. for horizontal baseline, we want to retain hinting in Y.
1317 * The way we remove hinting is to scale the font by some value (4) in that
1318 * direction, ask for the path, and then scale the path back down.
1319 */
1320 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1321 SkMatrix m;
1322 fRec.getSingleMatrix(&m);
1323
1324 // start out by assuming that we want no hining in X and Y
1325 scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
1326 // now see if we need to restore hinting for axis-aligned baselines
1327 switch (SkComputeAxisAlignmentForHText(m)) {
1328 case kX_SkAxisAlignment:
1329 scaleY = SK_Scalar1; // want hinting in the Y direction
1330 break;
1331 case kY_SkAxisAlignment:
1332 scaleX = SK_Scalar1; // want hinting in the X direction
1333 break;
1334 default:
1335 break;
1336 }
1337
1338 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
1339 // need to release font when we're done
1340 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
1341 }
1342
1343 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
1344 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
1345
1346 path->reset();
1347 if (cgPath != NULL) {
1348 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1349 }
1350
bungeman@google.comcefd9812013-05-15 15:07:32 +00001351 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001352 SkMatrix m;
1353 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1354 path->transform(m);
1355 // balance the call to CTFontCreateCopyWithAttributes
1356 CFSafeRelease(font);
1357 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001358 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001359 SkPoint offset;
1360 getVerticalOffset(cgGlyph, &offset);
1361 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001362 }
1363}
1364
1365void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
1366 SkPaint::FontMetrics* my) {
1367 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1368
1369 SkPaint::FontMetrics theMetrics;
1370 theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1371 theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1372 theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1373 theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1374 theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1375 theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1376 theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1377 theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
1378 theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
1379
1380 if (mx != NULL) {
1381 *mx = theMetrics;
1382 }
1383 if (my != NULL) {
1384 *my = theMetrics;
1385 }
1386}
1387
1388void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1389 SkPath* skPath = (SkPath*)info;
1390
1391 // Process the path element
1392 switch (element->type) {
1393 case kCGPathElementMoveToPoint:
1394 skPath->moveTo(element->points[0].x, -element->points[0].y);
1395 break;
1396
1397 case kCGPathElementAddLineToPoint:
1398 skPath->lineTo(element->points[0].x, -element->points[0].y);
1399 break;
1400
1401 case kCGPathElementAddQuadCurveToPoint:
1402 skPath->quadTo(element->points[0].x, -element->points[0].y,
1403 element->points[1].x, -element->points[1].y);
1404 break;
1405
1406 case kCGPathElementAddCurveToPoint:
1407 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1408 element->points[1].x, -element->points[1].y,
1409 element->points[2].x, -element->points[2].y);
1410 break;
1411
1412 case kCGPathElementCloseSubpath:
1413 skPath->close();
1414 break;
1415
1416 default:
1417 SkDEBUGFAIL("Unknown path element!");
1418 break;
1419 }
1420}
1421
1422
1423///////////////////////////////////////////////////////////////////////////////
1424
1425// Returns NULL on failure
1426// Call must still manage its ownership of provider
1427static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1428 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
1429 if (NULL == cg) {
1430 return NULL;
1431 }
1432 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
1433 return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
1434}
1435
1436SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
1437 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
1438 if (NULL == provider) {
1439 return NULL;
1440 }
1441 return create_from_dataProvider(provider);
1442}
1443
1444SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
1445 AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
1446 if (NULL == provider) {
1447 return NULL;
1448 }
1449 return create_from_dataProvider(provider);
1450}
1451
1452// Web fonts added to the the CTFont registry do not return their character set.
1453// Iterate through the font in this case. The existing caller caches the result,
1454// so the performance impact isn't too bad.
1455static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1456 SkTDArray<SkUnichar>* glyphToUnicode) {
1457 glyphToUnicode->setCount(glyphCount);
1458 SkUnichar* out = glyphToUnicode->begin();
1459 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1460 UniChar unichar = 0;
1461 while (glyphCount > 0) {
1462 CGGlyph glyph;
1463 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1464 out[glyph] = unichar;
1465 --glyphCount;
1466 }
1467 if (++unichar == 0) {
1468 break;
1469 }
1470 }
1471}
1472
1473// Construct Glyph to Unicode table.
1474// Unicode code points that require conjugate pairs in utf16 are not
1475// supported.
1476static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1477 SkTDArray<SkUnichar>* glyphToUnicode) {
1478 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1479 if (!charSet) {
1480 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1481 return;
1482 }
1483
1484 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1485 charSet));
1486 if (!bitmap) {
1487 return;
1488 }
1489 CFIndex length = CFDataGetLength(bitmap);
1490 if (!length) {
1491 return;
1492 }
1493 if (length > 8192) {
1494 // TODO: Add support for Unicode above 0xFFFF
1495 // Consider only the BMP portion of the Unicode character points.
1496 // The bitmap may contain other planes, up to plane 16.
1497 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1498 length = 8192;
1499 }
1500 const UInt8* bits = CFDataGetBytePtr(bitmap);
1501 glyphToUnicode->setCount(glyphCount);
1502 SkUnichar* out = glyphToUnicode->begin();
1503 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1504 for (int i = 0; i < length; i++) {
1505 int mask = bits[i];
1506 if (!mask) {
1507 continue;
1508 }
1509 for (int j = 0; j < 8; j++) {
1510 CGGlyph glyph;
1511 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1512 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1513 out[glyph] = unichar;
1514 }
1515 }
1516 }
1517}
1518
1519static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1520 CGSize advance;
1521 advance.width = 0;
1522 CGGlyph glyph = gId;
1523 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1524 *data = sk_float_round2int(advance.width);
1525 return true;
1526}
1527
1528// we might move this into our CGUtils...
1529static void CFStringToSkString(CFStringRef src, SkString* dst) {
1530 // Reserve enough room for the worst-case string,
1531 // plus 1 byte for the trailing null.
1532 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1533 kCFStringEncodingUTF8) + 1;
1534 dst->resize(length);
1535 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1536 // Resize to the actual UTF-8 length used, stripping the null character.
1537 dst->resize(strlen(dst->c_str()));
1538}
1539
reed@google.com2689f612013-03-20 20:01:47 +00001540SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001541 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1542 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001543 uint32_t glyphIDsCount) const {
1544
1545 CTFontRef originalCTFont = fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001546 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
1547 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
1548 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1549
1550 {
1551 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
1552 CFStringToSkString(fontName, &info->fFontName);
1553 }
1554
1555 info->fMultiMaster = false;
1556 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1557 info->fLastGlyphID = SkToU16(glyphCount - 1);
1558 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1559
1560 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1561 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1562 }
1563
1564 info->fStyle = 0;
1565
1566 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1567 // fonts always have both glyf and loca tables. At the least, this is what
1568 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1569 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001570 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001571 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1572 info->fItalicAngle = 0;
1573 info->fAscent = 0;
1574 info->fDescent = 0;
1575 info->fStemV = 0;
1576 info->fCapHeight = 0;
1577 info->fBBox = SkIRect::MakeEmpty();
1578 return info;
1579 }
1580
1581 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1582 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1583 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1584 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1585 }
1586 if (symbolicTraits & kCTFontItalicTrait) {
1587 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1588 }
1589 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001590 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1591 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1592 } else if (stylisticClass & kCTFontScriptsClass) {
1593 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1594 }
1595 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1596 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1597 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1598 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1599 CGRect bbox = CTFontGetBoundingBox(ctFont);
1600
1601 SkRect r;
1602 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1603 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1604 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1605 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1606
1607 r.roundOut(&(info->fBBox));
1608
1609 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1610 // This probably isn't very good with an italic font.
1611 int16_t min_width = SHRT_MAX;
1612 info->fStemV = 0;
1613 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1614 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1615 CGGlyph glyphs[count];
1616 CGRect boundingRects[count];
1617 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1618 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1619 glyphs, boundingRects, count);
1620 for (size_t i = 0; i < count; i++) {
1621 int16_t width = (int16_t) boundingRects[i].size.width;
1622 if (width > 0 && width < min_width) {
1623 min_width = width;
1624 info->fStemV = min_width;
1625 }
1626 }
1627 }
1628
1629 if (false) { // TODO: haven't figured out how to know if font is embeddable
1630 // (information is in the OS/2 table)
1631 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
1632 } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
1633 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1634 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1635 info->fGlyphWidths->fAdvance.append(1, &min_width);
1636 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1637 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1638 } else {
1639 info->fGlyphWidths.reset(
1640 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
1641 glyphCount,
1642 glyphIDs,
1643 glyphIDsCount,
1644 &getWidthAdvance));
1645 }
1646 }
1647 return info;
1648}
1649
1650///////////////////////////////////////////////////////////////////////////////
1651
reed@google.comcc9aad52013-03-21 19:28:10 +00001652static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1653 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001654 AutoCFRelease<CFNumberRef> fontFormatRef(
1655 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1656 if (!fontFormatRef) {
1657 return 0;
1658 }
1659
1660 SInt32 fontFormatValue;
1661 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1662 return 0;
1663 }
1664
1665 switch (fontFormatValue) {
1666 case kCTFontFormatOpenTypePostScript:
1667 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1668 case kCTFontFormatOpenTypeTrueType:
1669 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1670 case kCTFontFormatTrueType:
1671 return SkSFNTHeader::fontType_MacTrueType::TAG;
1672 case kCTFontFormatPostScript:
1673 return SkSFNTHeader::fontType_PostScript::TAG;
1674 case kCTFontFormatBitmap:
1675 return SkSFNTHeader::fontType_MacTrueType::TAG;
1676 case kCTFontFormatUnrecognized:
1677 default:
1678 //CT seems to be unreliable in being able to obtain the type,
1679 //even if all we want is the first four bytes of the font resource.
1680 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1681 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1682 }
1683}
1684
reed@google.comcc9aad52013-03-21 19:28:10 +00001685SkStream* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
1686 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001687 if (0 == fontType) {
1688 return NULL;
1689 }
1690
1691 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001692 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001693 SkTDArray<SkFontTableTag> tableTags;
1694 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001695 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001696
1697 // calc total size for font, save sizes
1698 SkTDArray<size_t> tableSizes;
1699 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1700 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001701 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001702 totalSize += (tableSize + 3) & ~3;
1703 *tableSizes.append() = tableSize;
1704 }
1705
1706 // reserve memory for stream, and zero it (tables must be zero padded)
1707 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1708 char* dataStart = (char*)stream->getMemoryBase();
1709 sk_bzero(dataStart, totalSize);
1710 char* dataPtr = dataStart;
1711
1712 // compute font header entries
1713 uint16_t entrySelector = 0;
1714 uint16_t searchRange = 1;
1715 while (searchRange < numTables >> 1) {
1716 entrySelector++;
1717 searchRange <<= 1;
1718 }
1719 searchRange <<= 4;
1720 uint16_t rangeShift = (numTables << 4) - searchRange;
1721
1722 // write font header
1723 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1724 header->fontType = fontType;
1725 header->numTables = SkEndian_SwapBE16(numTables);
1726 header->searchRange = SkEndian_SwapBE16(searchRange);
1727 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1728 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1729 dataPtr += sizeof(SkSFNTHeader);
1730
1731 // write tables
1732 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1733 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1734 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1735 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001736 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001737 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1738 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1739 tableSize));
1740 entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
1741 entry->logicalLength = SkEndian_SwapBE32(tableSize);
1742
1743 dataPtr += (tableSize + 3) & ~3;
1744 ++entry;
1745 }
1746
1747 return stream;
1748}
1749
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001750///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001751///////////////////////////////////////////////////////////////////////////////
1752
1753int SkTypeface_Mac::onGetUPEM() const {
1754 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1755 return CGFontGetUnitsPerEm(cgFont);
1756}
1757
1758// If, as is the case with web fonts, the CTFont data isn't available,
1759// the CGFont data may work. While the CGFont may always provide the
1760// right result, leave the CTFont code path to minimize disruption.
1761static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1762 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1763 kCTFontTableOptionNoOptions);
1764 if (NULL == data) {
1765 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
1766 data = CGFontCopyTableForTag(cgFont, tag);
1767 }
1768 return data;
1769}
1770
1771int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1772 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1773 kCTFontTableOptionNoOptions));
1774 if (NULL == cfArray) {
1775 return 0;
1776 }
1777 int count = CFArrayGetCount(cfArray);
1778 if (tags) {
1779 for (int i = 0; i < count; ++i) {
1780 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1781 tags[i] = static_cast<SkFontTableTag>(fontTag);
1782 }
1783 }
1784 return count;
1785}
1786
1787size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1788 size_t length, void* dstData) const {
1789 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
1790 if (NULL == srcData) {
1791 return 0;
1792 }
1793
1794 size_t srcSize = CFDataGetLength(srcData);
1795 if (offset >= srcSize) {
1796 return 0;
1797 }
1798 if (length > srcSize - offset) {
1799 length = srcSize - offset;
1800 }
1801 if (dstData) {
1802 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1803 }
1804 return length;
1805}
1806
1807SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001808 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001809}
1810
1811void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001812 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1813 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1814 {
1815 rec->fMaskFormat = SkMask::kA8_Format;
1816 // Render the glyphs as close as possible to what was requested.
1817 // The above turns off subpixel rendering, but the user requested it.
1818 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1819 // See comments below for more details.
1820 rec->setHinting(SkPaint::kNormal_Hinting);
1821 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001822
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001823 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
1824 SkScalerContext::kAutohinting_Flag |
1825 SkScalerContext::kLCD_BGROrder_Flag |
1826 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001827
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001828 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001829
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001830 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001831
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001832 // Only two levels of hinting are supported.
1833 // kNo_Hinting means avoid CoreGraphics outline dilation.
1834 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1835 // If there is no lcd support, hinting (dilation) cannot be supported.
1836 SkPaint::Hinting hinting = rec->getHinting();
1837 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1838 hinting = SkPaint::kNo_Hinting;
1839 } else if (SkPaint::kFull_Hinting == hinting) {
1840 hinting = SkPaint::kNormal_Hinting;
1841 }
1842 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001843
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001844 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1845 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1846 // There is no current means to honor a request for unhinted lcd,
1847 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001848
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001849 // Hinting and smoothing should be orthogonal, but currently they are not.
1850 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
1851 // output is drawn from auto-dilated outlines (the amount of which is
1852 // determined by AppleFontSmoothing). Its regular anti-aliased output is
1853 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001854
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001855 // The behavior of Skia is as follows:
1856 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
1857 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
1858 // channel. This matches [LCD][yes-hint] in weight.
1859 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
1860 // Currenly side with LCD, effectively ignoring the hinting setting.
1861 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001862
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001863 if (isLCDFormat(rec->fMaskFormat)) {
1864 if (lcdSupport) {
1865 //CoreGraphics creates 555 masks for smoothed text anyway.
1866 rec->fMaskFormat = SkMask::kLCD16_Format;
1867 rec->setHinting(SkPaint::kNormal_Hinting);
1868 } else {
1869 rec->fMaskFormat = SkMask::kA8_Format;
1870 }
1871 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001872
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001873 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1874 // All other masks can use regular gamma.
1875 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
1876#ifndef SK_GAMMA_APPLY_TO_A8
1877 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00001878#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001879 } else {
1880 //CoreGraphics dialates smoothed text as needed.
1881 rec->setContrast(0);
1882 }
1883}
1884
1885// we take ownership of the ref
1886static const char* get_str(CFStringRef ref, SkString* str) {
1887 CFStringToSkString(ref, str);
1888 CFSafeRelease(ref);
1889 return str->c_str();
1890}
1891
reed@google.com5526ede2013-03-25 13:03:37 +00001892void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
1893 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001894 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001895
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001896 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
1897 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
1898 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
reed@google.com5526ede2013-03-25 13:03:37 +00001899 // TODO: need to add support for local-streams (here and openStream)
1900 *isLocalStream = false;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001901}
reed@google.com5526ede2013-03-25 13:03:37 +00001902
reed@google.com95625db2013-03-25 20:44:02 +00001903///////////////////////////////////////////////////////////////////////////////
1904///////////////////////////////////////////////////////////////////////////////
reed@google.com95625db2013-03-25 20:44:02 +00001905#if 1
reed@google.com83787c52013-03-26 17:19:15 +00001906
1907static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
1908 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
1909 if (NULL == ref.get()) {
1910 return false;
1911 }
1912 CFStringToSkString(ref, value);
1913 return true;
1914}
1915
1916static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
1917 CFNumberRef num;
1918 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
1919 && CFNumberIsFloatType(num)
1920 && CFNumberGetValue(num, kCFNumberFloatType, value);
1921}
1922
reed@google.com95625db2013-03-25 20:44:02 +00001923#include "SkFontMgr.h"
1924
reed@google.com83787c52013-03-26 17:19:15 +00001925static int unit_weight_to_fontstyle(float unit) {
1926 float value;
1927 if (unit < 0) {
1928 value = 100 + (1 + unit) * 300;
1929 } else {
1930 value = 400 + unit * 500;
1931 }
1932 return sk_float_round2int(value);
1933}
1934
1935static int unit_width_to_fontstyle(float unit) {
1936 float value;
1937 if (unit < 0) {
1938 value = 1 + (1 + unit) * 4;
1939 } else {
1940 value = 5 + unit * 4;
1941 }
1942 return sk_float_round2int(value);
1943}
1944
reed@google.com964988f2013-03-29 14:57:22 +00001945static inline int sqr(int value) {
1946 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
1947 return value * value;
1948}
1949
1950// We normalize each axis (weight, width, italic) to be base-900
1951static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
1952 return sqr(a.weight() - b.weight()) +
1953 sqr((a.width() - b.width()) * 100) +
1954 sqr((a.isItalic() != b.isItalic()) * 900);
1955}
1956
reed@google.com83787c52013-03-26 17:19:15 +00001957static SkFontStyle desc2fontstyle(CTFontDescriptorRef desc) {
1958 AutoCFRelease<CFDictionaryRef> dict(
1959 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc,
1960 kCTFontTraitsAttribute));
1961 if (NULL == dict.get()) {
1962 return SkFontStyle();
1963 }
1964
1965 float weight, width, slant;
1966 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
1967 weight = 0;
1968 }
1969 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
1970 width = 0;
1971 }
1972 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
1973 slant = 0;
1974 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00001975
reed@google.com83787c52013-03-26 17:19:15 +00001976 return SkFontStyle(unit_weight_to_fontstyle(weight),
1977 unit_width_to_fontstyle(width),
1978 slant ? SkFontStyle::kItalic_Slant
1979 : SkFontStyle::kUpright_Slant);
1980}
1981
reed@google.comdea7ee02013-03-28 14:12:10 +00001982struct NameFontStyleRec {
1983 SkString fFamilyName;
1984 SkFontStyle fFontStyle;
1985};
1986
1987static bool nameFontStyleProc(SkTypeface* face, SkTypeface::Style,
1988 void* ctx) {
1989 SkTypeface_Mac* macFace = (SkTypeface_Mac*)face;
1990 const NameFontStyleRec* rec = (const NameFontStyleRec*)ctx;
1991
1992 return macFace->fFontStyle == rec->fFontStyle &&
1993 macFace->fName == rec->fFamilyName;
1994}
1995
reed@google.comce8b3de2013-03-26 19:30:16 +00001996static SkTypeface* createFromDesc(CFStringRef cfFamilyName,
1997 CTFontDescriptorRef desc) {
reed@google.comdea7ee02013-03-28 14:12:10 +00001998 NameFontStyleRec rec;
1999 CFStringToSkString(cfFamilyName, &rec.fFamilyName);
2000 rec.fFontStyle = desc2fontstyle(desc);
2001
2002 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(nameFontStyleProc,
2003 &rec);
2004 if (face) {
2005 return face;
2006 }
2007
reed@google.comce8b3de2013-03-26 19:30:16 +00002008 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFamilyName, 1, NULL));
2009 CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc);
2010 if (NULL == ctFont) {
2011 return NULL;
2012 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002013
reed@google.comce8b3de2013-03-26 19:30:16 +00002014 SkString str;
2015 CFStringToSkString(cfFamilyName, &str);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002016
reed@google.comce8b3de2013-03-26 19:30:16 +00002017 bool isFixedPitch;
2018 (void)computeStyleBits(ctFont, &isFixedPitch);
2019 SkFontID fontID = CTFontRef_to_SkFontID(ctFont);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002020
reed@google.comdea7ee02013-03-28 14:12:10 +00002021 face = SkNEW_ARGS(SkTypeface_Mac, (rec.fFontStyle, fontID, isFixedPitch,
2022 ctFont, str.c_str()));
2023 SkTypefaceCache::Add(face, face->style());
2024 return face;
reed@google.comce8b3de2013-03-26 19:30:16 +00002025}
2026
reed@google.com83787c52013-03-26 17:19:15 +00002027class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002028public:
reed@google.com83787c52013-03-26 17:19:15 +00002029 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
2030 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, NULL))
reed@google.comdea7ee02013-03-28 14:12:10 +00002031 , fFamilyName(familyName)
2032 , fCount(0) {
reed@google.com83787c52013-03-26 17:19:15 +00002033 CFRetain(familyName);
reed@google.comdea7ee02013-03-28 14:12:10 +00002034 if (NULL == fArray) {
2035 fArray = CFArrayCreate(NULL, NULL, 0, NULL);
2036 }
2037 fCount = CFArrayGetCount(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002038 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002039
reed@google.com83787c52013-03-26 17:19:15 +00002040 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002041 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002042 CFRelease(fFamilyName);
2043 }
2044
2045 virtual int count() SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002046 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002047 }
2048
2049 virtual void getStyle(int index, SkFontStyle* style,
2050 SkString* name) SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002051 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002052 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2053 if (style) {
2054 *style = desc2fontstyle(desc);
2055 }
2056 if (name) {
2057 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2058 name->reset();
2059 }
2060 }
2061 }
2062
2063 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
2064 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2065 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002066
reed@google.com83787c52013-03-26 17:19:15 +00002067 return createFromDesc(fFamilyName, desc);
2068 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002069
reed@google.com964988f2013-03-29 14:57:22 +00002070 virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
2071 if (0 == fCount) {
2072 return NULL;
2073 }
2074 return createFromDesc(fFamilyName, findMatchingDesc(pattern));
2075 }
2076
reed@google.com83787c52013-03-26 17:19:15 +00002077private:
2078 CFArrayRef fArray;
2079 CFStringRef fFamilyName;
reed@google.comdea7ee02013-03-28 14:12:10 +00002080 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002081
2082 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2083 int bestMetric = SK_MaxS32;
2084 CTFontDescriptorRef bestDesc = NULL;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002085
reed@google.com964988f2013-03-29 14:57:22 +00002086 for (int i = 0; i < fCount; ++i) {
2087 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
2088 int metric = compute_metric(pattern, desc2fontstyle(desc));
2089 if (0 == metric) {
2090 return desc;
2091 }
2092 if (metric < bestMetric) {
2093 bestMetric = metric;
2094 bestDesc = desc;
2095 }
2096 }
2097 SkASSERT(bestDesc);
2098 return bestDesc;
2099 }
reed@google.com83787c52013-03-26 17:19:15 +00002100};
2101
2102class SkFontMgr_Mac : public SkFontMgr {
2103 int fCount;
2104 CFArrayRef fNames;
2105
2106 CFStringRef stringAt(int index) const {
2107 SkASSERT((unsigned)index < (unsigned)fCount);
2108 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2109 }
2110
2111 void lazyInit() {
2112 if (NULL == fNames) {
reed@google.com3dcbd462013-03-27 13:56:34 +00002113 fNames = SkCTFontManagerCopyAvailableFontFamilyNames();
reed@google.com83787c52013-03-26 17:19:15 +00002114 fCount = fNames ? CFArrayGetCount(fNames) : 0;
2115 }
2116 }
2117
reed@google.com964988f2013-03-29 14:57:22 +00002118 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2119 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2120 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2121 &kCFTypeDictionaryKeyCallBacks,
2122 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002123
reed@google.com964988f2013-03-29 14:57:22 +00002124 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002125
reed@google.com964988f2013-03-29 14:57:22 +00002126 AutoCFRelease<CTFontDescriptorRef> desc(
2127 CTFontDescriptorCreateWithAttributes(cfAttr));
2128 return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc));
2129 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002130
reed@google.com83787c52013-03-26 17:19:15 +00002131public:
2132 SkFontMgr_Mac() : fCount(0), fNames(NULL) {}
2133
2134 virtual ~SkFontMgr_Mac() {
2135 CFSafeRelease(fNames);
2136 }
reed@google.com95625db2013-03-25 20:44:02 +00002137
2138protected:
2139 virtual int onCountFamilies() SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002140 this->lazyInit();
2141 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002142 }
2143
2144 virtual void onGetFamilyName(int index, SkString* familyName) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002145 this->lazyInit();
2146 if ((unsigned)index < (unsigned)fCount) {
2147 CFStringToSkString(this->stringAt(index), familyName);
2148 } else {
2149 familyName->reset();
2150 }
reed@google.com95625db2013-03-25 20:44:02 +00002151 }
2152
2153 virtual SkFontStyleSet* onCreateStyleSet(int index) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002154 this->lazyInit();
2155 if ((unsigned)index >= (unsigned)fCount) {
2156 return NULL;
2157 }
reed@google.com964988f2013-03-29 14:57:22 +00002158 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002159 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002160
reed@google.com964988f2013-03-29 14:57:22 +00002161 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) SK_OVERRIDE {
2162 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2163 return CreateSet(cfName);
2164 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002165
reed@google.com95625db2013-03-25 20:44:02 +00002166 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
2167 const SkFontStyle&) SK_OVERRIDE {
2168 return NULL;
2169 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002170
reed@google.com95625db2013-03-25 20:44:02 +00002171 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2172 const SkFontStyle&) SK_OVERRIDE {
2173 return NULL;
2174 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002175
reed@google.com95625db2013-03-25 20:44:02 +00002176 virtual SkTypeface* onCreateFromData(SkData* data,
2177 int ttcIndex) SK_OVERRIDE {
2178 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
2179 if (NULL == pr) {
2180 return NULL;
2181 }
2182 return create_from_dataProvider(pr);
2183 }
2184
2185 virtual SkTypeface* onCreateFromStream(SkStream* stream,
2186 int ttcIndex) SK_OVERRIDE {
2187 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
2188 if (NULL == pr) {
2189 return NULL;
2190 }
2191 return create_from_dataProvider(pr);
2192 }
2193
2194 virtual SkTypeface* onCreateFromFile(const char path[],
2195 int ttcIndex) SK_OVERRIDE {
2196 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2197 if (NULL == pr) {
2198 return NULL;
2199 }
2200 return create_from_dataProvider(pr);
2201 }
2202};
2203
2204SkFontMgr* SkFontMgr::Factory() {
2205 return SkNEW(SkFontMgr_Mac);
2206}
2207#endif