blob: 71fa5ebdb066dd1541c030c3fc245c793d406d37 [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;
reed@google.com7fdcd442013-07-30 21:25:49 +0000468 virtual SkTypeface* onRefMatchingStyle(Style) const SK_OVERRIDE;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000469
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000470private:
reed@google.comce8b3de2013-03-26 19:30:16 +0000471
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000472 typedef SkTypeface INHERITED;
473};
474
475static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
476 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000477 bool isFixedPitch;
478 SkTypeface::Style style = computeStyleBits(fontRef, &isFixedPitch);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000479 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
480
bungeman@google.comfe747652013-03-25 19:36:11 +0000481 return new SkTypeface_Mac(style, fontID, isFixedPitch, fontRef, name);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000482}
483
484static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
485 CTFontRef ctFont = NULL;
486
487 CTFontSymbolicTraits ctFontTraits = 0;
488 if (theStyle & SkTypeface::kBold) {
489 ctFontTraits |= kCTFontBoldTrait;
490 }
491 if (theStyle & SkTypeface::kItalic) {
492 ctFontTraits |= kCTFontItalicTrait;
493 }
494
495 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000496 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000497
498 AutoCFRelease<CFNumberRef> cfFontTraits(
499 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
500
501 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
502 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
503 &kCFTypeDictionaryKeyCallBacks,
504 &kCFTypeDictionaryValueCallBacks));
505
506 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
507 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
508 &kCFTypeDictionaryKeyCallBacks,
509 &kCFTypeDictionaryValueCallBacks));
510
511 // Create the font
512 if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
513 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
514
515 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
516 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
517
518 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
519 CTFontDescriptorCreateWithAttributes(cfAttributes));
520
521 if (ctFontDesc != NULL) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000522 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000523 }
524 }
525
526 return ctFont ? NewFromFontRef(ctFont, familyName) : NULL;
527}
528
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000529static SkTypeface* GetDefaultFace() {
530 SK_DECLARE_STATIC_MUTEX(gMutex);
531 SkAutoMutexAcquire ma(gMutex);
532
533 static SkTypeface* gDefaultFace;
534
535 if (NULL == gDefaultFace) {
536 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal);
537 SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal);
538 }
539 return gDefaultFace;
540}
541
542///////////////////////////////////////////////////////////////////////////////
543
544extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
545CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
546 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
547 return macface ? macface->fFontRef.get() : NULL;
548}
549
550/* This function is visible on the outside. It first searches the cache, and if
551 * not found, returns a new entry (after adding it to the cache).
552 */
553SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
554 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
555 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
556 if (face) {
557 face->ref();
558 } else {
559 face = NewFromFontRef(fontRef, NULL);
560 SkTypefaceCache::Add(face, face->style());
561 // NewFromFontRef doesn't retain the parameter, but the typeface it
562 // creates does release it in its destructor, so we balance that with
563 // a retain call here.
564 CFRetain(fontRef);
565 }
566 SkASSERT(face->getRefCnt() > 1);
567 return face;
568}
569
570struct NameStyleRec {
571 const char* fName;
572 SkTypeface::Style fStyle;
573};
574
575static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style,
576 void* ctx) {
577 const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face);
578 const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx);
579
580 return rec->fStyle == style && mface->fName.equals(rec->fName);
581}
582
583static const char* map_css_names(const char* name) {
584 static const struct {
585 const char* fFrom; // name the caller specified
586 const char* fTo; // "canonical" name we map to
587 } gPairs[] = {
588 { "sans-serif", "Helvetica" },
589 { "serif", "Times" },
590 { "monospace", "Courier" }
591 };
592
593 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
594 if (strcmp(name, gPairs[i].fFrom) == 0) {
595 return gPairs[i].fTo;
596 }
597 }
598 return name; // no change
599}
600
reed@google.com7fdcd442013-07-30 21:25:49 +0000601static SkTypeface* create_typeface(const SkTypeface* familyFace,
602 const char familyName[],
603 SkTypeface::Style style) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000604 if (familyName) {
605 familyName = map_css_names(familyName);
606 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +0000607
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000608 // Clone an existing typeface
609 // TODO: only clone if style matches the familyFace's style...
610 if (familyName == NULL && familyFace != NULL) {
611 familyFace->ref();
612 return const_cast<SkTypeface*>(familyFace);
613 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +0000614
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000615 if (!familyName || !*familyName) {
616 familyName = FONT_DEFAULT_NAME;
617 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +0000618
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000619 NameStyleRec rec = { familyName, style };
620 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec);
skia.committer@gmail.com76015b02013-07-31 07:01:00 +0000621
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000622 if (NULL == face) {
623 face = NewFromName(familyName, style);
624 if (face) {
625 SkTypefaceCache::Add(face, style);
626 } else {
627 face = GetDefaultFace();
628 face->ref();
629 }
630 }
631 return face;
632}
633
reed@google.com7fdcd442013-07-30 21:25:49 +0000634SkTypeface* SkTypeface_Mac::onRefMatchingStyle(Style styleBits) const {
635 return create_typeface(this, NULL, styleBits);
636}
637
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000638///////////////////////////////////////////////////////////////////////////////
639
bungeman@google.comcefd9812013-05-15 15:07:32 +0000640/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000641struct GlyphRect {
642 int16_t fMinX;
643 int16_t fMinY;
644 int16_t fMaxX;
645 int16_t fMaxY;
646};
647
648class SkScalerContext_Mac : public SkScalerContext {
649public:
reed@google.com0da48612013-03-19 16:06:52 +0000650 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000651
652protected:
653 unsigned generateGlyphCount(void) SK_OVERRIDE;
654 uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
655 void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
656 void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
657 void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
658 void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
659 void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
660
661private:
662 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000663
bungeman@google.comcefd9812013-05-15 15:07:32 +0000664 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
665 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000666
667 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
668 *
669 * For use with (and must be called before) generateBBoxes.
670 */
671 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000672
bungeman@google.comcefd9812013-05-15 15:07:32 +0000673 /** Initializes fFBoundingBoxes and returns true on success.
674 *
675 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
676 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
677 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
678 * font directly.
679 *
680 * This routine initializes fFBoundingBoxes to an array of
681 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
682 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
683 *
684 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
685 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
686 *
687 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
688 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000689 bool generateBBoxes();
690
bungeman@google.comcefd9812013-05-15 15:07:32 +0000691 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
692 *
693 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
694 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
695 */
696 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000697
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000698 Offscreen fOffscreen;
699 AutoCFRelease<CTFontRef> fCTFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000700
bungeman@google.comcefd9812013-05-15 15:07:32 +0000701 /** Vertical variant of fCTFont.
702 *
703 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
704 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
705 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
706 * Use fCTVerticalFont with kCTFontVerticalOrientation to get metrics in the same space.
707 */
708 AutoCFRelease<CTFontRef> fCTVerticalFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000709
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000710 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000711 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000712 uint16_t fFBoundingBoxesGlyphOffset;
713 uint16_t fGlyphCount;
714 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000715 const bool fDoSubPosition;
716 const bool fVertical;
717
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000718 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000719
720 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000721};
722
reed@google.com0da48612013-03-19 16:06:52 +0000723SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
724 const SkDescriptor* desc)
725 : INHERITED(typeface, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000726 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000727 , fFBoundingBoxesGlyphOffset(0)
728 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000729 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
730 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
731
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000732{
reed@google.com2689f612013-03-20 20:01:47 +0000733 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000734 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000735 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
736 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000737
bungeman@google.comcefd9812013-05-15 15:07:32 +0000738 fRec.getSingleMatrix(&fFUnitMatrix);
739 CGAffineTransform transform = MatrixToCGAffineTransform(fFUnitMatrix);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000740
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000741 AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
742 if (fVertical) {
743 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
744 kCFAllocatorDefault, 0,
745 &kCFTypeDictionaryKeyCallBacks,
746 &kCFTypeDictionaryValueCallBacks));
747 if (cfAttributes) {
748 CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
749 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
750 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
751 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
752 ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
753 }
754 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000755 // Since our matrix includes everything, we pass 1 for size.
756 fCTFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, ctFontDesc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000757 fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL);
758 if (fVertical) {
759 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
760 transform = CGAffineTransformConcat(rotateLeft, transform);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000761 fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000762 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000763
bungeman@google.comcefd9812013-05-15 15:07:32 +0000764 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
765 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000766}
767
768CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
769 CGGlyph glyphID, size_t* rowBytesPtr,
770 bool generateA8FromLCD) {
771 if (!fRGBSpace) {
772 //It doesn't appear to matter what color space is specified.
773 //Regular blends and antialiased text are always (s*a + d*(1-a))
774 //and smoothed text is always g=2.0.
775 fRGBSpace = CGColorSpaceCreateDeviceRGB();
776 }
777
778 // default to kBW_Format
779 bool doAA = false;
780 bool doLCD = false;
781
782 if (SkMask::kBW_Format != glyph.fMaskFormat) {
783 doLCD = true;
784 doAA = true;
785 }
786
787 // FIXME: lcd smoothed un-hinted rasterization unsupported.
788 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
789 doLCD = false;
790 doAA = true;
791 }
792
793 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
794 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
795 if (fSize.fWidth < glyph.fWidth) {
796 fSize.fWidth = RoundSize(glyph.fWidth);
797 }
798 if (fSize.fHeight < glyph.fHeight) {
799 fSize.fHeight = RoundSize(glyph.fHeight);
800 }
801
802 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
803 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
804 fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
805 rowBytes, fRGBSpace, BITMAP_INFO_RGB);
806
807 // skia handles quantization itself, so we disable this for cg to get
808 // full fractional data from them.
809 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
810 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
811
812 CGContextSetTextDrawingMode(fCG, kCGTextFill);
813 CGContextSetFont(fCG, context.fCGFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000814 CGContextSetFontSize(fCG, 1 /*CTFontGetSize(context.fCTFont)*/);
815 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000816
bungeman@google.comcefd9812013-05-15 15:07:32 +0000817 // Because CG always draws from the horizontal baseline,
818 // if there is a non-integral translation from the horizontal origin to the vertical origin,
819 // then CG cannot draw the glyph in the correct location without subpixel positioning.
820 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical);
821 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || context.fVertical);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000822
823 // Draw white on black to create mask.
824 // TODO: Draw black on white and invert, CG has a special case codepath.
825 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
826
827 // force our checks below to happen
828 fDoAA = !doAA;
829 fDoLCD = !doLCD;
830 }
831
832 if (fDoAA != doAA) {
833 CGContextSetShouldAntialias(fCG, doAA);
834 fDoAA = doAA;
835 }
836 if (fDoLCD != doLCD) {
837 CGContextSetShouldSmoothFonts(fCG, doLCD);
838 fDoLCD = doLCD;
839 }
840
841 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
842 // skip rows based on the glyph's height
843 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
844
845 // erase to black
846 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
847
848 float subX = 0;
849 float subY = 0;
850 if (context.fDoSubPosition) {
851 subX = SkFixedToFloat(glyph.getSubXFixed());
852 subY = SkFixedToFloat(glyph.getSubYFixed());
853 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000854
bungeman@google.comcefd9812013-05-15 15:07:32 +0000855 // CGContextShowGlyphsAtPoint always draws using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000856 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000857 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000858 context.getVerticalOffset(glyphID, &offset);
859 subX += offset.fX;
860 subY += offset.fY;
861 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000862
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000863 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
864 glyph.fTop + glyph.fHeight - subY,
865 &glyphID, 1);
866
867 SkASSERT(rowBytesPtr);
868 *rowBytesPtr = rowBytes;
869 return image;
870}
871
bungeman@google.comcefd9812013-05-15 15:07:32 +0000872void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
873 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
874 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
875 CGSize cgVertOffset;
876 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
877
878 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
879 if (isSnowLeopard()) {
880 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
881 fFUnitMatrix.mapPoints(&skVertOffset, 1);
882 } else {
883 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
884 skVertOffset.fY = -skVertOffset.fY;
885 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000886
bungeman@google.comcefd9812013-05-15 15:07:32 +0000887 *offset = skVertOffset;
888}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000889
890uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
891 if (fFBoundingBoxesGlyphOffset) {
892 return fFBoundingBoxesGlyphOffset;
893 }
894 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
895 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
896 if (hheaTable.fData) {
897 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
898 }
899 return fFBoundingBoxesGlyphOffset;
900}
reed@android.com0680d6c2008-12-19 19:46:15 +0000901
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000902bool SkScalerContext_Mac::generateBBoxes() {
903 if (fGeneratedFBoundingBoxes) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000904 return NULL != fFBoundingBoxes.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000905 }
906 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000907
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000908 AutoCGTable<SkOTTableHead> headTable(fCGFont);
909 if (!headTable.fData) {
910 return false;
911 }
912
913 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
914 if (!locaTable.fData) {
915 return false;
916 }
917
918 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
919 if (!glyfTable.fData) {
920 return false;
921 }
922
923 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000924 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000925
926 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
927 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
928 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
929 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
930 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
931 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
932 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
933 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
934 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
935 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
936 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000937
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000938 return true;
939}
940
941unsigned SkScalerContext_Mac::generateGlyphCount(void) {
942 return fGlyphCount;
943}
944
945uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
946 CGGlyph cgGlyph;
947 UniChar theChar;
948
949 // Validate our parameters and state
950 SkASSERT(uni <= 0x0000FFFF);
951 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
952
953 // Get the glyph
954 theChar = (UniChar) uni;
955
956 if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1)) {
957 cgGlyph = 0;
958 }
959
960 return cgGlyph;
961}
962
963void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
964 this->generateMetrics(glyph);
965}
966
bungeman@google.comcefd9812013-05-15 15:07:32 +0000967void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
968 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
969 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000970
bungeman@google.comcefd9812013-05-15 15:07:32 +0000971 // The following block produces cgAdvance in CG units (pixels, y up).
972 CGSize cgAdvance;
973 if (fVertical) {
974 CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
975 &cgGlyph, &cgAdvance, 1);
976 } else {
977 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
978 &cgGlyph, &cgAdvance, 1);
979 }
980 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
981 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000982
bungeman@google.comcefd9812013-05-15 15:07:32 +0000983 // The following produces skBounds in SkGlyph units (pixels, y down),
984 // or returns early if skBounds would be empty.
985 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000986
bungeman@google.comcefd9812013-05-15 15:07:32 +0000987 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
988 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
989 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
990 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
991 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
992 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
993 // to center the glyph along the vertical baseline and also perform some mysterious shift
994 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
995 // these steps.
996 //
997 // It is not known which is correct (or if either is correct). However, we must always draw
998 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
999 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001000
bungeman@google.comcefd9812013-05-15 15:07:32 +00001001 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
1002 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001003
bungeman@google.comcefd9812013-05-15 15:07:32 +00001004 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
1005 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
1006 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
1007 // font directly.
1008 if ((isLion() || isMountainLion()) &&
1009 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1010 {
1011 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1012 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1013 return;
1014 }
1015 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1016 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1017 fFUnitMatrix.mapRect(&skBounds);
1018
1019 } else {
1020 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1021 CGRect cgBounds;
1022 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1023 &cgGlyph, &cgBounds, 1);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001024
bungeman@google.comcefd9812013-05-15 15:07:32 +00001025 // BUG?
1026 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1027 // it should be empty. So, if we see a zero-advance, we check if it has an
1028 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1029 // is rare, so we won't incur a big performance cost for this extra check.
1030 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
1031 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
1032 if (NULL == path || CGPathIsEmpty(path)) {
1033 return;
1034 }
1035 }
1036
1037 if (CGRectIsEmpty_inline(cgBounds)) {
1038 return;
1039 }
1040
1041 // Convert cgBounds to SkGlyph units (pixels, y down).
1042 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1043 cgBounds.size.width, cgBounds.size.height);
1044 }
1045
1046 if (fVertical) {
1047 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1048 // Convert these horizontal bounds into vertical bounds.
1049 SkPoint offset;
1050 getVerticalOffset(cgGlyph, &offset);
1051 skBounds.offset(offset);
1052 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001053
bungeman@google.comcefd9812013-05-15 15:07:32 +00001054 // Currently the bounds are based on being rendered at (0,0).
1055 // The top left must not move, since that is the base from which subpixel positioning is offset.
1056 if (fDoSubPosition) {
1057 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1058 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1059 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001060
bungeman@google.comcefd9812013-05-15 15:07:32 +00001061 SkIRect skIBounds;
1062 skBounds.roundOut(&skIBounds);
1063 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1064 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1065 // is not currently known, as CG dilates the outlines by some percentage.
1066 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1067 skIBounds.outset(1, 1);
1068 glyph->fLeft = SkToS16(skIBounds.fLeft);
1069 glyph->fTop = SkToS16(skIBounds.fTop);
1070 glyph->fWidth = SkToU16(skIBounds.width());
1071 glyph->fHeight = SkToU16(skIBounds.height());
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001072
bungeman@google.comcefd9812013-05-15 15:07:32 +00001073#ifdef HACK_COLORGLYPHS
1074 glyph->fMaskFormat = SkMask::kARGB32_Format;
1075#endif
1076}
1077
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001078#include "SkColorPriv.h"
1079
1080static void build_power_table(uint8_t table[], float ee) {
1081 for (int i = 0; i < 256; i++) {
1082 float x = i / 255.f;
1083 x = sk_float_pow(x, ee);
1084 int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
1085 table[i] = SkToU8(xx);
1086 }
1087}
1088
1089/**
1090 * This will invert the gamma applied by CoreGraphics, so we can get linear
1091 * values.
1092 *
1093 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1094 * The color space used does not appear to affect this choice.
1095 */
1096static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1097 static bool gInited;
1098 static uint8_t gTableCoreGraphicsSmoothing[256];
1099 if (!gInited) {
1100 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1101 gInited = true;
1102 }
1103 return gTableCoreGraphicsSmoothing;
1104}
1105
1106static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1107 while (count > 0) {
1108 uint8_t mask = 0;
1109 for (int i = 7; i >= 0; --i) {
1110 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1111 if (0 == --count) {
1112 break;
1113 }
1114 }
1115 *dst++ = mask;
1116 }
1117}
1118
1119template<bool APPLY_PREBLEND>
1120static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1121 U8CPU r = (rgb >> 16) & 0xFF;
1122 U8CPU g = (rgb >> 8) & 0xFF;
1123 U8CPU b = (rgb >> 0) & 0xFF;
1124 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1125}
1126template<bool APPLY_PREBLEND>
1127static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1128 const SkGlyph& glyph, const uint8_t* table8) {
1129 const int width = glyph.fWidth;
1130 size_t dstRB = glyph.rowBytes();
1131 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1132
1133 for (int y = 0; y < glyph.fHeight; y++) {
1134 for (int i = 0; i < width; ++i) {
1135 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1136 }
1137 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1138 dst += dstRB;
1139 }
1140}
1141
1142template<bool APPLY_PREBLEND>
1143static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1144 const uint8_t* tableG,
1145 const uint8_t* tableB) {
1146 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1147 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1148 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1149 return SkPack888ToRGB16(r, g, b);
1150}
1151template<bool APPLY_PREBLEND>
1152static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1153 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1154 const int width = glyph.fWidth;
1155 size_t dstRB = glyph.rowBytes();
1156 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1157
1158 for (int y = 0; y < glyph.fHeight; y++) {
1159 for (int i = 0; i < width; i++) {
1160 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1161 }
1162 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1163 dst = (uint16_t*)((char*)dst + dstRB);
1164 }
1165}
1166
1167template<bool APPLY_PREBLEND>
1168static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
1169 const uint8_t* tableG,
1170 const uint8_t* tableB) {
1171 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1172 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1173 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1174 return SkPackARGB32(0xFF, r, g, b);
1175}
1176template<bool APPLY_PREBLEND>
1177static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1178 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1179 const int width = glyph.fWidth;
1180 size_t dstRB = glyph.rowBytes();
1181 uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
1182 for (int y = 0; y < glyph.fHeight; y++) {
1183 for (int i = 0; i < width; i++) {
1184 dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1185 }
1186 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1187 dst = (uint32_t*)((char*)dst + dstRB);
1188 }
1189}
1190
reed@google.comf77b35d2013-05-02 20:39:44 +00001191#ifdef HACK_COLORGLYPHS
1192// hack to colorize the output for testing kARGB32_Format
1193static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb, const SkGlyph& glyph,
1194 int x, int y) {
1195 U8CPU r = (rgb >> 16) & 0xFF;
1196 U8CPU g = (rgb >> 8) & 0xFF;
1197 U8CPU b = (rgb >> 0) & 0xFF;
1198 unsigned a = SkComputeLuminance(r, g, b);
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +00001199
reed@google.comf77b35d2013-05-02 20:39:44 +00001200 // compute gradient from x,y
1201 r = x * 255 / glyph.fWidth;
1202 g = 0;
1203 b = (glyph.fHeight - y) * 255 / glyph.fHeight;
1204 return SkPreMultiplyARGB(a, r, g, b); // red
1205}
1206#endif
1207
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001208template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1209 return (T*)((char*)ptr + byteOffset);
1210}
1211
1212void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
1213 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
1214
1215 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1216 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1217
1218 // Draw the glyph
1219 size_t cgRowBytes;
1220 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
1221 if (cgPixels == NULL) {
1222 return;
1223 }
1224
1225 //TODO: see if drawing black on white and inverting is faster (at least in
1226 //lcd case) as core graphics appears to have special case code for drawing
1227 //black text.
1228
1229 // Fix the glyph
1230 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1231 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1232 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1233
1234 //Note that the following cannot really be integrated into the
1235 //pre-blend, since we may not be applying the pre-blend; when we aren't
1236 //applying the pre-blend it means that a filter wants linear anyway.
1237 //Other code may also be applying the pre-blend, so we'd need another
1238 //one with this and one without.
1239 CGRGBPixel* addr = cgPixels;
1240 for (int y = 0; y < glyph.fHeight; ++y) {
1241 for (int x = 0; x < glyph.fWidth; ++x) {
1242 int r = (addr[x] >> 16) & 0xFF;
1243 int g = (addr[x] >> 8) & 0xFF;
1244 int b = (addr[x] >> 0) & 0xFF;
1245 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1246 }
1247 addr = SkTAddByteOffset(addr, cgRowBytes);
1248 }
1249 }
1250
1251 // Convert glyph to mask
1252 switch (glyph.fMaskFormat) {
1253 case SkMask::kLCD32_Format: {
1254 if (fPreBlend.isApplicable()) {
1255 rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
1256 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1257 } else {
1258 rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
1259 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1260 }
1261 } break;
1262 case SkMask::kLCD16_Format: {
1263 if (fPreBlend.isApplicable()) {
1264 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1265 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1266 } else {
1267 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1268 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1269 }
1270 } break;
1271 case SkMask::kA8_Format: {
1272 if (fPreBlend.isApplicable()) {
1273 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1274 } else {
1275 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1276 }
1277 } break;
1278 case SkMask::kBW_Format: {
1279 const int width = glyph.fWidth;
1280 size_t dstRB = glyph.rowBytes();
1281 uint8_t* dst = (uint8_t*)glyph.fImage;
1282 for (int y = 0; y < glyph.fHeight; y++) {
1283 cgpixels_to_bits(dst, cgPixels, width);
1284 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1285 dst += dstRB;
1286 }
1287 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001288#ifdef HACK_COLORGLYPHS
1289 case SkMask::kARGB32_Format: {
1290 const int width = glyph.fWidth;
1291 size_t dstRB = glyph.rowBytes();
1292 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1293 for (int y = 0; y < glyph.fHeight; y++) {
1294 for (int x = 0; x < width; ++x) {
1295 dst[x] = cgpixels_to_pmcolor(cgPixels[x], glyph, x, y);
1296 }
1297 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1298 dst = (SkPMColor*)((char*)dst + dstRB);
1299 }
1300 } break;
1301#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001302 default:
1303 SkDEBUGFAIL("unexpected mask format");
1304 break;
1305 }
1306}
1307
1308/*
1309 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1310 * seems sufficient, and possibly even correct, to allow the hinted outline
1311 * to be subpixel positioned.
1312 */
1313#define kScaleForSubPixelPositionHinting (4.0f)
1314
1315void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
1316 CTFontRef font = fCTFont;
1317 SkScalar scaleX = SK_Scalar1;
1318 SkScalar scaleY = SK_Scalar1;
1319
1320 /*
1321 * For subpixel positioning, we want to return an unhinted outline, so it
1322 * can be positioned nicely at fractional offsets. However, we special-case
1323 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1324 * we want to retain hinting in the direction orthogonal to the baseline.
1325 * e.g. for horizontal baseline, we want to retain hinting in Y.
1326 * The way we remove hinting is to scale the font by some value (4) in that
1327 * direction, ask for the path, and then scale the path back down.
1328 */
1329 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1330 SkMatrix m;
1331 fRec.getSingleMatrix(&m);
1332
1333 // start out by assuming that we want no hining in X and Y
1334 scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
1335 // now see if we need to restore hinting for axis-aligned baselines
1336 switch (SkComputeAxisAlignmentForHText(m)) {
1337 case kX_SkAxisAlignment:
1338 scaleY = SK_Scalar1; // want hinting in the Y direction
1339 break;
1340 case kY_SkAxisAlignment:
1341 scaleX = SK_Scalar1; // want hinting in the X direction
1342 break;
1343 default:
1344 break;
1345 }
1346
1347 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
1348 // need to release font when we're done
1349 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
1350 }
1351
1352 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
1353 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
1354
1355 path->reset();
1356 if (cgPath != NULL) {
1357 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1358 }
1359
bungeman@google.comcefd9812013-05-15 15:07:32 +00001360 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001361 SkMatrix m;
1362 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1363 path->transform(m);
1364 // balance the call to CTFontCreateCopyWithAttributes
1365 CFSafeRelease(font);
1366 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001367 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001368 SkPoint offset;
1369 getVerticalOffset(cgGlyph, &offset);
1370 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001371 }
1372}
1373
1374void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
1375 SkPaint::FontMetrics* my) {
1376 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1377
1378 SkPaint::FontMetrics theMetrics;
1379 theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1380 theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1381 theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1382 theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1383 theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1384 theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1385 theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1386 theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
1387 theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
1388
1389 if (mx != NULL) {
1390 *mx = theMetrics;
1391 }
1392 if (my != NULL) {
1393 *my = theMetrics;
1394 }
1395}
1396
1397void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1398 SkPath* skPath = (SkPath*)info;
1399
1400 // Process the path element
1401 switch (element->type) {
1402 case kCGPathElementMoveToPoint:
1403 skPath->moveTo(element->points[0].x, -element->points[0].y);
1404 break;
1405
1406 case kCGPathElementAddLineToPoint:
1407 skPath->lineTo(element->points[0].x, -element->points[0].y);
1408 break;
1409
1410 case kCGPathElementAddQuadCurveToPoint:
1411 skPath->quadTo(element->points[0].x, -element->points[0].y,
1412 element->points[1].x, -element->points[1].y);
1413 break;
1414
1415 case kCGPathElementAddCurveToPoint:
1416 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1417 element->points[1].x, -element->points[1].y,
1418 element->points[2].x, -element->points[2].y);
1419 break;
1420
1421 case kCGPathElementCloseSubpath:
1422 skPath->close();
1423 break;
1424
1425 default:
1426 SkDEBUGFAIL("Unknown path element!");
1427 break;
1428 }
1429}
1430
1431
1432///////////////////////////////////////////////////////////////////////////////
1433
1434// Returns NULL on failure
1435// Call must still manage its ownership of provider
1436static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1437 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
1438 if (NULL == cg) {
1439 return NULL;
1440 }
1441 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
1442 return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
1443}
1444
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001445// Web fonts added to the the CTFont registry do not return their character set.
1446// Iterate through the font in this case. The existing caller caches the result,
1447// so the performance impact isn't too bad.
1448static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1449 SkTDArray<SkUnichar>* glyphToUnicode) {
1450 glyphToUnicode->setCount(glyphCount);
1451 SkUnichar* out = glyphToUnicode->begin();
1452 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1453 UniChar unichar = 0;
1454 while (glyphCount > 0) {
1455 CGGlyph glyph;
1456 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1457 out[glyph] = unichar;
1458 --glyphCount;
1459 }
1460 if (++unichar == 0) {
1461 break;
1462 }
1463 }
1464}
1465
1466// Construct Glyph to Unicode table.
1467// Unicode code points that require conjugate pairs in utf16 are not
1468// supported.
1469static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1470 SkTDArray<SkUnichar>* glyphToUnicode) {
1471 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1472 if (!charSet) {
1473 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1474 return;
1475 }
1476
1477 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1478 charSet));
1479 if (!bitmap) {
1480 return;
1481 }
1482 CFIndex length = CFDataGetLength(bitmap);
1483 if (!length) {
1484 return;
1485 }
1486 if (length > 8192) {
1487 // TODO: Add support for Unicode above 0xFFFF
1488 // Consider only the BMP portion of the Unicode character points.
1489 // The bitmap may contain other planes, up to plane 16.
1490 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1491 length = 8192;
1492 }
1493 const UInt8* bits = CFDataGetBytePtr(bitmap);
1494 glyphToUnicode->setCount(glyphCount);
1495 SkUnichar* out = glyphToUnicode->begin();
1496 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1497 for (int i = 0; i < length; i++) {
1498 int mask = bits[i];
1499 if (!mask) {
1500 continue;
1501 }
1502 for (int j = 0; j < 8; j++) {
1503 CGGlyph glyph;
1504 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1505 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1506 out[glyph] = unichar;
1507 }
1508 }
1509 }
1510}
1511
1512static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1513 CGSize advance;
1514 advance.width = 0;
1515 CGGlyph glyph = gId;
1516 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1517 *data = sk_float_round2int(advance.width);
1518 return true;
1519}
1520
1521// we might move this into our CGUtils...
1522static void CFStringToSkString(CFStringRef src, SkString* dst) {
1523 // Reserve enough room for the worst-case string,
1524 // plus 1 byte for the trailing null.
1525 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1526 kCFStringEncodingUTF8) + 1;
1527 dst->resize(length);
1528 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1529 // Resize to the actual UTF-8 length used, stripping the null character.
1530 dst->resize(strlen(dst->c_str()));
1531}
1532
reed@google.com2689f612013-03-20 20:01:47 +00001533SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001534 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1535 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001536 uint32_t glyphIDsCount) const {
1537
1538 CTFontRef originalCTFont = fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001539 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
1540 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
1541 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1542
1543 {
1544 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
1545 CFStringToSkString(fontName, &info->fFontName);
1546 }
1547
1548 info->fMultiMaster = false;
1549 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1550 info->fLastGlyphID = SkToU16(glyphCount - 1);
1551 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1552
1553 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1554 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1555 }
1556
1557 info->fStyle = 0;
1558
1559 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1560 // fonts always have both glyf and loca tables. At the least, this is what
1561 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1562 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001563 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001564 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1565 info->fItalicAngle = 0;
1566 info->fAscent = 0;
1567 info->fDescent = 0;
1568 info->fStemV = 0;
1569 info->fCapHeight = 0;
1570 info->fBBox = SkIRect::MakeEmpty();
1571 return info;
1572 }
1573
1574 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1575 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1576 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1577 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1578 }
1579 if (symbolicTraits & kCTFontItalicTrait) {
1580 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1581 }
1582 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001583 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1584 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1585 } else if (stylisticClass & kCTFontScriptsClass) {
1586 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1587 }
1588 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1589 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1590 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1591 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1592 CGRect bbox = CTFontGetBoundingBox(ctFont);
1593
1594 SkRect r;
1595 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1596 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1597 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1598 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1599
1600 r.roundOut(&(info->fBBox));
1601
1602 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1603 // This probably isn't very good with an italic font.
1604 int16_t min_width = SHRT_MAX;
1605 info->fStemV = 0;
1606 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1607 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1608 CGGlyph glyphs[count];
1609 CGRect boundingRects[count];
1610 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1611 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1612 glyphs, boundingRects, count);
1613 for (size_t i = 0; i < count; i++) {
1614 int16_t width = (int16_t) boundingRects[i].size.width;
1615 if (width > 0 && width < min_width) {
1616 min_width = width;
1617 info->fStemV = min_width;
1618 }
1619 }
1620 }
1621
1622 if (false) { // TODO: haven't figured out how to know if font is embeddable
1623 // (information is in the OS/2 table)
1624 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
1625 } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
1626 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1627 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1628 info->fGlyphWidths->fAdvance.append(1, &min_width);
1629 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1630 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1631 } else {
1632 info->fGlyphWidths.reset(
1633 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
1634 glyphCount,
1635 glyphIDs,
1636 glyphIDsCount,
1637 &getWidthAdvance));
1638 }
1639 }
1640 return info;
1641}
1642
1643///////////////////////////////////////////////////////////////////////////////
1644
reed@google.comcc9aad52013-03-21 19:28:10 +00001645static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1646 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001647 AutoCFRelease<CFNumberRef> fontFormatRef(
1648 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1649 if (!fontFormatRef) {
1650 return 0;
1651 }
1652
1653 SInt32 fontFormatValue;
1654 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1655 return 0;
1656 }
1657
1658 switch (fontFormatValue) {
1659 case kCTFontFormatOpenTypePostScript:
1660 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1661 case kCTFontFormatOpenTypeTrueType:
1662 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1663 case kCTFontFormatTrueType:
1664 return SkSFNTHeader::fontType_MacTrueType::TAG;
1665 case kCTFontFormatPostScript:
1666 return SkSFNTHeader::fontType_PostScript::TAG;
1667 case kCTFontFormatBitmap:
1668 return SkSFNTHeader::fontType_MacTrueType::TAG;
1669 case kCTFontFormatUnrecognized:
1670 default:
1671 //CT seems to be unreliable in being able to obtain the type,
1672 //even if all we want is the first four bytes of the font resource.
1673 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1674 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1675 }
1676}
1677
reed@google.comcc9aad52013-03-21 19:28:10 +00001678SkStream* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
1679 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001680 if (0 == fontType) {
1681 return NULL;
1682 }
1683
1684 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001685 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001686 SkTDArray<SkFontTableTag> tableTags;
1687 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001688 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001689
1690 // calc total size for font, save sizes
1691 SkTDArray<size_t> tableSizes;
1692 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1693 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001694 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001695 totalSize += (tableSize + 3) & ~3;
1696 *tableSizes.append() = tableSize;
1697 }
1698
1699 // reserve memory for stream, and zero it (tables must be zero padded)
1700 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1701 char* dataStart = (char*)stream->getMemoryBase();
1702 sk_bzero(dataStart, totalSize);
1703 char* dataPtr = dataStart;
1704
1705 // compute font header entries
1706 uint16_t entrySelector = 0;
1707 uint16_t searchRange = 1;
1708 while (searchRange < numTables >> 1) {
1709 entrySelector++;
1710 searchRange <<= 1;
1711 }
1712 searchRange <<= 4;
1713 uint16_t rangeShift = (numTables << 4) - searchRange;
1714
1715 // write font header
1716 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1717 header->fontType = fontType;
1718 header->numTables = SkEndian_SwapBE16(numTables);
1719 header->searchRange = SkEndian_SwapBE16(searchRange);
1720 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1721 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1722 dataPtr += sizeof(SkSFNTHeader);
1723
1724 // write tables
1725 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1726 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1727 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1728 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001729 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001730 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1731 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1732 tableSize));
1733 entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
1734 entry->logicalLength = SkEndian_SwapBE32(tableSize);
1735
1736 dataPtr += (tableSize + 3) & ~3;
1737 ++entry;
1738 }
1739
1740 return stream;
1741}
1742
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001743///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001744///////////////////////////////////////////////////////////////////////////////
1745
1746int SkTypeface_Mac::onGetUPEM() const {
1747 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1748 return CGFontGetUnitsPerEm(cgFont);
1749}
1750
1751// If, as is the case with web fonts, the CTFont data isn't available,
1752// the CGFont data may work. While the CGFont may always provide the
1753// right result, leave the CTFont code path to minimize disruption.
1754static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1755 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1756 kCTFontTableOptionNoOptions);
1757 if (NULL == data) {
1758 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
1759 data = CGFontCopyTableForTag(cgFont, tag);
1760 }
1761 return data;
1762}
1763
1764int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1765 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1766 kCTFontTableOptionNoOptions));
1767 if (NULL == cfArray) {
1768 return 0;
1769 }
1770 int count = CFArrayGetCount(cfArray);
1771 if (tags) {
1772 for (int i = 0; i < count; ++i) {
1773 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1774 tags[i] = static_cast<SkFontTableTag>(fontTag);
1775 }
1776 }
1777 return count;
1778}
1779
1780size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1781 size_t length, void* dstData) const {
1782 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
1783 if (NULL == srcData) {
1784 return 0;
1785 }
1786
1787 size_t srcSize = CFDataGetLength(srcData);
1788 if (offset >= srcSize) {
1789 return 0;
1790 }
1791 if (length > srcSize - offset) {
1792 length = srcSize - offset;
1793 }
1794 if (dstData) {
1795 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1796 }
1797 return length;
1798}
1799
1800SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001801 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001802}
1803
1804void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001805 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1806 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1807 {
1808 rec->fMaskFormat = SkMask::kA8_Format;
1809 // Render the glyphs as close as possible to what was requested.
1810 // The above turns off subpixel rendering, but the user requested it.
1811 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1812 // See comments below for more details.
1813 rec->setHinting(SkPaint::kNormal_Hinting);
1814 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001815
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001816 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
1817 SkScalerContext::kAutohinting_Flag |
1818 SkScalerContext::kLCD_BGROrder_Flag |
1819 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001820
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001821 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001822
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001823 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001824
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001825 // Only two levels of hinting are supported.
1826 // kNo_Hinting means avoid CoreGraphics outline dilation.
1827 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1828 // If there is no lcd support, hinting (dilation) cannot be supported.
1829 SkPaint::Hinting hinting = rec->getHinting();
1830 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1831 hinting = SkPaint::kNo_Hinting;
1832 } else if (SkPaint::kFull_Hinting == hinting) {
1833 hinting = SkPaint::kNormal_Hinting;
1834 }
1835 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001836
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001837 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1838 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1839 // There is no current means to honor a request for unhinted lcd,
1840 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001841
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001842 // Hinting and smoothing should be orthogonal, but currently they are not.
1843 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
1844 // output is drawn from auto-dilated outlines (the amount of which is
1845 // determined by AppleFontSmoothing). Its regular anti-aliased output is
1846 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001847
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001848 // The behavior of Skia is as follows:
1849 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
1850 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
1851 // channel. This matches [LCD][yes-hint] in weight.
1852 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
1853 // Currenly side with LCD, effectively ignoring the hinting setting.
1854 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001855
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001856 if (isLCDFormat(rec->fMaskFormat)) {
1857 if (lcdSupport) {
1858 //CoreGraphics creates 555 masks for smoothed text anyway.
1859 rec->fMaskFormat = SkMask::kLCD16_Format;
1860 rec->setHinting(SkPaint::kNormal_Hinting);
1861 } else {
1862 rec->fMaskFormat = SkMask::kA8_Format;
1863 }
1864 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001865
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001866 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1867 // All other masks can use regular gamma.
1868 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
1869#ifndef SK_GAMMA_APPLY_TO_A8
1870 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00001871#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001872 } else {
1873 //CoreGraphics dialates smoothed text as needed.
1874 rec->setContrast(0);
1875 }
1876}
1877
1878// we take ownership of the ref
1879static const char* get_str(CFStringRef ref, SkString* str) {
1880 CFStringToSkString(ref, str);
1881 CFSafeRelease(ref);
1882 return str->c_str();
1883}
1884
reed@google.com5526ede2013-03-25 13:03:37 +00001885void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
1886 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001887 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001888
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001889 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
1890 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
1891 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
reed@google.com5526ede2013-03-25 13:03:37 +00001892 // TODO: need to add support for local-streams (here and openStream)
1893 *isLocalStream = false;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001894}
reed@google.com5526ede2013-03-25 13:03:37 +00001895
reed@google.combcb42ae2013-07-02 13:56:39 +00001896int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
1897 uint16_t glyphs[], int glyphCount) const {
1898 // UniChar is utf16
1899 SkAutoSTMalloc<1024, UniChar> charStorage;
1900 const UniChar* src;
1901 switch (encoding) {
1902 case kUTF8_Encoding: {
1903 const char* u8 = (const char*)chars;
1904 const UniChar* u16 = src = charStorage.reset(2 * glyphCount);
1905 for (int i = 0; i < glyphCount; ++i) {
1906 SkUnichar uni = SkUTF8_NextUnichar(&u8);
1907 int n = SkUTF16_FromUnichar(uni, (uint16_t*)u16);
1908 u16 += n;
1909 }
1910 break;
1911 }
1912 case kUTF16_Encoding:
1913 src = (const UniChar*)chars;
1914 break;
1915 case kUTF32_Encoding: {
1916 const SkUnichar* u32 = (const SkUnichar*)chars;
1917 const UniChar* u16 = src = charStorage.reset(2 * glyphCount);
1918 for (int i = 0; i < glyphCount; ++i) {
1919 int n = SkUTF16_FromUnichar(u32[i], (uint16_t*)u16);
1920 u16 += n;
1921 }
1922 break;
1923 }
1924 }
1925
1926 // Our caller may not want glyphs for output, but we need to give that
1927 // storage to CT, so we can walk it looking for the first non-zero.
1928 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
1929 uint16_t* macGlyphs = glyphs;
1930 if (NULL == macGlyphs) {
1931 macGlyphs = glyphStorage.reset(glyphCount);
1932 }
1933
1934 if (CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, glyphCount)) {
1935 return glyphCount;
1936 }
1937 // If we got false, then we need to manually look for first failure
1938 for (int i = 0; i < glyphCount; ++i) {
1939 if (0 == macGlyphs[i]) {
1940 return i;
1941 }
1942 }
1943 // odd to get here, as we expected CT to have returned true up front.
1944 return glyphCount;
1945}
1946
1947int SkTypeface_Mac::onCountGlyphs() const {
1948 return CTFontGetGlyphCount(fFontRef);
1949}
1950
reed@google.com95625db2013-03-25 20:44:02 +00001951///////////////////////////////////////////////////////////////////////////////
1952///////////////////////////////////////////////////////////////////////////////
reed@google.com95625db2013-03-25 20:44:02 +00001953#if 1
reed@google.com83787c52013-03-26 17:19:15 +00001954
1955static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
1956 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
1957 if (NULL == ref.get()) {
1958 return false;
1959 }
1960 CFStringToSkString(ref, value);
1961 return true;
1962}
1963
1964static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
1965 CFNumberRef num;
1966 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
1967 && CFNumberIsFloatType(num)
1968 && CFNumberGetValue(num, kCFNumberFloatType, value);
1969}
1970
reed@google.com95625db2013-03-25 20:44:02 +00001971#include "SkFontMgr.h"
1972
reed@google.com83787c52013-03-26 17:19:15 +00001973static int unit_weight_to_fontstyle(float unit) {
1974 float value;
1975 if (unit < 0) {
1976 value = 100 + (1 + unit) * 300;
1977 } else {
1978 value = 400 + unit * 500;
1979 }
1980 return sk_float_round2int(value);
1981}
1982
1983static int unit_width_to_fontstyle(float unit) {
1984 float value;
1985 if (unit < 0) {
1986 value = 1 + (1 + unit) * 4;
1987 } else {
1988 value = 5 + unit * 4;
1989 }
1990 return sk_float_round2int(value);
1991}
1992
reed@google.com964988f2013-03-29 14:57:22 +00001993static inline int sqr(int value) {
1994 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
1995 return value * value;
1996}
1997
1998// We normalize each axis (weight, width, italic) to be base-900
1999static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2000 return sqr(a.weight() - b.weight()) +
2001 sqr((a.width() - b.width()) * 100) +
2002 sqr((a.isItalic() != b.isItalic()) * 900);
2003}
2004
reed@google.com83787c52013-03-26 17:19:15 +00002005static SkFontStyle desc2fontstyle(CTFontDescriptorRef desc) {
2006 AutoCFRelease<CFDictionaryRef> dict(
2007 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc,
2008 kCTFontTraitsAttribute));
2009 if (NULL == dict.get()) {
2010 return SkFontStyle();
2011 }
2012
2013 float weight, width, slant;
2014 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
2015 weight = 0;
2016 }
2017 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
2018 width = 0;
2019 }
2020 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
2021 slant = 0;
2022 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002023
reed@google.com83787c52013-03-26 17:19:15 +00002024 return SkFontStyle(unit_weight_to_fontstyle(weight),
2025 unit_width_to_fontstyle(width),
2026 slant ? SkFontStyle::kItalic_Slant
2027 : SkFontStyle::kUpright_Slant);
2028}
2029
reed@google.comdea7ee02013-03-28 14:12:10 +00002030struct NameFontStyleRec {
2031 SkString fFamilyName;
2032 SkFontStyle fFontStyle;
2033};
2034
2035static bool nameFontStyleProc(SkTypeface* face, SkTypeface::Style,
2036 void* ctx) {
2037 SkTypeface_Mac* macFace = (SkTypeface_Mac*)face;
2038 const NameFontStyleRec* rec = (const NameFontStyleRec*)ctx;
2039
2040 return macFace->fFontStyle == rec->fFontStyle &&
2041 macFace->fName == rec->fFamilyName;
2042}
2043
reed@google.comce8b3de2013-03-26 19:30:16 +00002044static SkTypeface* createFromDesc(CFStringRef cfFamilyName,
2045 CTFontDescriptorRef desc) {
reed@google.comdea7ee02013-03-28 14:12:10 +00002046 NameFontStyleRec rec;
2047 CFStringToSkString(cfFamilyName, &rec.fFamilyName);
2048 rec.fFontStyle = desc2fontstyle(desc);
2049
2050 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(nameFontStyleProc,
2051 &rec);
2052 if (face) {
2053 return face;
2054 }
2055
reed@google.comce8b3de2013-03-26 19:30:16 +00002056 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFamilyName, 1, NULL));
2057 CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc);
2058 if (NULL == ctFont) {
2059 return NULL;
2060 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002061
reed@google.comce8b3de2013-03-26 19:30:16 +00002062 SkString str;
2063 CFStringToSkString(cfFamilyName, &str);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002064
reed@google.comce8b3de2013-03-26 19:30:16 +00002065 bool isFixedPitch;
2066 (void)computeStyleBits(ctFont, &isFixedPitch);
2067 SkFontID fontID = CTFontRef_to_SkFontID(ctFont);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002068
reed@google.comdea7ee02013-03-28 14:12:10 +00002069 face = SkNEW_ARGS(SkTypeface_Mac, (rec.fFontStyle, fontID, isFixedPitch,
2070 ctFont, str.c_str()));
2071 SkTypefaceCache::Add(face, face->style());
2072 return face;
reed@google.comce8b3de2013-03-26 19:30:16 +00002073}
2074
reed@google.com83787c52013-03-26 17:19:15 +00002075class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002076public:
reed@google.com83787c52013-03-26 17:19:15 +00002077 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
2078 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, NULL))
reed@google.comdea7ee02013-03-28 14:12:10 +00002079 , fFamilyName(familyName)
2080 , fCount(0) {
reed@google.com83787c52013-03-26 17:19:15 +00002081 CFRetain(familyName);
reed@google.comdea7ee02013-03-28 14:12:10 +00002082 if (NULL == fArray) {
2083 fArray = CFArrayCreate(NULL, NULL, 0, NULL);
2084 }
2085 fCount = CFArrayGetCount(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002086 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002087
reed@google.com83787c52013-03-26 17:19:15 +00002088 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002089 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002090 CFRelease(fFamilyName);
2091 }
2092
2093 virtual int count() SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002094 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002095 }
2096
2097 virtual void getStyle(int index, SkFontStyle* style,
2098 SkString* name) SK_OVERRIDE {
reed@google.comdea7ee02013-03-28 14:12:10 +00002099 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002100 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2101 if (style) {
2102 *style = desc2fontstyle(desc);
2103 }
2104 if (name) {
2105 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2106 name->reset();
2107 }
2108 }
2109 }
2110
2111 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE {
2112 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2113 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002114
reed@google.com83787c52013-03-26 17:19:15 +00002115 return createFromDesc(fFamilyName, desc);
2116 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002117
reed@google.com964988f2013-03-29 14:57:22 +00002118 virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE {
2119 if (0 == fCount) {
2120 return NULL;
2121 }
2122 return createFromDesc(fFamilyName, findMatchingDesc(pattern));
2123 }
2124
reed@google.com83787c52013-03-26 17:19:15 +00002125private:
2126 CFArrayRef fArray;
2127 CFStringRef fFamilyName;
reed@google.comdea7ee02013-03-28 14:12:10 +00002128 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002129
2130 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2131 int bestMetric = SK_MaxS32;
2132 CTFontDescriptorRef bestDesc = NULL;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002133
reed@google.com964988f2013-03-29 14:57:22 +00002134 for (int i = 0; i < fCount; ++i) {
2135 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
2136 int metric = compute_metric(pattern, desc2fontstyle(desc));
2137 if (0 == metric) {
2138 return desc;
2139 }
2140 if (metric < bestMetric) {
2141 bestMetric = metric;
2142 bestDesc = desc;
2143 }
2144 }
2145 SkASSERT(bestDesc);
2146 return bestDesc;
2147 }
reed@google.com83787c52013-03-26 17:19:15 +00002148};
2149
2150class SkFontMgr_Mac : public SkFontMgr {
2151 int fCount;
2152 CFArrayRef fNames;
2153
2154 CFStringRef stringAt(int index) const {
2155 SkASSERT((unsigned)index < (unsigned)fCount);
2156 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2157 }
2158
2159 void lazyInit() {
2160 if (NULL == fNames) {
reed@google.com3dcbd462013-03-27 13:56:34 +00002161 fNames = SkCTFontManagerCopyAvailableFontFamilyNames();
reed@google.com83787c52013-03-26 17:19:15 +00002162 fCount = fNames ? CFArrayGetCount(fNames) : 0;
2163 }
2164 }
2165
reed@google.com964988f2013-03-29 14:57:22 +00002166 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2167 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2168 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2169 &kCFTypeDictionaryKeyCallBacks,
2170 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002171
reed@google.com964988f2013-03-29 14:57:22 +00002172 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002173
reed@google.com964988f2013-03-29 14:57:22 +00002174 AutoCFRelease<CTFontDescriptorRef> desc(
2175 CTFontDescriptorCreateWithAttributes(cfAttr));
2176 return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc));
2177 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002178
reed@google.com83787c52013-03-26 17:19:15 +00002179public:
2180 SkFontMgr_Mac() : fCount(0), fNames(NULL) {}
2181
2182 virtual ~SkFontMgr_Mac() {
2183 CFSafeRelease(fNames);
2184 }
reed@google.com95625db2013-03-25 20:44:02 +00002185
2186protected:
2187 virtual int onCountFamilies() SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002188 this->lazyInit();
2189 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002190 }
2191
2192 virtual void onGetFamilyName(int index, SkString* familyName) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002193 this->lazyInit();
2194 if ((unsigned)index < (unsigned)fCount) {
2195 CFStringToSkString(this->stringAt(index), familyName);
2196 } else {
2197 familyName->reset();
2198 }
reed@google.com95625db2013-03-25 20:44:02 +00002199 }
2200
2201 virtual SkFontStyleSet* onCreateStyleSet(int index) SK_OVERRIDE {
reed@google.com83787c52013-03-26 17:19:15 +00002202 this->lazyInit();
2203 if ((unsigned)index >= (unsigned)fCount) {
2204 return NULL;
2205 }
reed@google.com964988f2013-03-29 14:57:22 +00002206 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002207 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002208
reed@google.com964988f2013-03-29 14:57:22 +00002209 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) SK_OVERRIDE {
2210 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2211 return CreateSet(cfName);
2212 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002213
reed@google.com95625db2013-03-25 20:44:02 +00002214 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
2215 const SkFontStyle&) SK_OVERRIDE {
2216 return NULL;
2217 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002218
reed@google.com95625db2013-03-25 20:44:02 +00002219 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2220 const SkFontStyle&) SK_OVERRIDE {
2221 return NULL;
2222 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002223
reed@google.com95625db2013-03-25 20:44:02 +00002224 virtual SkTypeface* onCreateFromData(SkData* data,
2225 int ttcIndex) SK_OVERRIDE {
2226 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
2227 if (NULL == pr) {
2228 return NULL;
2229 }
2230 return create_from_dataProvider(pr);
2231 }
2232
2233 virtual SkTypeface* onCreateFromStream(SkStream* stream,
2234 int ttcIndex) SK_OVERRIDE {
2235 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
2236 if (NULL == pr) {
2237 return NULL;
2238 }
2239 return create_from_dataProvider(pr);
2240 }
2241
2242 virtual SkTypeface* onCreateFromFile(const char path[],
2243 int ttcIndex) SK_OVERRIDE {
2244 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2245 if (NULL == pr) {
2246 return NULL;
2247 }
2248 return create_from_dataProvider(pr);
2249 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002250
reed@google.com7fdcd442013-07-30 21:25:49 +00002251 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
2252 unsigned styleBits) SK_OVERRIDE {
2253 return create_typeface(NULL, familyName, (SkTypeface::Style)styleBits);
2254 }
reed@google.com95625db2013-03-25 20:44:02 +00002255};
2256
reed@google.com7fdcd442013-07-30 21:25:49 +00002257///////////////////////////////////////////////////////////////////////////////
2258
2259#ifndef SK_FONTHOST_USES_FONTMGR
2260
2261SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
2262 const char familyName[],
2263 SkTypeface::Style style) {
2264 return create_typeface(familyFace, familyName, style);
2265}
2266
2267SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
2268 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
2269 if (NULL == provider) {
2270 return NULL;
2271 }
2272 return create_from_dataProvider(provider);
2273}
2274
2275SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
2276 AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
2277 if (NULL == provider) {
2278 return NULL;
2279 }
2280 return create_from_dataProvider(provider);
2281}
2282
2283#endif
2284
reed@google.com95625db2013-03-25 20:44:02 +00002285SkFontMgr* SkFontMgr::Factory() {
2286 return SkNEW(SkFontMgr_Mac);
2287}
2288#endif