blob: ef51aef018a9b650aed4f27cee2c7e074c6913ed [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"
reed@google.combcb42ae2013-07-02 13:56:39 +000045#include "SkUtils.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000046
reed@google.comf77b35d2013-05-02 20:39:44 +000047//#define HACK_COLORGLYPHS
48
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000049class SkScalerContext_Mac;
50
reed@google.com3dcbd462013-03-27 13:56:34 +000051// CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
52// provide a wrapper here that will return an empty array if need be.
53static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() {
54#ifdef SK_BUILD_FOR_IOS
55 return CFArrayCreate(NULL, NULL, 0, NULL);
56#else
57 return CTFontManagerCopyAvailableFontFamilyNames();
58#endif
59}
60
61
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000062// Being templated and taking const T* prevents calling
63// CFSafeRelease(autoCFRelease) through implicit conversion.
64template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
65 if (cfTypeRef) {
66 CFRelease(cfTypeRef);
67 }
68}
69
70// Being templated and taking const T* prevents calling
71// CFSafeRetain(autoCFRelease) through implicit conversion.
72template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
73 if (cfTypeRef) {
74 CFRetain(cfTypeRef);
75 }
76}
77
78/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
79template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
80public:
81 explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
82 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
83
84 void reset(CFRef that = NULL) {
85 CFSafeRetain(that);
86 CFSafeRelease(fCFRef);
87 fCFRef = that;
88 }
89
90 AutoCFRelease& operator =(CFRef that) {
91 reset(that);
92 return *this;
93 }
94
95 operator CFRef() const { return fCFRef; }
96 CFRef get() const { return fCFRef; }
97
98private:
99 CFRef fCFRef;
100};
101
reed@google.com964988f2013-03-29 14:57:22 +0000102static CFStringRef make_CFString(const char str[]) {
103 return CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
104}
105
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000106template<typename T> class AutoCGTable : SkNoncopyable {
107public:
108 AutoCGTable(CGFontRef font)
109 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
110 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
111 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
112 { }
113
114 const T* operator->() const { return fData; }
115
116private:
117 AutoCFRelease<CFDataRef> fCFData;
118public:
119 const T* fData;
120};
121
122// inline versions of these rect helpers
123
124static bool CGRectIsEmpty_inline(const CGRect& rect) {
125 return rect.size.width <= 0 || rect.size.height <= 0;
126}
127
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000128static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
129 return rect.origin.x;
130}
131
132static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
133 return rect.origin.x + rect.size.width;
134}
135
136static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
137 return rect.origin.y;
138}
139
140static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
141 return rect.origin.y + rect.size.height;
142}
143
144static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
145 return rect.size.width;
146}
147
148///////////////////////////////////////////////////////////////////////////////
149
150static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
151 size_t width, size_t height, size_t rowBytes) {
152 SkASSERT(width);
153 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
154
155 if (width >= 32) {
156 while (height) {
157 sk_memset32(ptr, value, width);
158 ptr = (uint32_t*)((char*)ptr + rowBytes);
159 height -= 1;
160 }
161 return;
162 }
163
164 rowBytes -= width * sizeof(uint32_t);
165
166 if (width >= 8) {
167 while (height) {
168 int w = width;
169 do {
170 *ptr++ = value; *ptr++ = value;
171 *ptr++ = value; *ptr++ = value;
172 *ptr++ = value; *ptr++ = value;
173 *ptr++ = value; *ptr++ = value;
174 w -= 8;
175 } while (w >= 8);
176 while (--w >= 0) {
177 *ptr++ = value;
178 }
179 ptr = (uint32_t*)((char*)ptr + rowBytes);
180 height -= 1;
181 }
182 } else {
183 while (height) {
184 int w = width;
185 do {
186 *ptr++ = value;
187 } while (--w > 0);
188 ptr = (uint32_t*)((char*)ptr + rowBytes);
189 height -= 1;
190 }
191 }
192}
193
194#include <sys/utsname.h>
195
196typedef uint32_t CGRGBPixel;
197
198static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
199 return pixel & 0xFF;
200}
201
202// The calls to support subpixel are present in 10.5, but are not included in
203// the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are
204// included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for
205// instance, is present in the 10.5 CoreGraphics libary, use:
206// cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/
207// cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/
208// nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts
209
210#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
211CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value);
212CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value);
213CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value);
214CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value);
215CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value);
216#endif
217
218static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
219
220// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
221static int readVersion() {
222 struct utsname info;
223 if (uname(&info) != 0) {
224 SkDebugf("uname failed\n");
225 return 0;
226 }
227 if (strcmp(info.sysname, "Darwin") != 0) {
228 SkDebugf("unexpected uname sysname %s\n", info.sysname);
229 return 0;
230 }
231 char* dot = strchr(info.release, '.');
232 if (!dot) {
233 SkDebugf("expected dot in uname release %s\n", info.release);
234 return 0;
235 }
236 int version = atoi(info.release);
237 if (version == 0) {
238 SkDebugf("could not parse uname release %s\n", info.release);
239 }
240 return version;
241}
242
243static int darwinVersion() {
244 static int darwin_version = readVersion();
245 return darwin_version;
246}
247
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000248static bool isSnowLeopard() {
249 return darwinVersion() == 10;
250}
251
252static bool isLion() {
253 return darwinVersion() == 11;
254}
255
256static bool isMountainLion() {
257 return darwinVersion() == 12;
258}
259
260static bool isLCDFormat(unsigned format) {
261 return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
262}
263
264static CGFloat ScalarToCG(SkScalar scalar) {
265 if (sizeof(CGFloat) == sizeof(float)) {
266 return SkScalarToFloat(scalar);
267 } else {
268 SkASSERT(sizeof(CGFloat) == sizeof(double));
269 return (CGFloat) SkScalarToDouble(scalar);
270 }
271}
272
273static SkScalar CGToScalar(CGFloat cgFloat) {
274 if (sizeof(CGFloat) == sizeof(float)) {
275 return SkFloatToScalar(cgFloat);
276 } else {
277 SkASSERT(sizeof(CGFloat) == sizeof(double));
278 return SkDoubleToScalar(cgFloat);
279 }
280}
281
282static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
283 SkScalar sx = SK_Scalar1,
284 SkScalar sy = SK_Scalar1) {
285 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
286 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
287 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
288 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
289 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
290 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
291}
292
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000293///////////////////////////////////////////////////////////////////////////////
294
295#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
296#define BITMAP_INFO_GRAY (kCGImageAlphaNone)
297
298/**
299 * There does not appear to be a publicly accessable API for determining if lcd
300 * font smoothing will be applied if we request it. The main issue is that if
301 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
302 */
303static bool supports_LCD() {
304 static int gSupportsLCD = -1;
305 if (gSupportsLCD >= 0) {
306 return (bool) gSupportsLCD;
307 }
308 uint32_t rgb = 0;
309 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
310 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
311 colorspace, BITMAP_INFO_RGB));
312 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
313 CGContextSetShouldSmoothFonts(cgContext, true);
314 CGContextSetShouldAntialias(cgContext, true);
315 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
316 CGContextSetGrayFillColor(cgContext, 1, 1);
317 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
318 uint32_t r = (rgb >> 16) & 0xFF;
319 uint32_t g = (rgb >> 8) & 0xFF;
320 uint32_t b = (rgb >> 0) & 0xFF;
321 gSupportsLCD = (r != g || r != b);
322 return (bool) gSupportsLCD;
323}
324
325class Offscreen {
326public:
327 Offscreen();
328
329 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
330 CGGlyph glyphID, size_t* rowBytesPtr,
331 bool generateA8FromLCD);
332
333private:
334 enum {
335 kSize = 32 * 32 * sizeof(CGRGBPixel)
336 };
337 SkAutoSMalloc<kSize> fImageStorage;
338 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
339
340 // cached state
341 AutoCFRelease<CGContextRef> fCG;
342 SkISize fSize;
343 bool fDoAA;
344 bool fDoLCD;
345
346 static int RoundSize(int dimension) {
347 return SkNextPow2(dimension);
348 }
349};
350
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +0000351Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL),
robertphillips@google.com87379e12013-03-29 12:11:10 +0000352 fDoAA(false), fDoLCD(false) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000353 fSize.set(0, 0);
354}
355
356///////////////////////////////////////////////////////////////////////////////
357
bungeman@google.comfe747652013-03-25 19:36:11 +0000358static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000359 unsigned style = SkTypeface::kNormal;
360 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
361
362 if (traits & kCTFontBoldTrait) {
363 style |= SkTypeface::kBold;
364 }
365 if (traits & kCTFontItalicTrait) {
366 style |= SkTypeface::kItalic;
367 }
bungeman@google.comfe747652013-03-25 19:36:11 +0000368 if (isFixedPitch) {
369 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000370 }
371 return (SkTypeface::Style)style;
372}
373
374static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
375 SkFontID id = 0;
376// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
377// bracket this to be Mac only.
378#ifdef SK_BUILD_FOR_MAC
379 ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
380 id = (SkFontID)ats;
381 if (id != 0) {
382 id &= 0x3FFFFFFF; // make top two bits 00
383 return id;
384 }
385#endif
386 // CTFontGetPlatformFont returns NULL if the font is local
387 // (e.g., was created by a CSS3 @font-face rule).
388 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
389 AutoCGTable<SkOTTableHead> headTable(cgFont);
390 if (headTable.fData) {
391 id = (SkFontID) headTable->checksumAdjustment;
392 id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
393 }
394 // well-formed fonts have checksums, but as a last resort, use the pointer.
395 if (id == 0) {
396 id = (SkFontID) (uintptr_t) fontRef;
397 id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
398 }
399 return id;
400}
401
reed@google.comce8b3de2013-03-26 19:30:16 +0000402static SkFontStyle stylebits2fontstyle(SkTypeface::Style styleBits) {
403 return SkFontStyle((styleBits & SkTypeface::kBold)
404 ? SkFontStyle::kBold_Weight
405 : SkFontStyle::kNormal_Weight,
406 SkFontStyle::kNormal_Width,
407 (styleBits & SkTypeface::kItalic)
408 ? SkFontStyle::kItalic_Slant
409 : SkFontStyle::kUpright_Slant);
410}
411
412#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
413
414static SkTypeface::Style fontstyle2stylebits(const SkFontStyle& fs) {
415 unsigned style = 0;
416 if (fs.width() >= WEIGHT_THRESHOLD) {
417 style |= SkTypeface::kBold;
418 }
419 if (fs.isItalic()) {
420 style |= SkTypeface::kItalic;
421 }
422 return (SkTypeface::Style)style;
423}
424
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000425class SkTypeface_Mac : public SkTypeface {
426public:
bungeman@google.comfe747652013-03-25 19:36:11 +0000427 SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isFixedPitch,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000428 CTFontRef fontRef, const char name[])
reed@google.comce8b3de2013-03-26 19:30:16 +0000429 : SkTypeface(style, fontID, isFixedPitch)
430 , fName(name)
431 , fFontRef(fontRef) // caller has already called CFRetain for us
432 , fFontStyle(stylebits2fontstyle(style))
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000433 {
434 SkASSERT(fontRef);
435 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000436
reed@google.comce8b3de2013-03-26 19:30:16 +0000437 SkTypeface_Mac(const SkFontStyle& fs, SkFontID fontID, bool isFixedPitch,
438 CTFontRef fontRef, const char name[])
439 : SkTypeface(fontstyle2stylebits(fs), fontID, isFixedPitch)
440 , fName(name)
441 , fFontRef(fontRef) // caller has already called CFRetain for us
442 , fFontStyle(fs)
443 {
444 SkASSERT(fontRef);
445 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000446
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000447 SkString fName;
448 AutoCFRelease<CTFontRef> fFontRef;
reed@google.comce8b3de2013-03-26 19:30:16 +0000449 SkFontStyle fFontStyle;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000450
451protected:
452 friend class SkFontHost; // to access our protected members for deprecated methods
453
454 virtual int onGetUPEM() const SK_OVERRIDE;
reed@google.comcc9aad52013-03-21 19:28:10 +0000455 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000456 virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
457 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
458 size_t length, void* data) const SK_OVERRIDE;
459 virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
460 virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
reed@google.com5526ede2013-03-25 13:03:37 +0000461 virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
reed@google.com2689f612013-03-20 20:01:47 +0000462 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
463 SkAdvancedTypefaceMetrics::PerGlyphInfo,
464 const uint32_t*, uint32_t) const SK_OVERRIDE;
reed@google.combcb42ae2013-07-02 13:56:39 +0000465 virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
466 int glyphCount) const SK_OVERRIDE;
467 virtual int onCountGlyphs() const SK_OVERRIDE;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000468
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000469private:
reed@google.comce8b3de2013-03-26 19:30:16 +0000470
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000471 typedef SkTypeface INHERITED;
472};
473
474static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
475 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000476 bool isFixedPitch;
477 SkTypeface::Style style = computeStyleBits(fontRef, &isFixedPitch);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000478 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
479
bungeman@google.comfe747652013-03-25 19:36:11 +0000480 return new SkTypeface_Mac(style, fontID, isFixedPitch, fontRef, name);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000481}
482
483static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
484 CTFontRef ctFont = NULL;
485
486 CTFontSymbolicTraits ctFontTraits = 0;
487 if (theStyle & SkTypeface::kBold) {
488 ctFontTraits |= kCTFontBoldTrait;
489 }
490 if (theStyle & SkTypeface::kItalic) {
491 ctFontTraits |= kCTFontItalicTrait;
492 }
493
494 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000495 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000496
497 AutoCFRelease<CFNumberRef> cfFontTraits(
498 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
499
500 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
501 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
502 &kCFTypeDictionaryKeyCallBacks,
503 &kCFTypeDictionaryValueCallBacks));
504
505 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
506 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
507 &kCFTypeDictionaryKeyCallBacks,
508 &kCFTypeDictionaryValueCallBacks));
509
510 // Create the font
511 if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
512 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
513
514 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
515 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
516
517 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
518 CTFontDescriptorCreateWithAttributes(cfAttributes));
519
520 if (ctFontDesc != NULL) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000521 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000522 }
523 }
524
525 return ctFont ? NewFromFontRef(ctFont, familyName) : NULL;
526}
527
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000528static SkTypeface* GetDefaultFace() {
529 SK_DECLARE_STATIC_MUTEX(gMutex);
530 SkAutoMutexAcquire ma(gMutex);
531
532 static SkTypeface* gDefaultFace;
533
534 if (NULL == gDefaultFace) {
535 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal);
536 SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal);
537 }
538 return gDefaultFace;
539}
540
541///////////////////////////////////////////////////////////////////////////////
542
543extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
544CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
545 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
546 return macface ? macface->fFontRef.get() : NULL;
547}
548
549/* This function is visible on the outside. It first searches the cache, and if
550 * not found, returns a new entry (after adding it to the cache).
551 */
552SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
553 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
554 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
555 if (face) {
556 face->ref();
557 } else {
558 face = NewFromFontRef(fontRef, NULL);
559 SkTypefaceCache::Add(face, face->style());
560 // NewFromFontRef doesn't retain the parameter, but the typeface it
561 // creates does release it in its destructor, so we balance that with
562 // a retain call here.
563 CFRetain(fontRef);
564 }
565 SkASSERT(face->getRefCnt() > 1);
566 return face;
567}
568
569struct NameStyleRec {
570 const char* fName;
571 SkTypeface::Style fStyle;
572};
573
574static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style,
575 void* ctx) {
576 const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face);
577 const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx);
578
579 return rec->fStyle == style && mface->fName.equals(rec->fName);
580}
581
582static const char* map_css_names(const char* name) {
583 static const struct {
584 const char* fFrom; // name the caller specified
585 const char* fTo; // "canonical" name we map to
586 } gPairs[] = {
587 { "sans-serif", "Helvetica" },
588 { "serif", "Times" },
589 { "monospace", "Courier" }
590 };
591
592 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
593 if (strcmp(name, gPairs[i].fFrom) == 0) {
594 return gPairs[i].fTo;
595 }
596 }
597 return name; // no change
598}
599
600SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
601 const char familyName[],
602 SkTypeface::Style style) {
603 if (familyName) {
604 familyName = map_css_names(familyName);
605 }
606
607 // Clone an existing typeface
608 // TODO: only clone if style matches the familyFace's style...
609 if (familyName == NULL && familyFace != NULL) {
610 familyFace->ref();
611 return const_cast<SkTypeface*>(familyFace);
612 }
613
614 if (!familyName || !*familyName) {
615 familyName = FONT_DEFAULT_NAME;
616 }
617
618 NameStyleRec rec = { familyName, style };
619 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec);
620
621 if (NULL == face) {
622 face = NewFromName(familyName, style);
623 if (face) {
624 SkTypefaceCache::Add(face, style);
625 } else {
626 face = GetDefaultFace();
627 face->ref();
628 }
629 }
630 return face;
631}
632
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000633///////////////////////////////////////////////////////////////////////////////
634
bungeman@google.comcefd9812013-05-15 15:07:32 +0000635/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000636struct GlyphRect {
637 int16_t fMinX;
638 int16_t fMinY;
639 int16_t fMaxX;
640 int16_t fMaxY;
641};
642
643class SkScalerContext_Mac : public SkScalerContext {
644public:
reed@google.com0da48612013-03-19 16:06:52 +0000645 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000646
647protected:
648 unsigned generateGlyphCount(void) SK_OVERRIDE;
649 uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
650 void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
651 void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
652 void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
653 void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
654 void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
655
656private:
657 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000658
bungeman@google.comcefd9812013-05-15 15:07:32 +0000659 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
660 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000661
662 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
663 *
664 * For use with (and must be called before) generateBBoxes.
665 */
666 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000667
bungeman@google.comcefd9812013-05-15 15:07:32 +0000668 /** Initializes fFBoundingBoxes and returns true on success.
669 *
670 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
671 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
672 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
673 * font directly.
674 *
675 * This routine initializes fFBoundingBoxes to an array of
676 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
677 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
678 *
679 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
680 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
681 *
682 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
683 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000684 bool generateBBoxes();
685
bungeman@google.comcefd9812013-05-15 15:07:32 +0000686 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
687 *
688 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
689 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
690 */
691 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000692
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000693 Offscreen fOffscreen;
694 AutoCFRelease<CTFontRef> fCTFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000695
bungeman@google.comcefd9812013-05-15 15:07:32 +0000696 /** Vertical variant of fCTFont.
697 *
698 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
699 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
700 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
701 * Use fCTVerticalFont with kCTFontVerticalOrientation to get metrics in the same space.
702 */
703 AutoCFRelease<CTFontRef> fCTVerticalFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000704
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000705 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000706 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000707 uint16_t fFBoundingBoxesGlyphOffset;
708 uint16_t fGlyphCount;
709 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000710 const bool fDoSubPosition;
711 const bool fVertical;
712
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000713 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000714
715 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000716};
717
reed@google.com0da48612013-03-19 16:06:52 +0000718SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
719 const SkDescriptor* desc)
720 : INHERITED(typeface, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000721 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000722 , fFBoundingBoxesGlyphOffset(0)
723 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000724 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
725 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
726
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000727{
reed@google.com2689f612013-03-20 20:01:47 +0000728 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000729 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000730 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
731 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000732
bungeman@google.comcefd9812013-05-15 15:07:32 +0000733 fRec.getSingleMatrix(&fFUnitMatrix);
734 CGAffineTransform transform = MatrixToCGAffineTransform(fFUnitMatrix);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000735
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000736 AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
737 if (fVertical) {
738 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
739 kCFAllocatorDefault, 0,
740 &kCFTypeDictionaryKeyCallBacks,
741 &kCFTypeDictionaryValueCallBacks));
742 if (cfAttributes) {
743 CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
744 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
745 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
746 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
747 ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
748 }
749 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000750 // Since our matrix includes everything, we pass 1 for size.
751 fCTFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, ctFontDesc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000752 fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL);
753 if (fVertical) {
754 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
755 transform = CGAffineTransformConcat(rotateLeft, transform);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000756 fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000757 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000758
bungeman@google.comcefd9812013-05-15 15:07:32 +0000759 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
760 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000761}
762
763CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
764 CGGlyph glyphID, size_t* rowBytesPtr,
765 bool generateA8FromLCD) {
766 if (!fRGBSpace) {
767 //It doesn't appear to matter what color space is specified.
768 //Regular blends and antialiased text are always (s*a + d*(1-a))
769 //and smoothed text is always g=2.0.
770 fRGBSpace = CGColorSpaceCreateDeviceRGB();
771 }
772
773 // default to kBW_Format
774 bool doAA = false;
775 bool doLCD = false;
776
777 if (SkMask::kBW_Format != glyph.fMaskFormat) {
778 doLCD = true;
779 doAA = true;
780 }
781
782 // FIXME: lcd smoothed un-hinted rasterization unsupported.
783 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
784 doLCD = false;
785 doAA = true;
786 }
787
788 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
789 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
790 if (fSize.fWidth < glyph.fWidth) {
791 fSize.fWidth = RoundSize(glyph.fWidth);
792 }
793 if (fSize.fHeight < glyph.fHeight) {
794 fSize.fHeight = RoundSize(glyph.fHeight);
795 }
796
797 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
798 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
799 fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
800 rowBytes, fRGBSpace, BITMAP_INFO_RGB);
801
802 // skia handles quantization itself, so we disable this for cg to get
803 // full fractional data from them.
804 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
805 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
806
807 CGContextSetTextDrawingMode(fCG, kCGTextFill);
808 CGContextSetFont(fCG, context.fCGFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000809 CGContextSetFontSize(fCG, 1 /*CTFontGetSize(context.fCTFont)*/);
810 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000811
bungeman@google.comcefd9812013-05-15 15:07:32 +0000812 // Because CG always draws from the horizontal baseline,
813 // if there is a non-integral translation from the horizontal origin to the vertical origin,
814 // then CG cannot draw the glyph in the correct location without subpixel positioning.
815 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical);
816 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || context.fVertical);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000817
818 // Draw white on black to create mask.
819 // TODO: Draw black on white and invert, CG has a special case codepath.
820 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
821
822 // force our checks below to happen
823 fDoAA = !doAA;
824 fDoLCD = !doLCD;
825 }
826
827 if (fDoAA != doAA) {
828 CGContextSetShouldAntialias(fCG, doAA);
829 fDoAA = doAA;
830 }
831 if (fDoLCD != doLCD) {
832 CGContextSetShouldSmoothFonts(fCG, doLCD);
833 fDoLCD = doLCD;
834 }
835
836 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
837 // skip rows based on the glyph's height
838 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
839
840 // erase to black
841 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
842
843 float subX = 0;
844 float subY = 0;
845 if (context.fDoSubPosition) {
846 subX = SkFixedToFloat(glyph.getSubXFixed());
847 subY = SkFixedToFloat(glyph.getSubYFixed());
848 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000849
bungeman@google.comcefd9812013-05-15 15:07:32 +0000850 // CGContextShowGlyphsAtPoint always draws using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000851 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000852 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000853 context.getVerticalOffset(glyphID, &offset);
854 subX += offset.fX;
855 subY += offset.fY;
856 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000857
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000858 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
859 glyph.fTop + glyph.fHeight - subY,
860 &glyphID, 1);
861
862 SkASSERT(rowBytesPtr);
863 *rowBytesPtr = rowBytes;
864 return image;
865}
866
bungeman@google.comcefd9812013-05-15 15:07:32 +0000867void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
868 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
869 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
870 CGSize cgVertOffset;
871 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
872
873 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
874 if (isSnowLeopard()) {
875 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
876 fFUnitMatrix.mapPoints(&skVertOffset, 1);
877 } else {
878 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
879 skVertOffset.fY = -skVertOffset.fY;
880 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000881
bungeman@google.comcefd9812013-05-15 15:07:32 +0000882 *offset = skVertOffset;
883}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000884
885uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
886 if (fFBoundingBoxesGlyphOffset) {
887 return fFBoundingBoxesGlyphOffset;
888 }
889 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
890 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
891 if (hheaTable.fData) {
892 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
893 }
894 return fFBoundingBoxesGlyphOffset;
895}
reed@android.com0680d6c2008-12-19 19:46:15 +0000896
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000897bool SkScalerContext_Mac::generateBBoxes() {
898 if (fGeneratedFBoundingBoxes) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000899 return NULL != fFBoundingBoxes.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000900 }
901 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000902
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000903 AutoCGTable<SkOTTableHead> headTable(fCGFont);
904 if (!headTable.fData) {
905 return false;
906 }
907
908 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
909 if (!locaTable.fData) {
910 return false;
911 }
912
913 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
914 if (!glyfTable.fData) {
915 return false;
916 }
917
918 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000919 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000920
921 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
922 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
923 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
924 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
925 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
926 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
927 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
928 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
929 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
930 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
931 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000932
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000933 return true;
934}
935
936unsigned SkScalerContext_Mac::generateGlyphCount(void) {
937 return fGlyphCount;
938}
939
940uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
941 CGGlyph cgGlyph;
942 UniChar theChar;
943
944 // Validate our parameters and state
945 SkASSERT(uni <= 0x0000FFFF);
946 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
947
948 // Get the glyph
949 theChar = (UniChar) uni;
950
951 if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1)) {
952 cgGlyph = 0;
953 }
954
955 return cgGlyph;
956}
957
958void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
959 this->generateMetrics(glyph);
960}
961
bungeman@google.comcefd9812013-05-15 15:07:32 +0000962void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
963 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
964 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000965
bungeman@google.comcefd9812013-05-15 15:07:32 +0000966 // The following block produces cgAdvance in CG units (pixels, y up).
967 CGSize cgAdvance;
968 if (fVertical) {
969 CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
970 &cgGlyph, &cgAdvance, 1);
971 } else {
972 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
973 &cgGlyph, &cgAdvance, 1);
974 }
975 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
976 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000977
bungeman@google.comcefd9812013-05-15 15:07:32 +0000978 // The following produces skBounds in SkGlyph units (pixels, y down),
979 // or returns early if skBounds would be empty.
980 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000981
bungeman@google.comcefd9812013-05-15 15:07:32 +0000982 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
983 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
984 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
985 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
986 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
987 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
988 // to center the glyph along the vertical baseline and also perform some mysterious shift
989 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
990 // these steps.
991 //
992 // It is not known which is correct (or if either is correct). However, we must always draw
993 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
994 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000995
bungeman@google.comcefd9812013-05-15 15:07:32 +0000996 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
997 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000998
bungeman@google.comcefd9812013-05-15 15:07:32 +0000999 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
1000 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
1001 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
1002 // font directly.
1003 if ((isLion() || isMountainLion()) &&
1004 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1005 {
1006 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1007 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1008 return;
1009 }
1010 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1011 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1012 fFUnitMatrix.mapRect(&skBounds);
1013
1014 } else {
1015 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1016 CGRect cgBounds;
1017 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1018 &cgGlyph, &cgBounds, 1);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001019
bungeman@google.comcefd9812013-05-15 15:07:32 +00001020 // BUG?
1021 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1022 // it should be empty. So, if we see a zero-advance, we check if it has an
1023 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1024 // is rare, so we won't incur a big performance cost for this extra check.
1025 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
1026 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
1027 if (NULL == path || CGPathIsEmpty(path)) {
1028 return;
1029 }
1030 }
1031
1032 if (CGRectIsEmpty_inline(cgBounds)) {
1033 return;
1034 }
1035
1036 // Convert cgBounds to SkGlyph units (pixels, y down).
1037 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1038 cgBounds.size.width, cgBounds.size.height);
1039 }
1040
1041 if (fVertical) {
1042 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1043 // Convert these horizontal bounds into vertical bounds.
1044 SkPoint offset;
1045 getVerticalOffset(cgGlyph, &offset);
1046 skBounds.offset(offset);
1047 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001048
bungeman@google.comcefd9812013-05-15 15:07:32 +00001049 // Currently the bounds are based on being rendered at (0,0).
1050 // The top left must not move, since that is the base from which subpixel positioning is offset.
1051 if (fDoSubPosition) {
1052 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1053 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1054 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001055
bungeman@google.comcefd9812013-05-15 15:07:32 +00001056 SkIRect skIBounds;
1057 skBounds.roundOut(&skIBounds);
1058 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1059 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1060 // is not currently known, as CG dilates the outlines by some percentage.
1061 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1062 skIBounds.outset(1, 1);
1063 glyph->fLeft = SkToS16(skIBounds.fLeft);
1064 glyph->fTop = SkToS16(skIBounds.fTop);
1065 glyph->fWidth = SkToU16(skIBounds.width());
1066 glyph->fHeight = SkToU16(skIBounds.height());
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001067
bungeman@google.comcefd9812013-05-15 15:07:32 +00001068#ifdef HACK_COLORGLYPHS
1069 glyph->fMaskFormat = SkMask::kARGB32_Format;
1070#endif
1071}
1072
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001073#include "SkColorPriv.h"
1074
1075static void build_power_table(uint8_t table[], float ee) {
1076 for (int i = 0; i < 256; i++) {
1077 float x = i / 255.f;
1078 x = sk_float_pow(x, ee);
1079 int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
1080 table[i] = SkToU8(xx);
1081 }
1082}
1083
1084/**
1085 * This will invert the gamma applied by CoreGraphics, so we can get linear
1086 * values.
1087 *
1088 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1089 * The color space used does not appear to affect this choice.
1090 */
1091static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1092 static bool gInited;
1093 static uint8_t gTableCoreGraphicsSmoothing[256];
1094 if (!gInited) {
1095 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1096 gInited = true;
1097 }
1098 return gTableCoreGraphicsSmoothing;
1099}
1100
1101static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1102 while (count > 0) {
1103 uint8_t mask = 0;
1104 for (int i = 7; i >= 0; --i) {
1105 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1106 if (0 == --count) {
1107 break;
1108 }
1109 }
1110 *dst++ = mask;
1111 }
1112}
1113
1114template<bool APPLY_PREBLEND>
1115static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1116 U8CPU r = (rgb >> 16) & 0xFF;
1117 U8CPU g = (rgb >> 8) & 0xFF;
1118 U8CPU b = (rgb >> 0) & 0xFF;
1119 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1120}
1121template<bool APPLY_PREBLEND>
1122static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1123 const SkGlyph& glyph, const uint8_t* table8) {
1124 const int width = glyph.fWidth;
1125 size_t dstRB = glyph.rowBytes();
1126 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1127
1128 for (int y = 0; y < glyph.fHeight; y++) {
1129 for (int i = 0; i < width; ++i) {
1130 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1131 }
1132 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1133 dst += dstRB;
1134 }
1135}
1136
1137template<bool APPLY_PREBLEND>
1138static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1139 const uint8_t* tableG,
1140 const uint8_t* tableB) {
1141 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1142 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1143 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1144 return SkPack888ToRGB16(r, g, b);
1145}
1146template<bool APPLY_PREBLEND>
1147static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1148 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1149 const int width = glyph.fWidth;
1150 size_t dstRB = glyph.rowBytes();
1151 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1152
1153 for (int y = 0; y < glyph.fHeight; y++) {
1154 for (int i = 0; i < width; i++) {
1155 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1156 }
1157 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1158 dst = (uint16_t*)((char*)dst + dstRB);
1159 }
1160}
1161
1162template<bool APPLY_PREBLEND>
1163static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
1164 const uint8_t* tableG,
1165 const uint8_t* tableB) {
1166 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1167 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1168 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1169 return SkPackARGB32(0xFF, r, g, b);
1170}
1171template<bool APPLY_PREBLEND>
1172static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1173 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1174 const int width = glyph.fWidth;
1175 size_t dstRB = glyph.rowBytes();
1176 uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
1177 for (int y = 0; y < glyph.fHeight; y++) {
1178 for (int i = 0; i < width; i++) {
1179 dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1180 }
1181 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1182 dst = (uint32_t*)((char*)dst + dstRB);
1183 }
1184}
1185
reed@google.comf77b35d2013-05-02 20:39:44 +00001186#ifdef HACK_COLORGLYPHS
1187// hack to colorize the output for testing kARGB32_Format
1188static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb, const SkGlyph& glyph,
1189 int x, int y) {
1190 U8CPU r = (rgb >> 16) & 0xFF;
1191 U8CPU g = (rgb >> 8) & 0xFF;
1192 U8CPU b = (rgb >> 0) & 0xFF;
1193 unsigned a = SkComputeLuminance(r, g, b);
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +00001194
reed@google.comf77b35d2013-05-02 20:39:44 +00001195 // compute gradient from x,y
1196 r = x * 255 / glyph.fWidth;
1197 g = 0;
1198 b = (glyph.fHeight - y) * 255 / glyph.fHeight;
1199 return SkPreMultiplyARGB(a, r, g, b); // red
1200}
1201#endif
1202
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001203template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1204 return (T*)((char*)ptr + byteOffset);
1205}
1206
1207void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
1208 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
1209
1210 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1211 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1212
1213 // Draw the glyph
1214 size_t cgRowBytes;
1215 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
1216 if (cgPixels == NULL) {
1217 return;
1218 }
1219
1220 //TODO: see if drawing black on white and inverting is faster (at least in
1221 //lcd case) as core graphics appears to have special case code for drawing
1222 //black text.
1223
1224 // Fix the glyph
1225 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1226 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1227 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1228
1229 //Note that the following cannot really be integrated into the
1230 //pre-blend, since we may not be applying the pre-blend; when we aren't
1231 //applying the pre-blend it means that a filter wants linear anyway.
1232 //Other code may also be applying the pre-blend, so we'd need another
1233 //one with this and one without.
1234 CGRGBPixel* addr = cgPixels;
1235 for (int y = 0; y < glyph.fHeight; ++y) {
1236 for (int x = 0; x < glyph.fWidth; ++x) {
1237 int r = (addr[x] >> 16) & 0xFF;
1238 int g = (addr[x] >> 8) & 0xFF;
1239 int b = (addr[x] >> 0) & 0xFF;
1240 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1241 }
1242 addr = SkTAddByteOffset(addr, cgRowBytes);
1243 }
1244 }
1245
1246 // Convert glyph to mask
1247 switch (glyph.fMaskFormat) {
1248 case SkMask::kLCD32_Format: {
1249 if (fPreBlend.isApplicable()) {
1250 rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
1251 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1252 } else {
1253 rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
1254 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1255 }
1256 } break;
1257 case SkMask::kLCD16_Format: {
1258 if (fPreBlend.isApplicable()) {
1259 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1260 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1261 } else {
1262 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1263 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1264 }
1265 } break;
1266 case SkMask::kA8_Format: {
1267 if (fPreBlend.isApplicable()) {
1268 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1269 } else {
1270 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1271 }
1272 } break;
1273 case SkMask::kBW_Format: {
1274 const int width = glyph.fWidth;
1275 size_t dstRB = glyph.rowBytes();
1276 uint8_t* dst = (uint8_t*)glyph.fImage;
1277 for (int y = 0; y < glyph.fHeight; y++) {
1278 cgpixels_to_bits(dst, cgPixels, width);
1279 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1280 dst += dstRB;
1281 }
1282 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001283#ifdef HACK_COLORGLYPHS
1284 case SkMask::kARGB32_Format: {
1285 const int width = glyph.fWidth;
1286 size_t dstRB = glyph.rowBytes();
1287 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1288 for (int y = 0; y < glyph.fHeight; y++) {
1289 for (int x = 0; x < width; ++x) {
1290 dst[x] = cgpixels_to_pmcolor(cgPixels[x], glyph, x, y);
1291 }
1292 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1293 dst = (SkPMColor*)((char*)dst + dstRB);
1294 }
1295 } break;
1296#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001297 default:
1298 SkDEBUGFAIL("unexpected mask format");
1299 break;
1300 }
1301}
1302
1303/*
1304 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1305 * seems sufficient, and possibly even correct, to allow the hinted outline
1306 * to be subpixel positioned.
1307 */
1308#define kScaleForSubPixelPositionHinting (4.0f)
1309
1310void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
1311 CTFontRef font = fCTFont;
1312 SkScalar scaleX = SK_Scalar1;
1313 SkScalar scaleY = SK_Scalar1;
1314
1315 /*
1316 * For subpixel positioning, we want to return an unhinted outline, so it
1317 * can be positioned nicely at fractional offsets. However, we special-case
1318 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1319 * we want to retain hinting in the direction orthogonal to the baseline.
1320 * e.g. for horizontal baseline, we want to retain hinting in Y.
1321 * The way we remove hinting is to scale the font by some value (4) in that
1322 * direction, ask for the path, and then scale the path back down.
1323 */
1324 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1325 SkMatrix m;
1326 fRec.getSingleMatrix(&m);
1327
1328 // start out by assuming that we want no hining in X and Y
1329 scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
1330 // now see if we need to restore hinting for axis-aligned baselines
1331 switch (SkComputeAxisAlignmentForHText(m)) {
1332 case kX_SkAxisAlignment:
1333 scaleY = SK_Scalar1; // want hinting in the Y direction
1334 break;
1335 case kY_SkAxisAlignment:
1336 scaleX = SK_Scalar1; // want hinting in the X direction
1337 break;
1338 default:
1339 break;
1340 }
1341
1342 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
1343 // need to release font when we're done
1344 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
1345 }
1346
1347 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
1348 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
1349
1350 path->reset();
1351 if (cgPath != NULL) {
1352 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1353 }
1354
bungeman@google.comcefd9812013-05-15 15:07:32 +00001355 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001356 SkMatrix m;
1357 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1358 path->transform(m);
1359 // balance the call to CTFontCreateCopyWithAttributes
1360 CFSafeRelease(font);
1361 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001362 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001363 SkPoint offset;
1364 getVerticalOffset(cgGlyph, &offset);
1365 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001366 }
1367}
1368
1369void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
1370 SkPaint::FontMetrics* my) {
1371 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1372
1373 SkPaint::FontMetrics theMetrics;
1374 theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1375 theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1376 theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1377 theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1378 theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1379 theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1380 theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1381 theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
1382 theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
1383
1384 if (mx != NULL) {
1385 *mx = theMetrics;
1386 }
1387 if (my != NULL) {
1388 *my = theMetrics;
1389 }
1390}
1391
1392void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1393 SkPath* skPath = (SkPath*)info;
1394
1395 // Process the path element
1396 switch (element->type) {
1397 case kCGPathElementMoveToPoint:
1398 skPath->moveTo(element->points[0].x, -element->points[0].y);
1399 break;
1400
1401 case kCGPathElementAddLineToPoint:
1402 skPath->lineTo(element->points[0].x, -element->points[0].y);
1403 break;
1404
1405 case kCGPathElementAddQuadCurveToPoint:
1406 skPath->quadTo(element->points[0].x, -element->points[0].y,
1407 element->points[1].x, -element->points[1].y);
1408 break;
1409
1410 case kCGPathElementAddCurveToPoint:
1411 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1412 element->points[1].x, -element->points[1].y,
1413 element->points[2].x, -element->points[2].y);
1414 break;
1415
1416 case kCGPathElementCloseSubpath:
1417 skPath->close();
1418 break;
1419
1420 default:
1421 SkDEBUGFAIL("Unknown path element!");
1422 break;
1423 }
1424}
1425
1426
1427///////////////////////////////////////////////////////////////////////////////
1428
1429// Returns NULL on failure
1430// Call must still manage its ownership of provider
1431static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1432 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
1433 if (NULL == cg) {
1434 return NULL;
1435 }
1436 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
1437 return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
1438}
1439
1440SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
1441 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
1442 if (NULL == provider) {
1443 return NULL;
1444 }
1445 return create_from_dataProvider(provider);
1446}
1447
1448SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
1449 AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
1450 if (NULL == provider) {
1451 return NULL;
1452 }
1453 return create_from_dataProvider(provider);
1454}
1455
1456// Web fonts added to the the CTFont registry do not return their character set.
1457// Iterate through the font in this case. The existing caller caches the result,
1458// so the performance impact isn't too bad.
1459static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1460 SkTDArray<SkUnichar>* glyphToUnicode) {
1461 glyphToUnicode->setCount(glyphCount);
1462 SkUnichar* out = glyphToUnicode->begin();
1463 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1464 UniChar unichar = 0;
1465 while (glyphCount > 0) {
1466 CGGlyph glyph;
1467 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1468 out[glyph] = unichar;
1469 --glyphCount;
1470 }
1471 if (++unichar == 0) {
1472 break;
1473 }
1474 }
1475}
1476
1477// Construct Glyph to Unicode table.
1478// Unicode code points that require conjugate pairs in utf16 are not
1479// supported.
1480static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1481 SkTDArray<SkUnichar>* glyphToUnicode) {
1482 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1483 if (!charSet) {
1484 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1485 return;
1486 }
1487
1488 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1489 charSet));
1490 if (!bitmap) {
1491 return;
1492 }
1493 CFIndex length = CFDataGetLength(bitmap);
1494 if (!length) {
1495 return;
1496 }
1497 if (length > 8192) {
1498 // TODO: Add support for Unicode above 0xFFFF
1499 // Consider only the BMP portion of the Unicode character points.
1500 // The bitmap may contain other planes, up to plane 16.
1501 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1502 length = 8192;
1503 }
1504 const UInt8* bits = CFDataGetBytePtr(bitmap);
1505 glyphToUnicode->setCount(glyphCount);
1506 SkUnichar* out = glyphToUnicode->begin();
1507 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1508 for (int i = 0; i < length; i++) {
1509 int mask = bits[i];
1510 if (!mask) {
1511 continue;
1512 }
1513 for (int j = 0; j < 8; j++) {
1514 CGGlyph glyph;
1515 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1516 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1517 out[glyph] = unichar;
1518 }
1519 }
1520 }
1521}
1522
1523static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1524 CGSize advance;
1525 advance.width = 0;
1526 CGGlyph glyph = gId;
1527 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1528 *data = sk_float_round2int(advance.width);
1529 return true;
1530}
1531
1532// we might move this into our CGUtils...
1533static void CFStringToSkString(CFStringRef src, SkString* dst) {
1534 // Reserve enough room for the worst-case string,
1535 // plus 1 byte for the trailing null.
1536 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1537 kCFStringEncodingUTF8) + 1;
1538 dst->resize(length);
1539 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1540 // Resize to the actual UTF-8 length used, stripping the null character.
1541 dst->resize(strlen(dst->c_str()));
1542}
1543
reed@google.com2689f612013-03-20 20:01:47 +00001544SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001545 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1546 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001547 uint32_t glyphIDsCount) const {
1548
1549 CTFontRef originalCTFont = fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001550 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
1551 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
1552 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1553
1554 {
1555 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
1556 CFStringToSkString(fontName, &info->fFontName);
1557 }
1558
1559 info->fMultiMaster = false;
1560 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1561 info->fLastGlyphID = SkToU16(glyphCount - 1);
1562 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1563
1564 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1565 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1566 }
1567
1568 info->fStyle = 0;
1569
1570 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1571 // fonts always have both glyf and loca tables. At the least, this is what
1572 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1573 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001574 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001575 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1576 info->fItalicAngle = 0;
1577 info->fAscent = 0;
1578 info->fDescent = 0;
1579 info->fStemV = 0;
1580 info->fCapHeight = 0;
1581 info->fBBox = SkIRect::MakeEmpty();
1582 return info;
1583 }
1584
1585 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1586 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1587 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1588 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1589 }
1590 if (symbolicTraits & kCTFontItalicTrait) {
1591 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1592 }
1593 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001594 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1595 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1596 } else if (stylisticClass & kCTFontScriptsClass) {
1597 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1598 }
1599 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1600 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1601 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1602 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1603 CGRect bbox = CTFontGetBoundingBox(ctFont);
1604
1605 SkRect r;
1606 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1607 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1608 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1609 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1610
1611 r.roundOut(&(info->fBBox));
1612
1613 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1614 // This probably isn't very good with an italic font.
1615 int16_t min_width = SHRT_MAX;
1616 info->fStemV = 0;
1617 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1618 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1619 CGGlyph glyphs[count];
1620 CGRect boundingRects[count];
1621 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1622 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1623 glyphs, boundingRects, count);
1624 for (size_t i = 0; i < count; i++) {
1625 int16_t width = (int16_t) boundingRects[i].size.width;
1626 if (width > 0 && width < min_width) {
1627 min_width = width;
1628 info->fStemV = min_width;
1629 }
1630 }
1631 }
1632
1633 if (false) { // TODO: haven't figured out how to know if font is embeddable
1634 // (information is in the OS/2 table)
1635 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
1636 } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
1637 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1638 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1639 info->fGlyphWidths->fAdvance.append(1, &min_width);
1640 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1641 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1642 } else {
1643 info->fGlyphWidths.reset(
1644 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
1645 glyphCount,
1646 glyphIDs,
1647 glyphIDsCount,
1648 &getWidthAdvance));
1649 }
1650 }
1651 return info;
1652}
1653
1654///////////////////////////////////////////////////////////////////////////////
1655
reed@google.comcc9aad52013-03-21 19:28:10 +00001656static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1657 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001658 AutoCFRelease<CFNumberRef> fontFormatRef(
1659 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1660 if (!fontFormatRef) {
1661 return 0;
1662 }
1663
1664 SInt32 fontFormatValue;
1665 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1666 return 0;
1667 }
1668
1669 switch (fontFormatValue) {
1670 case kCTFontFormatOpenTypePostScript:
1671 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1672 case kCTFontFormatOpenTypeTrueType:
1673 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1674 case kCTFontFormatTrueType:
1675 return SkSFNTHeader::fontType_MacTrueType::TAG;
1676 case kCTFontFormatPostScript:
1677 return SkSFNTHeader::fontType_PostScript::TAG;
1678 case kCTFontFormatBitmap:
1679 return SkSFNTHeader::fontType_MacTrueType::TAG;
1680 case kCTFontFormatUnrecognized:
1681 default:
1682 //CT seems to be unreliable in being able to obtain the type,
1683 //even if all we want is the first four bytes of the font resource.
1684 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1685 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1686 }
1687}
1688
reed@google.comcc9aad52013-03-21 19:28:10 +00001689SkStream* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
1690 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001691 if (0 == fontType) {
1692 return NULL;
1693 }
1694
1695 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001696 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001697 SkTDArray<SkFontTableTag> tableTags;
1698 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001699 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001700
1701 // calc total size for font, save sizes
1702 SkTDArray<size_t> tableSizes;
1703 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1704 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001705 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001706 totalSize += (tableSize + 3) & ~3;
1707 *tableSizes.append() = tableSize;
1708 }
1709
1710 // reserve memory for stream, and zero it (tables must be zero padded)
1711 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1712 char* dataStart = (char*)stream->getMemoryBase();
1713 sk_bzero(dataStart, totalSize);
1714 char* dataPtr = dataStart;
1715
1716 // compute font header entries
1717 uint16_t entrySelector = 0;
1718 uint16_t searchRange = 1;
1719 while (searchRange < numTables >> 1) {
1720 entrySelector++;
1721 searchRange <<= 1;
1722 }
1723 searchRange <<= 4;
1724 uint16_t rangeShift = (numTables << 4) - searchRange;
1725
1726 // write font header
1727 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1728 header->fontType = fontType;
1729 header->numTables = SkEndian_SwapBE16(numTables);
1730 header->searchRange = SkEndian_SwapBE16(searchRange);
1731 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1732 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1733 dataPtr += sizeof(SkSFNTHeader);
1734
1735 // write tables
1736 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1737 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1738 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1739 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001740 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001741 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1742 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1743 tableSize));
1744 entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
1745 entry->logicalLength = SkEndian_SwapBE32(tableSize);
1746
1747 dataPtr += (tableSize + 3) & ~3;
1748 ++entry;
1749 }
1750
1751 return stream;
1752}
1753
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001754///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001755///////////////////////////////////////////////////////////////////////////////
1756
1757int SkTypeface_Mac::onGetUPEM() const {
1758 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1759 return CGFontGetUnitsPerEm(cgFont);
1760}
1761
1762// If, as is the case with web fonts, the CTFont data isn't available,
1763// the CGFont data may work. While the CGFont may always provide the
1764// right result, leave the CTFont code path to minimize disruption.
1765static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1766 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1767 kCTFontTableOptionNoOptions);
1768 if (NULL == data) {
1769 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
1770 data = CGFontCopyTableForTag(cgFont, tag);
1771 }
1772 return data;
1773}
1774
1775int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1776 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1777 kCTFontTableOptionNoOptions));
1778 if (NULL == cfArray) {
1779 return 0;
1780 }
1781 int count = CFArrayGetCount(cfArray);
1782 if (tags) {
1783 for (int i = 0; i < count; ++i) {
1784 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1785 tags[i] = static_cast<SkFontTableTag>(fontTag);
1786 }
1787 }
1788 return count;
1789}
1790
1791size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1792 size_t length, void* dstData) const {
1793 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
1794 if (NULL == srcData) {
1795 return 0;
1796 }
1797
1798 size_t srcSize = CFDataGetLength(srcData);
1799 if (offset >= srcSize) {
1800 return 0;
1801 }
1802 if (length > srcSize - offset) {
1803 length = srcSize - offset;
1804 }
1805 if (dstData) {
1806 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1807 }
1808 return length;
1809}
1810
1811SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001812 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001813}
1814
1815void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001816 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1817 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1818 {
1819 rec->fMaskFormat = SkMask::kA8_Format;
1820 // Render the glyphs as close as possible to what was requested.
1821 // The above turns off subpixel rendering, but the user requested it.
1822 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1823 // See comments below for more details.
1824 rec->setHinting(SkPaint::kNormal_Hinting);
1825 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001826
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001827 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
1828 SkScalerContext::kAutohinting_Flag |
1829 SkScalerContext::kLCD_BGROrder_Flag |
1830 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001831
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001832 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001833
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001834 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001835
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001836 // Only two levels of hinting are supported.
1837 // kNo_Hinting means avoid CoreGraphics outline dilation.
1838 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1839 // If there is no lcd support, hinting (dilation) cannot be supported.
1840 SkPaint::Hinting hinting = rec->getHinting();
1841 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1842 hinting = SkPaint::kNo_Hinting;
1843 } else if (SkPaint::kFull_Hinting == hinting) {
1844 hinting = SkPaint::kNormal_Hinting;
1845 }
1846 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001847
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001848 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1849 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1850 // There is no current means to honor a request for unhinted lcd,
1851 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001852
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001853 // Hinting and smoothing should be orthogonal, but currently they are not.
1854 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
1855 // output is drawn from auto-dilated outlines (the amount of which is
1856 // determined by AppleFontSmoothing). Its regular anti-aliased output is
1857 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001858
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001859 // The behavior of Skia is as follows:
1860 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
1861 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
1862 // channel. This matches [LCD][yes-hint] in weight.
1863 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
1864 // Currenly side with LCD, effectively ignoring the hinting setting.
1865 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001866
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001867 if (isLCDFormat(rec->fMaskFormat)) {
1868 if (lcdSupport) {
1869 //CoreGraphics creates 555 masks for smoothed text anyway.
1870 rec->fMaskFormat = SkMask::kLCD16_Format;
1871 rec->setHinting(SkPaint::kNormal_Hinting);
1872 } else {
1873 rec->fMaskFormat = SkMask::kA8_Format;
1874 }
1875 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001876
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001877 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1878 // All other masks can use regular gamma.
1879 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
1880#ifndef SK_GAMMA_APPLY_TO_A8
1881 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00001882#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001883 } else {
1884 //CoreGraphics dialates smoothed text as needed.
1885 rec->setContrast(0);
1886 }
1887}
1888
1889// we take ownership of the ref
1890static const char* get_str(CFStringRef ref, SkString* str) {
1891 CFStringToSkString(ref, str);
1892 CFSafeRelease(ref);
1893 return str->c_str();
1894}
1895
reed@google.com5526ede2013-03-25 13:03:37 +00001896void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
1897 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001898 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001899
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001900 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
1901 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
1902 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
reed@google.com5526ede2013-03-25 13:03:37 +00001903 // TODO: need to add support for local-streams (here and openStream)
1904 *isLocalStream = false;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001905}
reed@google.com5526ede2013-03-25 13:03:37 +00001906
reed@google.combcb42ae2013-07-02 13:56:39 +00001907int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
1908 uint16_t glyphs[], int glyphCount) const {
1909 // UniChar is utf16
1910 SkAutoSTMalloc<1024, UniChar> charStorage;
1911 const UniChar* src;
1912 switch (encoding) {
1913 case kUTF8_Encoding: {
1914 const char* u8 = (const char*)chars;
1915 const UniChar* u16 = src = charStorage.reset(2 * glyphCount);
1916 for (int i = 0; i < glyphCount; ++i) {
1917 SkUnichar uni = SkUTF8_NextUnichar(&u8);
1918 int n = SkUTF16_FromUnichar(uni, (uint16_t*)u16);
1919 u16 += n;
1920 }
1921 break;
1922 }
1923 case kUTF16_Encoding:
1924 src = (const UniChar*)chars;
1925 break;
1926 case kUTF32_Encoding: {
1927 const SkUnichar* u32 = (const SkUnichar*)chars;
1928 const UniChar* u16 = src = charStorage.reset(2 * glyphCount);
1929 for (int i = 0; i < glyphCount; ++i) {
1930 int n = SkUTF16_FromUnichar(u32[i], (uint16_t*)u16);
1931 u16 += n;
1932 }
1933 break;
1934 }
1935 }
1936
1937 // Our caller may not want glyphs for output, but we need to give that
1938 // storage to CT, so we can walk it looking for the first non-zero.
1939 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
1940 uint16_t* macGlyphs = glyphs;
1941 if (NULL == macGlyphs) {
1942 macGlyphs = glyphStorage.reset(glyphCount);
1943 }
1944
1945 if (CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, glyphCount)) {
1946 return glyphCount;
1947 }
1948 // If we got false, then we need to manually look for first failure
1949 for (int i = 0; i < glyphCount; ++i) {
1950 if (0 == macGlyphs[i]) {
1951 return i;
1952 }
1953 }
1954 // odd to get here, as we expected CT to have returned true up front.
1955 return glyphCount;
1956}
1957
1958int SkTypeface_Mac::onCountGlyphs() const {
1959 return CTFontGetGlyphCount(fFontRef);
1960}
1961
reed@google.com95625db2013-03-25 20:44:02 +00001962///////////////////////////////////////////////////////////////////////////////
1963///////////////////////////////////////////////////////////////////////////////
reed@google.com95625db2013-03-25 20:44:02 +00001964#if 1
reed@google.com83787c52013-03-26 17:19:15 +00001965
1966static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
1967 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
1968 if (NULL == ref.get()) {
1969 return false;
1970 }
1971 CFStringToSkString(ref, value);
1972 return true;
1973}
1974
1975static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
1976 CFNumberRef num;
1977 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
1978 && CFNumberIsFloatType(num)
1979 && CFNumberGetValue(num, kCFNumberFloatType, value);
1980}
1981
reed@google.com95625db2013-03-25 20:44:02 +00001982#include "SkFontMgr.h"
1983
reed@google.com83787c52013-03-26 17:19:15 +00001984static int unit_weight_to_fontstyle(float unit) {
1985 float value;
1986 if (unit < 0) {
1987 value = 100 + (1 + unit) * 300;
1988 } else {
1989 value = 400 + unit * 500;
1990 }
1991 return sk_float_round2int(value);
1992}
1993
1994static int unit_width_to_fontstyle(float unit) {
1995 float value;
1996 if (unit < 0) {
1997 value = 1 + (1 + unit) * 4;
1998 } else {
1999 value = 5 + unit * 4;
2000 }
2001 return sk_float_round2int(value);
2002}
2003
reed@google.com964988f2013-03-29 14:57:22 +00002004static inline int sqr(int value) {
2005 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2006 return value * value;
2007}
2008
2009// We normalize each axis (weight, width, italic) to be base-900
2010static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2011 return sqr(a.weight() - b.weight()) +
2012 sqr((a.width() - b.width()) * 100) +
2013 sqr((a.isItalic() != b.isItalic()) * 900);
2014}
2015
reed@google.com83787c52013-03-26 17:19:15 +00002016static SkFontStyle desc2fontstyle(CTFontDescriptorRef desc) {
2017 AutoCFRelease<CFDictionaryRef> dict(
2018 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc,
2019 kCTFontTraitsAttribute));
2020 if (NULL == dict.get()) {
2021 return SkFontStyle();
2022 }
2023
2024 float weight, width, slant;
2025 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
2026 weight = 0;
2027 }
2028 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
2029 width = 0;
2030 }
2031 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
2032 slant = 0;
2033 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002034
reed@google.com83787c52013-03-26 17:19:15 +00002035 return SkFontStyle(unit_weight_to_fontstyle(weight),
2036 unit_width_to_fontstyle(width),
2037 slant ? SkFontStyle::kItalic_Slant
2038 : SkFontStyle::kUpright_Slant);
2039}
2040
reed@google.comdea7ee02013-03-28 14:12:10 +00002041struct NameFontStyleRec {
2042 SkString fFamilyName;
2043 SkFontStyle fFontStyle;
2044};
2045
2046static bool nameFontStyleProc(SkTypeface* face, SkTypeface::Style,
2047 void* ctx) {
2048 SkTypeface_Mac* macFace = (SkTypeface_Mac*)face;
2049 const NameFontStyleRec* rec = (const NameFontStyleRec*)ctx;
2050
2051 return macFace->fFontStyle == rec->fFontStyle &&
2052 macFace->fName == rec->fFamilyName;
2053}
2054
reed@google.comce8b3de2013-03-26 19:30:16 +00002055static SkTypeface* createFromDesc(CFStringRef cfFamilyName,
2056 CTFontDescriptorRef desc) {
reed@google.comdea7ee02013-03-28 14:12:10 +00002057 NameFontStyleRec rec;
2058 CFStringToSkString(cfFamilyName, &rec.fFamilyName);
2059 rec.fFontStyle = desc2fontstyle(desc);
2060
2061 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(nameFontStyleProc,
2062 &rec);
2063 if (face) {
2064 return face;
2065 }
2066
reed@google.comce8b3de2013-03-26 19:30:16 +00002067 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFamilyName, 1, NULL));
2068 CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc);
2069 if (NULL == ctFont) {
2070 return NULL;
2071 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002072
reed@google.comce8b3de2013-03-26 19:30:16 +00002073 SkString str;
2074 CFStringToSkString(cfFamilyName, &str);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002075
reed@google.comce8b3de2013-03-26 19:30:16 +00002076 bool isFixedPitch;
2077 (void)computeStyleBits(ctFont, &isFixedPitch);
2078 SkFontID fontID = CTFontRef_to_SkFontID(ctFont);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002079
reed@google.comdea7ee02013-03-28 14:12:10 +00002080 face = SkNEW_ARGS(SkTypeface_Mac, (rec.fFontStyle, fontID, isFixedPitch,
2081 ctFont, str.c_str()));
2082 SkTypefaceCache::Add(face, face->style());
2083 return face;
reed@google.comce8b3de2013-03-26 19:30:16 +00002084}
2085
reed@google.com83787c52013-03-26 17:19:15 +00002086class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002087public:
reed@google.com83787c52013-03-26 17:19:15 +00002088 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
2089 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, NULL))
reed@google.comdea7ee02013-03-28 14:12:10 +00002090 , fFamilyName(familyName)
2091 , fCount(0) {
reed@google.com83787c52013-03-26 17:19:15 +00002092 CFRetain(familyName);
reed@google.comdea7ee02013-03-28 14:12:10 +00002093 if (NULL == fArray) {
2094 fArray = CFArrayCreate(NULL, NULL, 0, NULL);
2095 }
2096 fCount = CFArrayGetCount(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002097 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002098
reed@google.com83787c52013-03-26 17:19:15 +00002099 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002100 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002101 CFRelease(fFamilyName);
2102 }
2103
2104 virtual int count() SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002105 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002106 }
2107
2108 virtual void getStyle(int index, SkFontStyle* style,
2109 SkString* name) SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002110 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002111 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2112 if (style) {
2113 *style = desc2fontstyle(desc);
2114 }
2115 if (name) {
2116 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2117 name->reset();
2118 }
2119 }
2120 }
2121
2122 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
2123 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2124 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002125
reed@google.com83787c52013-03-26 17:19:15 +00002126 return createFromDesc(fFamilyName, desc);
2127 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002128
reed@google.com964988f2013-03-29 14:57:22 +00002129 virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
2130 if (0 == fCount) {
2131 return NULL;
2132 }
2133 return createFromDesc(fFamilyName, findMatchingDesc(pattern));
2134 }
2135
reed@google.com83787c52013-03-26 17:19:15 +00002136private:
2137 CFArrayRef fArray;
2138 CFStringRef fFamilyName;
reed@google.comdea7ee02013-03-28 14:12:10 +00002139 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002140
2141 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2142 int bestMetric = SK_MaxS32;
2143 CTFontDescriptorRef bestDesc = NULL;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002144
reed@google.com964988f2013-03-29 14:57:22 +00002145 for (int i = 0; i < fCount; ++i) {
2146 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
2147 int metric = compute_metric(pattern, desc2fontstyle(desc));
2148 if (0 == metric) {
2149 return desc;
2150 }
2151 if (metric < bestMetric) {
2152 bestMetric = metric;
2153 bestDesc = desc;
2154 }
2155 }
2156 SkASSERT(bestDesc);
2157 return bestDesc;
2158 }
reed@google.com83787c52013-03-26 17:19:15 +00002159};
2160
2161class SkFontMgr_Mac : public SkFontMgr {
2162 int fCount;
2163 CFArrayRef fNames;
2164
2165 CFStringRef stringAt(int index) const {
2166 SkASSERT((unsigned)index < (unsigned)fCount);
2167 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2168 }
2169
2170 void lazyInit() {
2171 if (NULL == fNames) {
reed@google.com3dcbd462013-03-27 13:56:34 +00002172 fNames = SkCTFontManagerCopyAvailableFontFamilyNames();
reed@google.com83787c52013-03-26 17:19:15 +00002173 fCount = fNames ? CFArrayGetCount(fNames) : 0;
2174 }
2175 }
2176
reed@google.com964988f2013-03-29 14:57:22 +00002177 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2178 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2179 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2180 &kCFTypeDictionaryKeyCallBacks,
2181 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002182
reed@google.com964988f2013-03-29 14:57:22 +00002183 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002184
reed@google.com964988f2013-03-29 14:57:22 +00002185 AutoCFRelease<CTFontDescriptorRef> desc(
2186 CTFontDescriptorCreateWithAttributes(cfAttr));
2187 return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc));
2188 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002189
reed@google.com83787c52013-03-26 17:19:15 +00002190public:
2191 SkFontMgr_Mac() : fCount(0), fNames(NULL) {}
2192
2193 virtual ~SkFontMgr_Mac() {
2194 CFSafeRelease(fNames);
2195 }
reed@google.com95625db2013-03-25 20:44:02 +00002196
2197protected:
2198 virtual int onCountFamilies() SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002199 this->lazyInit();
2200 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002201 }
2202
2203 virtual void onGetFamilyName(int index, SkString* familyName) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002204 this->lazyInit();
2205 if ((unsigned)index < (unsigned)fCount) {
2206 CFStringToSkString(this->stringAt(index), familyName);
2207 } else {
2208 familyName->reset();
2209 }
reed@google.com95625db2013-03-25 20:44:02 +00002210 }
2211
2212 virtual SkFontStyleSet* onCreateStyleSet(int index) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002213 this->lazyInit();
2214 if ((unsigned)index >= (unsigned)fCount) {
2215 return NULL;
2216 }
reed@google.com964988f2013-03-29 14:57:22 +00002217 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002218 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002219
reed@google.com964988f2013-03-29 14:57:22 +00002220 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) SK_OVERRIDE {
2221 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2222 return CreateSet(cfName);
2223 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002224
reed@google.com95625db2013-03-25 20:44:02 +00002225 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
2226 const SkFontStyle&) SK_OVERRIDE {
2227 return NULL;
2228 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002229
reed@google.com95625db2013-03-25 20:44:02 +00002230 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2231 const SkFontStyle&) SK_OVERRIDE {
2232 return NULL;
2233 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002234
reed@google.com95625db2013-03-25 20:44:02 +00002235 virtual SkTypeface* onCreateFromData(SkData* data,
2236 int ttcIndex) SK_OVERRIDE {
2237 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
2238 if (NULL == pr) {
2239 return NULL;
2240 }
2241 return create_from_dataProvider(pr);
2242 }
2243
2244 virtual SkTypeface* onCreateFromStream(SkStream* stream,
2245 int ttcIndex) SK_OVERRIDE {
2246 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
2247 if (NULL == pr) {
2248 return NULL;
2249 }
2250 return create_from_dataProvider(pr);
2251 }
2252
2253 virtual SkTypeface* onCreateFromFile(const char path[],
2254 int ttcIndex) SK_OVERRIDE {
2255 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2256 if (NULL == pr) {
2257 return NULL;
2258 }
2259 return create_from_dataProvider(pr);
2260 }
2261};
2262
2263SkFontMgr* SkFontMgr::Factory() {
2264 return SkNEW(SkFontMgr_Mac);
2265}
2266#endif