blob: 5ad1c2fb5ea5ca0b0211df61fe723f0ad4a767fe [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
mtkleinde9e7a72015-03-11 12:01:25 -07009#include "SkTypes.h" // Keep this before any #ifdef ...
10
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000011#ifdef SK_BUILD_FOR_MAC
12#import <ApplicationServices/ApplicationServices.h>
13#endif
reed@android.com0680d6c2008-12-19 19:46:15 +000014
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000015#ifdef SK_BUILD_FOR_IOS
16#include <CoreText/CoreText.h>
reed@google.com3dcbd462013-03-27 13:56:34 +000017#include <CoreText/CTFontManager.h>
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000018#include <CoreGraphics/CoreGraphics.h>
19#include <CoreFoundation/CoreFoundation.h>
20#endif
21
reed39a9a502015-05-12 09:50:04 -070022#include "SkAdvancedTypefaceMetrics.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000023#include "SkCGUtils.h"
24#include "SkColorPriv.h"
25#include "SkDescriptor.h"
26#include "SkEndian.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000027#include "SkFloatingPoint.h"
mtkleinc50acf22015-07-01 14:00:57 -070028#include "SkFontDescriptor.h"
29#include "SkFontMgr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000030#include "SkGlyph.h"
bungeman34902632014-12-10 21:43:27 -080031#include "SkLazyFnPtr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000032#include "SkMaskGamma.h"
mtkleinc50acf22015-07-01 14:00:57 -070033#include "SkMutex.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000034#include "SkOTTable_glyf.h"
35#include "SkOTTable_head.h"
36#include "SkOTTable_hhea.h"
37#include "SkOTTable_loca.h"
38#include "SkOTUtils.h"
39#include "SkPaint.h"
40#include "SkPath.h"
mtkleinc50acf22015-07-01 14:00:57 -070041#include "SkSFNTHeader.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000042#include "SkStream.h"
mtkleinc50acf22015-07-01 14:00:57 -070043#include "SkString.h"
44#include "SkTypefaceCache.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000045#include "SkTypeface_mac.h"
46#include "SkUtils.h"
reed@google.combcb42ae2013-07-02 13:56:39 +000047#include "SkUtils.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000048
bungeman34902632014-12-10 21:43:27 -080049#include <dlfcn.h>
reed@google.comf77b35d2013-05-02 20:39:44 +000050
bungeman3b4b66c2015-01-08 08:33:44 -080051// Set to make glyph bounding boxes visible.
52#define SK_SHOW_TEXT_BLIT_COVERAGE 0
53
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000054class SkScalerContext_Mac;
55
reed@google.com3dcbd462013-03-27 13:56:34 +000056// CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
57// provide a wrapper here that will return an empty array if need be.
58static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() {
59#ifdef SK_BUILD_FOR_IOS
60 return CFArrayCreate(NULL, NULL, 0, NULL);
61#else
62 return CTFontManagerCopyAvailableFontFamilyNames();
63#endif
64}
65
66
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000067// Being templated and taking const T* prevents calling
68// CFSafeRelease(autoCFRelease) through implicit conversion.
69template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
70 if (cfTypeRef) {
71 CFRelease(cfTypeRef);
72 }
73}
74
75// Being templated and taking const T* prevents calling
76// CFSafeRetain(autoCFRelease) through implicit conversion.
77template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
78 if (cfTypeRef) {
79 CFRetain(cfTypeRef);
80 }
81}
82
83/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
84template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
85public:
86 explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
87 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
88
89 void reset(CFRef that = NULL) {
ljagielskie9d2d092014-07-15 20:02:04 -070090 if (that != fCFRef) {
91 CFSafeRelease(fCFRef);
92 fCFRef = that;
93 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000094 }
95
bungeman64fb51d2015-05-04 12:03:50 -070096 CFRef detach() {
97 CFRef self = fCFRef;
98 fCFRef = NULL;
99 return self;
100 }
101
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000102 operator CFRef() const { return fCFRef; }
103 CFRef get() const { return fCFRef; }
104
bungeman@google.coma9802692013-08-07 02:45:25 +0000105 CFRef* operator&() { SkASSERT(fCFRef == NULL); return &fCFRef; }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000106private:
107 CFRef fCFRef;
108};
109
reed@google.com964988f2013-03-29 14:57:22 +0000110static CFStringRef make_CFString(const char str[]) {
111 return CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
112}
113
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000114template<typename T> class AutoCGTable : SkNoncopyable {
115public:
116 AutoCGTable(CGFontRef font)
117 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
118 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
119 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
120 { }
121
122 const T* operator->() const { return fData; }
123
124private:
125 AutoCFRelease<CFDataRef> fCFData;
126public:
127 const T* fData;
128};
129
130// inline versions of these rect helpers
131
132static bool CGRectIsEmpty_inline(const CGRect& rect) {
133 return rect.size.width <= 0 || rect.size.height <= 0;
134}
135
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000136static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
137 return rect.origin.x;
138}
139
140static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
141 return rect.origin.x + rect.size.width;
142}
143
144static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
145 return rect.origin.y;
146}
147
148static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
149 return rect.origin.y + rect.size.height;
150}
151
152static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
153 return rect.size.width;
154}
155
156///////////////////////////////////////////////////////////////////////////////
157
158static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
reed@google.com7fa2a652014-01-27 13:42:58 +0000159 int width, int height, size_t rowBytes) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000160 SkASSERT(width);
161 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
162
163 if (width >= 32) {
164 while (height) {
165 sk_memset32(ptr, value, width);
166 ptr = (uint32_t*)((char*)ptr + rowBytes);
167 height -= 1;
168 }
169 return;
170 }
171
172 rowBytes -= width * sizeof(uint32_t);
173
174 if (width >= 8) {
175 while (height) {
176 int w = width;
177 do {
178 *ptr++ = value; *ptr++ = value;
179 *ptr++ = value; *ptr++ = value;
180 *ptr++ = value; *ptr++ = value;
181 *ptr++ = value; *ptr++ = value;
182 w -= 8;
183 } while (w >= 8);
184 while (--w >= 0) {
185 *ptr++ = value;
186 }
187 ptr = (uint32_t*)((char*)ptr + rowBytes);
188 height -= 1;
189 }
190 } else {
191 while (height) {
192 int w = width;
193 do {
194 *ptr++ = value;
195 } while (--w > 0);
196 ptr = (uint32_t*)((char*)ptr + rowBytes);
197 height -= 1;
198 }
199 }
200}
201
202#include <sys/utsname.h>
203
204typedef uint32_t CGRGBPixel;
205
206static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
207 return pixel & 0xFF;
208}
209
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000210static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
211
212// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
213static int readVersion() {
214 struct utsname info;
215 if (uname(&info) != 0) {
216 SkDebugf("uname failed\n");
217 return 0;
218 }
219 if (strcmp(info.sysname, "Darwin") != 0) {
220 SkDebugf("unexpected uname sysname %s\n", info.sysname);
221 return 0;
222 }
223 char* dot = strchr(info.release, '.');
224 if (!dot) {
225 SkDebugf("expected dot in uname release %s\n", info.release);
226 return 0;
227 }
228 int version = atoi(info.release);
229 if (version == 0) {
230 SkDebugf("could not parse uname release %s\n", info.release);
231 }
232 return version;
233}
234
235static int darwinVersion() {
236 static int darwin_version = readVersion();
237 return darwin_version;
238}
239
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000240static bool isSnowLeopard() {
241 return darwinVersion() == 10;
242}
243
244static bool isLion() {
245 return darwinVersion() == 11;
246}
247
248static bool isMountainLion() {
249 return darwinVersion() == 12;
250}
251
252static bool isLCDFormat(unsigned format) {
reedd54d3fc2014-11-13 14:39:58 -0800253 return SkMask::kLCD16_Format == format;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000254}
255
256static CGFloat ScalarToCG(SkScalar scalar) {
257 if (sizeof(CGFloat) == sizeof(float)) {
258 return SkScalarToFloat(scalar);
259 } else {
260 SkASSERT(sizeof(CGFloat) == sizeof(double));
261 return (CGFloat) SkScalarToDouble(scalar);
262 }
263}
264
265static SkScalar CGToScalar(CGFloat cgFloat) {
266 if (sizeof(CGFloat) == sizeof(float)) {
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000267 return cgFloat;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000268 } else {
269 SkASSERT(sizeof(CGFloat) == sizeof(double));
270 return SkDoubleToScalar(cgFloat);
271 }
272}
273
274static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
275 SkScalar sx = SK_Scalar1,
276 SkScalar sy = SK_Scalar1) {
277 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
278 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
279 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
280 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
281 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
282 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
283}
284
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000285///////////////////////////////////////////////////////////////////////////////
286
287#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000288
289/**
290 * There does not appear to be a publicly accessable API for determining if lcd
291 * font smoothing will be applied if we request it. The main issue is that if
292 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
293 */
294static bool supports_LCD() {
295 static int gSupportsLCD = -1;
296 if (gSupportsLCD >= 0) {
297 return (bool) gSupportsLCD;
298 }
299 uint32_t rgb = 0;
300 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
301 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
302 colorspace, BITMAP_INFO_RGB));
303 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
304 CGContextSetShouldSmoothFonts(cgContext, true);
305 CGContextSetShouldAntialias(cgContext, true);
306 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
307 CGContextSetGrayFillColor(cgContext, 1, 1);
308 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
309 uint32_t r = (rgb >> 16) & 0xFF;
310 uint32_t g = (rgb >> 8) & 0xFF;
311 uint32_t b = (rgb >> 0) & 0xFF;
312 gSupportsLCD = (r != g || r != b);
313 return (bool) gSupportsLCD;
314}
315
316class Offscreen {
317public:
bungeman34902632014-12-10 21:43:27 -0800318 Offscreen()
319 : fRGBSpace(NULL)
320 , fCG(NULL)
321 , fDoAA(false)
322 , fDoLCD(false)
323 {
324 fSize.set(0, 0);
325 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000326
327 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
328 CGGlyph glyphID, size_t* rowBytesPtr,
329 bool generateA8FromLCD);
330
331private:
332 enum {
333 kSize = 32 * 32 * sizeof(CGRGBPixel)
334 };
335 SkAutoSMalloc<kSize> fImageStorage;
336 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
337
338 // cached state
339 AutoCFRelease<CGContextRef> fCG;
340 SkISize fSize;
341 bool fDoAA;
342 bool fDoLCD;
343
344 static int RoundSize(int dimension) {
345 return SkNextPow2(dimension);
346 }
347};
348
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000349///////////////////////////////////////////////////////////////////////////////
350
bungemana4c4a2d2014-10-20 13:33:19 -0700351static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
352 CFNumberRef num;
353 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
354 && CFNumberIsFloatType(num)
355 && CFNumberGetValue(num, kCFNumberFloatType, value);
356}
357
358static int unit_weight_to_fontstyle(float unit) {
359 float value;
360 if (unit < 0) {
361 value = 100 + (1 + unit) * 300;
362 } else {
363 value = 400 + unit * 500;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000364 }
bungemana4c4a2d2014-10-20 13:33:19 -0700365 return sk_float_round2int(value);
366}
367
368static int unit_width_to_fontstyle(float unit) {
369 float value;
370 if (unit < 0) {
371 value = 1 + (1 + unit) * 4;
372 } else {
373 value = 5 + unit * 4;
374 }
375 return sk_float_round2int(value);
376}
377
bungeman336fdf22014-11-10 07:48:55 -0800378static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc) {
bungemana4c4a2d2014-10-20 13:33:19 -0700379 AutoCFRelease<CFDictionaryRef> dict(
380 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
381 if (NULL == dict.get()) {
382 return SkFontStyle();
383 }
384
bungemana4c4a2d2014-10-20 13:33:19 -0700385 float weight, width, slant;
386 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800387 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700388 }
389 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
390 width = 0;
391 }
392 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800393 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700394 }
395
396 return SkFontStyle(unit_weight_to_fontstyle(weight),
397 unit_width_to_fontstyle(width),
398 slant ? SkFontStyle::kItalic_Slant
399 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000400}
401
bungeman336fdf22014-11-10 07:48:55 -0800402static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) {
403 unsigned style = SkTypeface::kNormal;
404 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
405
406 if (traits & kCTFontBoldTrait) {
407 style |= SkTypeface::kBold;
408 }
409 if (traits & kCTFontItalicTrait) {
410 style |= SkTypeface::kItalic;
411 }
412 if (isFixedPitch) {
413 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0;
414 }
415 return (SkTypeface::Style)style;
416}
417
reed@google.comce8b3de2013-03-26 19:30:16 +0000418#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
419
bungeman994e8182015-02-23 16:17:43 -0800420// kCTFontColorGlyphsTrait was added in the Mac 10.7 and iPhone 4.3 SDKs.
421// Being an enum value it is not guarded by version macros, but old SDKs must still be supported.
422#if defined(__MAC_10_7) || defined(__IPHONE_4_3)
423static const uint32_t SkCTFontColorGlyphsTrait = kCTFontColorGlyphsTrait;
424#else
425static const uint32_t SkCTFontColorGlyphsTrait = (1 << 13);
426#endif
427
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000428class SkTypeface_Mac : public SkTypeface {
429public:
bungeman78884012015-06-08 13:39:12 -0700430 SkTypeface_Mac(CTFontRef fontRef, CFTypeRef resourceRef,
431 const SkFontStyle& fs, bool isFixedPitch,
432 const char requestedName[], bool isLocalStream)
bungeman64fb51d2015-05-04 12:03:50 -0700433 : SkTypeface(fs, SkTypefaceCache::NewFontID(), isFixedPitch)
bungeman967937c2014-10-30 11:49:27 -0700434 , fRequestedName(requestedName)
mtklein802ad832014-10-20 12:54:31 -0700435 , fFontRef(fontRef) // caller has already called CFRetain for us
bungeman78884012015-06-08 13:39:12 -0700436 , fOriginatingCFTypeRef(resourceRef) // caller has already called CFRetain for us
bungemane3bea5c2015-04-07 07:34:36 -0700437 , fHasColorGlyphs(SkToBool(CTFontGetSymbolicTraits(fFontRef) & SkCTFontColorGlyphsTrait))
caseq26337e92014-06-30 12:14:52 -0700438 , fIsLocalStream(isLocalStream)
reed@google.comce8b3de2013-03-26 19:30:16 +0000439 {
440 SkASSERT(fontRef);
441 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000442
bungeman967937c2014-10-30 11:49:27 -0700443 SkString fRequestedName;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000444 AutoCFRelease<CTFontRef> fFontRef;
bungeman78884012015-06-08 13:39:12 -0700445 AutoCFRelease<CFTypeRef> fOriginatingCFTypeRef;
bungemane3bea5c2015-04-07 07:34:36 -0700446 const bool fHasColorGlyphs;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000447
448protected:
mtklein36352bf2015-03-25 18:17:31 -0700449 int onGetUPEM() const override;
450 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
bungeman41868fe2015-05-20 09:21:04 -0700451 SkFontData* onCreateFontData() const override;
mtklein36352bf2015-03-25 18:17:31 -0700452 void onGetFamilyName(SkString* familyName) const override;
453 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
454 int onGetTableTags(SkFontTableTag tags[]) const override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000455 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
mtklein36352bf2015-03-25 18:17:31 -0700456 size_t length, void* data) const override;
457 SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override;
458 void onFilterRec(SkScalerContextRec*) const override;
459 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
reed@google.com2689f612013-03-20 20:01:47 +0000460 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -0700461 PerGlyphInfo,
mtklein36352bf2015-03-25 18:17:31 -0700462 const uint32_t*, uint32_t) const override;
reed@google.combcb42ae2013-07-02 13:56:39 +0000463 virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
mtklein36352bf2015-03-25 18:17:31 -0700464 int glyphCount) const override;
465 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000466
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000467private:
caseq26337e92014-06-30 12:14:52 -0700468 bool fIsLocalStream;
reed@google.comce8b3de2013-03-26 19:30:16 +0000469
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000470 typedef SkTypeface INHERITED;
471};
472
bungeman64fb51d2015-05-04 12:03:50 -0700473/** Creates a typeface without searching the cache. Takes ownership of the CTFontRef. */
bungeman78884012015-06-08 13:39:12 -0700474static SkTypeface* NewFromFontRef(CTFontRef fontRef, CFTypeRef resourceRef,
475 const char name[], bool isLocalStream)
bungeman41868fe2015-05-20 09:21:04 -0700476{
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000477 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000478 bool isFixedPitch;
bungeman336fdf22014-11-10 07:48:55 -0800479 SkFontStyle style = SkFontStyle(computeStyleBits(fontRef, &isFixedPitch));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000480
bungeman78884012015-06-08 13:39:12 -0700481 return new SkTypeface_Mac(fontRef, resourceRef, style, isFixedPitch, name, isLocalStream);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000482}
483
bungeman64fb51d2015-05-04 12:03:50 -0700484static bool find_by_CTFontRef(SkTypeface* cached, const SkFontStyle&, void* context) {
485 CTFontRef self = (CTFontRef)context;
486 CTFontRef other = ((SkTypeface_Mac*)cached)->fFontRef;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000487
bungeman64fb51d2015-05-04 12:03:50 -0700488 return CFEqual(self, other);
489}
490
491/** Creates a typeface from a name, searching the cache. */
492static SkTypeface* NewFromName(const char familyName[], const SkFontStyle& theStyle) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000493 CTFontSymbolicTraits ctFontTraits = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700494 if (theStyle.weight() >= SkFontStyle::kBold_Weight) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000495 ctFontTraits |= kCTFontBoldTrait;
496 }
bungemana4c4a2d2014-10-20 13:33:19 -0700497 if (theStyle.slant() != SkFontStyle::kUpright_Slant) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000498 ctFontTraits |= kCTFontItalicTrait;
499 }
500
bungemana4c4a2d2014-10-20 13:33:19 -0700501 //TODO: add weight width slant
502
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000503 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000504 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000505
506 AutoCFRelease<CFNumberRef> cfFontTraits(
507 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
508
509 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
510 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
511 &kCFTypeDictionaryKeyCallBacks,
512 &kCFTypeDictionaryValueCallBacks));
513
514 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
515 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
516 &kCFTypeDictionaryKeyCallBacks,
517 &kCFTypeDictionaryValueCallBacks));
518
bungeman64fb51d2015-05-04 12:03:50 -0700519 if (!cfFontName || !cfFontTraits || !cfAttributes || !cfTraits) {
520 return NULL;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000521 }
522
bungeman64fb51d2015-05-04 12:03:50 -0700523 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
524
525 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
526 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
527
528 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
529 CTFontDescriptorCreateWithAttributes(cfAttributes));
530 if (!ctFontDesc) {
531 return NULL;
532 }
533
534 AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL));
535 if (!ctFont) {
536 return NULL;
537 }
538
539 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)ctFont.get());
540 if (!face) {
bungeman78884012015-06-08 13:39:12 -0700541 face = NewFromFontRef(ctFont.detach(), NULL, NULL, false);
bungeman64fb51d2015-05-04 12:03:50 -0700542 SkTypefaceCache::Add(face, face->fontStyle());
543 }
544 return face;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000545}
546
bungemand6aeb6d2014-07-25 11:52:47 -0700547SK_DECLARE_STATIC_MUTEX(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000548static SkTypeface* GetDefaultFace() {
bungemand6aeb6d2014-07-25 11:52:47 -0700549 SkAutoMutexAcquire ma(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000550
551 static SkTypeface* gDefaultFace;
552
553 if (NULL == gDefaultFace) {
bungemana4c4a2d2014-10-20 13:33:19 -0700554 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkFontStyle());
555 SkTypefaceCache::Add(gDefaultFace, SkFontStyle());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000556 }
557 return gDefaultFace;
558}
559
560///////////////////////////////////////////////////////////////////////////////
561
562extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
563CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
564 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
565 return macface ? macface->fFontRef.get() : NULL;
566}
567
568/* This function is visible on the outside. It first searches the cache, and if
569 * not found, returns a new entry (after adding it to the cache).
570 */
bungeman78884012015-06-08 13:39:12 -0700571SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef, CFTypeRef resourceRef) {
bungeman64fb51d2015-05-04 12:03:50 -0700572 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)fontRef);
573 if (!face) {
574 CFRetain(fontRef);
bungeman78884012015-06-08 13:39:12 -0700575 if (resourceRef) {
576 CFRetain(resourceRef);
577 }
578 face = NewFromFontRef(fontRef, resourceRef, NULL, false);
bungemana4c4a2d2014-10-20 13:33:19 -0700579 SkTypefaceCache::Add(face, face->fontStyle());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000580 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000581 return face;
582}
583
bungeman967937c2014-10-30 11:49:27 -0700584struct NameStyle {
585 const char* fName;
586 SkFontStyle fStyle;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000587};
588
bungeman967937c2014-10-30 11:49:27 -0700589static bool find_by_NameStyle(SkTypeface* cachedFace, const SkFontStyle& cachedStyle, void* ctx) {
590 const SkTypeface_Mac* cachedMacFace = static_cast<SkTypeface_Mac*>(cachedFace);
591 const NameStyle* requested = static_cast<const NameStyle*>(ctx);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000592
bungeman967937c2014-10-30 11:49:27 -0700593 return cachedStyle == requested->fStyle
594 && cachedMacFace->fRequestedName.equals(requested->fName);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000595}
596
597static const char* map_css_names(const char* name) {
598 static const struct {
599 const char* fFrom; // name the caller specified
600 const char* fTo; // "canonical" name we map to
601 } gPairs[] = {
602 { "sans-serif", "Helvetica" },
603 { "serif", "Times" },
604 { "monospace", "Courier" }
605 };
606
607 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
608 if (strcmp(name, gPairs[i].fFrom) == 0) {
609 return gPairs[i].fTo;
610 }
611 }
612 return name; // no change
613}
614
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000615///////////////////////////////////////////////////////////////////////////////
616
bungeman@google.comcefd9812013-05-15 15:07:32 +0000617/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000618struct GlyphRect {
619 int16_t fMinX;
620 int16_t fMinY;
621 int16_t fMaxX;
622 int16_t fMaxY;
623};
624
625class SkScalerContext_Mac : public SkScalerContext {
626public:
reed@google.com0da48612013-03-19 16:06:52 +0000627 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000628
629protected:
mtklein36352bf2015-03-25 18:17:31 -0700630 unsigned generateGlyphCount(void) override;
631 uint16_t generateCharToGlyph(SkUnichar uni) override;
632 void generateAdvance(SkGlyph* glyph) override;
633 void generateMetrics(SkGlyph* glyph) override;
634 void generateImage(const SkGlyph& glyph) override;
635 void generatePath(const SkGlyph& glyph, SkPath* path) override;
636 void generateFontMetrics(SkPaint::FontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000637
638private:
639 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000640
bungeman@google.comcefd9812013-05-15 15:07:32 +0000641 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
642 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000643
644 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
645 *
646 * For use with (and must be called before) generateBBoxes.
647 */
648 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000649
bungeman@google.comcefd9812013-05-15 15:07:32 +0000650 /** Initializes fFBoundingBoxes and returns true on success.
651 *
652 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
653 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
654 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
655 * font directly.
656 *
657 * This routine initializes fFBoundingBoxes to an array of
658 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
659 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
660 *
661 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
662 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
663 *
664 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
665 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000666 bool generateBBoxes();
667
bungeman@google.comcefd9812013-05-15 15:07:32 +0000668 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
669 *
670 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
671 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
672 */
673 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000674
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000675 Offscreen fOffscreen;
676 AutoCFRelease<CTFontRef> fCTFont;
bungemanaae30912015-03-02 13:43:26 -0800677 CGAffineTransform fTransform;
bungeman34902632014-12-10 21:43:27 -0800678 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000679
bungeman3b4b66c2015-01-08 08:33:44 -0800680 /** Unrotated variant of fCTFont.
681 *
682 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
683 * advances, but always sets the height to 0. This font is used to get the advances of the
684 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000685 *
686 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
687 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
688 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
bungeman3b4b66c2015-01-08 08:33:44 -0800689 * With kCTFontVerticalOrientation the advances must be unrotated.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000690 */
bungeman3b4b66c2015-01-08 08:33:44 -0800691 AutoCFRelease<CTFontRef> fCTUnrotatedFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000692
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000693 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000694 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000695 uint16_t fFBoundingBoxesGlyphOffset;
696 uint16_t fGlyphCount;
697 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000698 const bool fDoSubPosition;
699 const bool fVertical;
700
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000701 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000702
703 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000704};
705
reed@google.com0da48612013-03-19 16:06:52 +0000706SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
707 const SkDescriptor* desc)
708 : INHERITED(typeface, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000709 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000710 , fFBoundingBoxesGlyphOffset(0)
711 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000712 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
713 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
714
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000715{
reed@google.com2689f612013-03-20 20:01:47 +0000716 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000717 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000718 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
719 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000720
bungeman34902632014-12-10 21:43:27 -0800721 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
722 // As a result, it is necessary to know the actual device size and request that.
723 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800724 SkMatrix skTransform;
bungeman34902632014-12-10 21:43:27 -0800725 fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, &skTransform,
726 NULL, NULL, &fFUnitMatrix);
bungemanaae30912015-03-02 13:43:26 -0800727 fTransform = MatrixToCGAffineTransform(skTransform);
728 fInvTransform = CGAffineTransformInvert(fTransform);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000729
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000730 AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
731 if (fVertical) {
bungemanaae30912015-03-02 13:43:26 -0800732 // Setting the vertical orientation here is required for vertical metrics on some versions.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000733 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
734 kCFAllocatorDefault, 0,
735 &kCFTypeDictionaryKeyCallBacks,
736 &kCFTypeDictionaryValueCallBacks));
737 if (cfAttributes) {
738 CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
739 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
740 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
741 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
bungemane194c492014-07-16 13:46:06 -0700742 ctFontDesc.reset(CTFontDescriptorCreateWithAttributes(cfAttributes));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000743 }
744 }
bungemanbe2284d2014-11-25 08:08:09 -0800745
746 // The transform contains everything except the requested text size.
747 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -0800748 CGFloat textSize = ScalarToCG(scale.y());
bungemanbe2284d2014-11-25 08:08:09 -0800749
bungemanaae30912015-03-02 13:43:26 -0800750 fCTFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize, &fTransform, ctFontDesc));
bungemane194c492014-07-16 13:46:06 -0700751 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont, NULL));
bungeman3b4b66c2015-01-08 08:33:44 -0800752 fCTUnrotatedFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize,
753 &CGAffineTransformIdentity, NULL));
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000754
bungemanbe2284d2014-11-25 08:08:09 -0800755 // The fUnitMatrix includes the text size (and em) as it is used to scale the raw font data.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000756 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
757 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000758}
759
bungeman34902632014-12-10 21:43:27 -0800760extern "C" {
761
762/** CTFontDrawGlyphs was introduced in 10.7. */
763typedef void (*CTFontDrawGlyphsProc)(CTFontRef, const CGGlyph[], const CGPoint[],
764 size_t, CGContextRef);
765
766/** This is an implementation of CTFontDrawGlyphs for 10.6. */
767static void sk_legacy_CTFontDrawGlyphs(CTFontRef, const CGGlyph glyphs[], const CGPoint points[],
768 size_t count, CGContextRef cg)
769{
770 CGContextShowGlyphsAtPositions(cg, glyphs, points, count);
771}
772
773}
774
775CTFontDrawGlyphsProc SkChooseCTFontDrawGlyphs() {
776 CTFontDrawGlyphsProc realCTFontDrawGlyphs;
777 *reinterpret_cast<void**>(&realCTFontDrawGlyphs) = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
778 return realCTFontDrawGlyphs ? realCTFontDrawGlyphs : sk_legacy_CTFontDrawGlyphs;
779};
780
781SK_DECLARE_STATIC_LAZY_FN_PTR(CTFontDrawGlyphsProc, gCTFontDrawGlyphs, SkChooseCTFontDrawGlyphs);
782
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000783CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
784 CGGlyph glyphID, size_t* rowBytesPtr,
bungeman34902632014-12-10 21:43:27 -0800785 bool generateA8FromLCD)
786{
787 CTFontDrawGlyphsProc ctFontDrawGlyphs = gCTFontDrawGlyphs.get();
788
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000789 if (!fRGBSpace) {
790 //It doesn't appear to matter what color space is specified.
791 //Regular blends and antialiased text are always (s*a + d*(1-a))
792 //and smoothed text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -0700793 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000794 }
795
796 // default to kBW_Format
797 bool doAA = false;
798 bool doLCD = false;
799
800 if (SkMask::kBW_Format != glyph.fMaskFormat) {
801 doLCD = true;
802 doAA = true;
803 }
804
805 // FIXME: lcd smoothed un-hinted rasterization unsupported.
806 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
807 doLCD = false;
808 doAA = true;
809 }
810
bungeman34902632014-12-10 21:43:27 -0800811 // If this font might have color glyphs, disable LCD as there's no way to support it.
812 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
813 // A8 will be ugly too (white on transparent), but TODO: we can detect gray and set to A8.
814 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
815 doLCD = false;
816 }
817
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000818 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
819 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
820 if (fSize.fWidth < glyph.fWidth) {
821 fSize.fWidth = RoundSize(glyph.fWidth);
822 }
823 if (fSize.fHeight < glyph.fHeight) {
824 fSize.fHeight = RoundSize(glyph.fHeight);
825 }
826
827 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
828 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -0800829 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
830 ? kCGImageAlphaPremultipliedFirst
831 : kCGImageAlphaNoneSkipFirst;
832 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -0700833 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungeman34902632014-12-10 21:43:27 -0800834 rowBytes, fRGBSpace, bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000835
bungeman34902632014-12-10 21:43:27 -0800836 // Skia handles quantization and subpixel positioning,
837 // so disable quantization and enabe subpixel positioning in CG.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000838 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
839 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
840
bungeman@google.comcefd9812013-05-15 15:07:32 +0000841 // Because CG always draws from the horizontal baseline,
842 // if there is a non-integral translation from the horizontal origin to the vertical origin,
843 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungeman34902632014-12-10 21:43:27 -0800844 CGContextSetAllowsFontSubpixelPositioning(fCG, true);
845 CGContextSetShouldSubpixelPositionFonts(fCG, true);
846
847 CGContextSetTextDrawingMode(fCG, kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000848
849 // Draw white on black to create mask.
850 // TODO: Draw black on white and invert, CG has a special case codepath.
851 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
852
853 // force our checks below to happen
854 fDoAA = !doAA;
855 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -0800856
857 if (sk_legacy_CTFontDrawGlyphs == ctFontDrawGlyphs) {
858 // CTFontDrawGlyphs will apply the font, font size, and font matrix to the CGContext.
859 // Our 'fake' one does not, so set up the CGContext here.
860 CGContextSetFont(fCG, context.fCGFont);
861 CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont));
bungeman34902632014-12-10 21:43:27 -0800862 }
bungemanaae30912015-03-02 13:43:26 -0800863 CGContextSetTextMatrix(fCG, context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000864 }
865
866 if (fDoAA != doAA) {
867 CGContextSetShouldAntialias(fCG, doAA);
868 fDoAA = doAA;
869 }
870 if (fDoLCD != doLCD) {
871 CGContextSetShouldSmoothFonts(fCG, doLCD);
872 fDoLCD = doLCD;
873 }
874
875 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
876 // skip rows based on the glyph's height
877 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
878
879 // erase to black
880 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
881
882 float subX = 0;
883 float subY = 0;
884 if (context.fDoSubPosition) {
885 subX = SkFixedToFloat(glyph.getSubXFixed());
886 subY = SkFixedToFloat(glyph.getSubYFixed());
887 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000888
bungeman34902632014-12-10 21:43:27 -0800889 // CoreText and CoreGraphics always draw using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000890 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000891 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000892 context.getVerticalOffset(glyphID, &offset);
893 subX += offset.fX;
894 subY += offset.fY;
895 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000896
bungeman34902632014-12-10 21:43:27 -0800897 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -0800898 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
899 // 'positions' which are in text space. The glyph location (in device space) must be
900 // mapped into text space, so that CG can convert it back into device space.
901 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
bungeman1b5f25c2015-06-08 14:32:24 -0700902 //
bungemanaae30912015-03-02 13:43:26 -0800903 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
904 // So always make the font transform identity and place the transform on the context.
905 point = CGPointApplyAffineTransform(point, context.fInvTransform);
906
907 ctFontDrawGlyphs(context.fCTUnrotatedFont, &glyphID, &point, 1, fCG);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000908
909 SkASSERT(rowBytesPtr);
910 *rowBytesPtr = rowBytes;
911 return image;
912}
913
bungeman@google.comcefd9812013-05-15 15:07:32 +0000914void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
915 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
916 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
917 CGSize cgVertOffset;
918 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
919
920 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
921 if (isSnowLeopard()) {
922 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
923 fFUnitMatrix.mapPoints(&skVertOffset, 1);
924 } else {
925 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
926 skVertOffset.fY = -skVertOffset.fY;
927 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000928
bungeman@google.comcefd9812013-05-15 15:07:32 +0000929 *offset = skVertOffset;
930}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000931
932uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
933 if (fFBoundingBoxesGlyphOffset) {
934 return fFBoundingBoxesGlyphOffset;
935 }
936 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
937 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
938 if (hheaTable.fData) {
939 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
940 }
941 return fFBoundingBoxesGlyphOffset;
942}
reed@android.com0680d6c2008-12-19 19:46:15 +0000943
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000944bool SkScalerContext_Mac::generateBBoxes() {
945 if (fGeneratedFBoundingBoxes) {
bsalomon49f085d2014-09-05 13:34:00 -0700946 return SkToBool(fFBoundingBoxes.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000947 }
948 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000949
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000950 AutoCGTable<SkOTTableHead> headTable(fCGFont);
951 if (!headTable.fData) {
952 return false;
953 }
954
955 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
956 if (!locaTable.fData) {
957 return false;
958 }
959
960 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
961 if (!glyfTable.fData) {
962 return false;
963 }
964
965 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000966 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000967
968 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
969 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
970 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
971 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
972 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
973 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
974 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
975 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
976 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
977 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
978 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000979
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000980 return true;
981}
982
983unsigned SkScalerContext_Mac::generateGlyphCount(void) {
984 return fGlyphCount;
985}
986
987uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000988 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000989 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000990
991 // Get the glyph
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000992 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar);
993 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000994
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000995 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
996 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
997 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
998 CTFontGetGlyphsForCharacters(fCTFont, theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000999 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001000}
1001
1002void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
1003 this->generateMetrics(glyph);
1004}
1005
bungeman@google.comcefd9812013-05-15 15:07:32 +00001006void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
djsollen1b277042014-08-06 06:58:06 -07001007 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +00001008 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001009
bungeman@google.comcefd9812013-05-15 15:07:32 +00001010 // The following block produces cgAdvance in CG units (pixels, y up).
1011 CGSize cgAdvance;
1012 if (fVertical) {
bungeman3b4b66c2015-01-08 08:33:44 -08001013 CTFontGetAdvancesForGlyphs(fCTUnrotatedFont, kCTFontVerticalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001014 &cgGlyph, &cgAdvance, 1);
bungeman3b4b66c2015-01-08 08:33:44 -08001015 // Vertical advances are returned as widths instead of heights.
1016 SkTSwap(cgAdvance.height, cgAdvance.width);
1017 cgAdvance.height = -cgAdvance.height;
bungeman@google.comcefd9812013-05-15 15:07:32 +00001018 } else {
bungeman3b4b66c2015-01-08 08:33:44 -08001019 CTFontGetAdvancesForGlyphs(fCTUnrotatedFont, kCTFontHorizontalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001020 &cgGlyph, &cgAdvance, 1);
1021 }
bungeman3b4b66c2015-01-08 08:33:44 -08001022 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, CTFontGetMatrix(fCTFont));
bungeman@google.comcefd9812013-05-15 15:07:32 +00001023 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
1024 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001025
bungeman@google.comcefd9812013-05-15 15:07:32 +00001026 // The following produces skBounds in SkGlyph units (pixels, y down),
1027 // or returns early if skBounds would be empty.
1028 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001029
bungeman@google.comcefd9812013-05-15 15:07:32 +00001030 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
1031 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
1032 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
1033 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
1034 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
1035 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
1036 // to center the glyph along the vertical baseline and also perform some mysterious shift
1037 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
1038 // these steps.
1039 //
1040 // It is not known which is correct (or if either is correct). However, we must always draw
1041 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
1042 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001043
bungeman@google.comcefd9812013-05-15 15:07:32 +00001044 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
1045 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001046
bungeman@google.comcefd9812013-05-15 15:07:32 +00001047 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
1048 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
1049 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
1050 // font directly.
1051 if ((isLion() || isMountainLion()) &&
1052 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1053 {
1054 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1055 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1056 return;
1057 }
1058 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1059 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1060 fFUnitMatrix.mapRect(&skBounds);
1061
1062 } else {
1063 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1064 CGRect cgBounds;
1065 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1066 &cgGlyph, &cgBounds, 1);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001067
bungeman@google.comcefd9812013-05-15 15:07:32 +00001068 // BUG?
1069 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1070 // it should be empty. So, if we see a zero-advance, we check if it has an
1071 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1072 // is rare, so we won't incur a big performance cost for this extra check.
1073 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
1074 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
1075 if (NULL == path || CGPathIsEmpty(path)) {
1076 return;
1077 }
1078 }
1079
1080 if (CGRectIsEmpty_inline(cgBounds)) {
1081 return;
1082 }
1083
1084 // Convert cgBounds to SkGlyph units (pixels, y down).
1085 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1086 cgBounds.size.width, cgBounds.size.height);
1087 }
1088
1089 if (fVertical) {
1090 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1091 // Convert these horizontal bounds into vertical bounds.
1092 SkPoint offset;
1093 getVerticalOffset(cgGlyph, &offset);
1094 skBounds.offset(offset);
1095 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001096
bungeman@google.comcefd9812013-05-15 15:07:32 +00001097 // Currently the bounds are based on being rendered at (0,0).
1098 // The top left must not move, since that is the base from which subpixel positioning is offset.
1099 if (fDoSubPosition) {
1100 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1101 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1102 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001103
bungeman@google.comcefd9812013-05-15 15:07:32 +00001104 SkIRect skIBounds;
1105 skBounds.roundOut(&skIBounds);
1106 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1107 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1108 // is not currently known, as CG dilates the outlines by some percentage.
1109 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1110 skIBounds.outset(1, 1);
1111 glyph->fLeft = SkToS16(skIBounds.fLeft);
1112 glyph->fTop = SkToS16(skIBounds.fTop);
1113 glyph->fWidth = SkToU16(skIBounds.width());
1114 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001115}
1116
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001117#include "SkColorPriv.h"
1118
1119static void build_power_table(uint8_t table[], float ee) {
1120 for (int i = 0; i < 256; i++) {
1121 float x = i / 255.f;
1122 x = sk_float_pow(x, ee);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001123 int xx = SkScalarRoundToInt(x * 255);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001124 table[i] = SkToU8(xx);
1125 }
1126}
1127
1128/**
1129 * This will invert the gamma applied by CoreGraphics, so we can get linear
1130 * values.
1131 *
1132 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1133 * The color space used does not appear to affect this choice.
1134 */
1135static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1136 static bool gInited;
1137 static uint8_t gTableCoreGraphicsSmoothing[256];
1138 if (!gInited) {
1139 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1140 gInited = true;
1141 }
1142 return gTableCoreGraphicsSmoothing;
1143}
1144
1145static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1146 while (count > 0) {
1147 uint8_t mask = 0;
1148 for (int i = 7; i >= 0; --i) {
1149 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1150 if (0 == --count) {
1151 break;
1152 }
1153 }
1154 *dst++ = mask;
1155 }
1156}
1157
1158template<bool APPLY_PREBLEND>
1159static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1160 U8CPU r = (rgb >> 16) & 0xFF;
1161 U8CPU g = (rgb >> 8) & 0xFF;
1162 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001163 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1164#if SK_SHOW_TEXT_BLIT_COVERAGE
1165 lum = SkTMax(lum, (U8CPU)0x30);
1166#endif
1167 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001168}
1169template<bool APPLY_PREBLEND>
1170static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1171 const SkGlyph& glyph, const uint8_t* table8) {
1172 const int width = glyph.fWidth;
1173 size_t dstRB = glyph.rowBytes();
1174 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1175
1176 for (int y = 0; y < glyph.fHeight; y++) {
1177 for (int i = 0; i < width; ++i) {
1178 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1179 }
1180 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1181 dst += dstRB;
1182 }
1183}
1184
1185template<bool APPLY_PREBLEND>
1186static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1187 const uint8_t* tableG,
1188 const uint8_t* tableB) {
1189 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1190 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1191 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001192#if SK_SHOW_TEXT_BLIT_COVERAGE
1193 r = SkTMax(r, (U8CPU)0x30);
1194 g = SkTMax(g, (U8CPU)0x30);
1195 b = SkTMax(b, (U8CPU)0x30);
1196#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001197 return SkPack888ToRGB16(r, g, b);
1198}
1199template<bool APPLY_PREBLEND>
1200static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1201 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1202 const int width = glyph.fWidth;
1203 size_t dstRB = glyph.rowBytes();
1204 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1205
1206 for (int y = 0; y < glyph.fHeight; y++) {
1207 for (int i = 0; i < width; i++) {
1208 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1209 }
1210 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1211 dst = (uint16_t*)((char*)dst + dstRB);
1212 }
1213}
1214
bungeman34902632014-12-10 21:43:27 -08001215static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1216 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001217 U8CPU r = (rgb >> 16) & 0xFF;
1218 U8CPU g = (rgb >> 8) & 0xFF;
1219 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001220#if SK_SHOW_TEXT_BLIT_COVERAGE
1221 a = SkTMax(a, (U8CPU)0x30);
1222#endif
bungeman34902632014-12-10 21:43:27 -08001223 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001224}
reed@google.comf77b35d2013-05-02 20:39:44 +00001225
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001226template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1227 return (T*)((char*)ptr + byteOffset);
1228}
1229
1230void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
djsollen1b277042014-08-06 06:58:06 -07001231 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001232
1233 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1234 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1235
1236 // Draw the glyph
1237 size_t cgRowBytes;
1238 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
1239 if (cgPixels == NULL) {
1240 return;
1241 }
1242
1243 //TODO: see if drawing black on white and inverting is faster (at least in
1244 //lcd case) as core graphics appears to have special case code for drawing
1245 //black text.
1246
1247 // Fix the glyph
1248 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1249 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1250 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1251
1252 //Note that the following cannot really be integrated into the
1253 //pre-blend, since we may not be applying the pre-blend; when we aren't
1254 //applying the pre-blend it means that a filter wants linear anyway.
1255 //Other code may also be applying the pre-blend, so we'd need another
1256 //one with this and one without.
1257 CGRGBPixel* addr = cgPixels;
1258 for (int y = 0; y < glyph.fHeight; ++y) {
1259 for (int x = 0; x < glyph.fWidth; ++x) {
1260 int r = (addr[x] >> 16) & 0xFF;
1261 int g = (addr[x] >> 8) & 0xFF;
1262 int b = (addr[x] >> 0) & 0xFF;
1263 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1264 }
1265 addr = SkTAddByteOffset(addr, cgRowBytes);
1266 }
1267 }
1268
1269 // Convert glyph to mask
1270 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001271 case SkMask::kLCD16_Format: {
1272 if (fPreBlend.isApplicable()) {
1273 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1274 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1275 } else {
1276 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1277 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1278 }
1279 } break;
1280 case SkMask::kA8_Format: {
1281 if (fPreBlend.isApplicable()) {
1282 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1283 } else {
1284 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1285 }
1286 } break;
1287 case SkMask::kBW_Format: {
1288 const int width = glyph.fWidth;
1289 size_t dstRB = glyph.rowBytes();
1290 uint8_t* dst = (uint8_t*)glyph.fImage;
1291 for (int y = 0; y < glyph.fHeight; y++) {
1292 cgpixels_to_bits(dst, cgPixels, width);
1293 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1294 dst += dstRB;
1295 }
1296 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001297 case SkMask::kARGB32_Format: {
1298 const int width = glyph.fWidth;
1299 size_t dstRB = glyph.rowBytes();
1300 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1301 for (int y = 0; y < glyph.fHeight; y++) {
1302 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001303 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001304 }
1305 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1306 dst = (SkPMColor*)((char*)dst + dstRB);
1307 }
1308 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001309 default:
1310 SkDEBUGFAIL("unexpected mask format");
1311 break;
1312 }
1313}
1314
1315/*
1316 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1317 * seems sufficient, and possibly even correct, to allow the hinted outline
1318 * to be subpixel positioned.
1319 */
1320#define kScaleForSubPixelPositionHinting (4.0f)
1321
1322void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
1323 CTFontRef font = fCTFont;
1324 SkScalar scaleX = SK_Scalar1;
1325 SkScalar scaleY = SK_Scalar1;
1326
1327 /*
1328 * For subpixel positioning, we want to return an unhinted outline, so it
1329 * can be positioned nicely at fractional offsets. However, we special-case
1330 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1331 * we want to retain hinting in the direction orthogonal to the baseline.
1332 * e.g. for horizontal baseline, we want to retain hinting in Y.
1333 * The way we remove hinting is to scale the font by some value (4) in that
1334 * direction, ask for the path, and then scale the path back down.
1335 */
1336 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1337 SkMatrix m;
1338 fRec.getSingleMatrix(&m);
1339
1340 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001341 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001342 // now see if we need to restore hinting for axis-aligned baselines
1343 switch (SkComputeAxisAlignmentForHText(m)) {
1344 case kX_SkAxisAlignment:
1345 scaleY = SK_Scalar1; // want hinting in the Y direction
1346 break;
1347 case kY_SkAxisAlignment:
1348 scaleX = SK_Scalar1; // want hinting in the X direction
1349 break;
1350 default:
1351 break;
1352 }
1353
1354 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
1355 // need to release font when we're done
1356 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
1357 }
1358
djsollen1b277042014-08-06 06:58:06 -07001359 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001360 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
1361
1362 path->reset();
1363 if (cgPath != NULL) {
1364 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1365 }
1366
bungeman@google.comcefd9812013-05-15 15:07:32 +00001367 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001368 SkMatrix m;
1369 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1370 path->transform(m);
1371 // balance the call to CTFontCreateCopyWithAttributes
1372 CFSafeRelease(font);
1373 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001374 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001375 SkPoint offset;
1376 getVerticalOffset(cgGlyph, &offset);
1377 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001378 }
1379}
1380
bungeman41078062014-07-07 08:16:37 -07001381void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
1382 if (NULL == metrics) {
1383 return;
1384 }
1385
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001386 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1387
bungeman41078062014-07-07 08:16:37 -07001388 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1389 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1390 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1391 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1392 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1393 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1394 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1395 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001396 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungeman41078062014-07-07 08:16:37 -07001397 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
bungeman6bd8d1c2015-06-09 08:40:51 -07001398 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont));
bungeman41078062014-07-07 08:16:37 -07001399 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont));
1400 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001401
bungeman41078062014-07-07 08:16:37 -07001402 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
1403 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001404}
1405
1406void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1407 SkPath* skPath = (SkPath*)info;
1408
1409 // Process the path element
1410 switch (element->type) {
1411 case kCGPathElementMoveToPoint:
1412 skPath->moveTo(element->points[0].x, -element->points[0].y);
1413 break;
1414
1415 case kCGPathElementAddLineToPoint:
1416 skPath->lineTo(element->points[0].x, -element->points[0].y);
1417 break;
1418
1419 case kCGPathElementAddQuadCurveToPoint:
1420 skPath->quadTo(element->points[0].x, -element->points[0].y,
1421 element->points[1].x, -element->points[1].y);
1422 break;
1423
1424 case kCGPathElementAddCurveToPoint:
1425 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1426 element->points[1].x, -element->points[1].y,
1427 element->points[2].x, -element->points[2].y);
1428 break;
1429
1430 case kCGPathElementCloseSubpath:
1431 skPath->close();
1432 break;
1433
1434 default:
1435 SkDEBUGFAIL("Unknown path element!");
1436 break;
1437 }
1438}
1439
1440
1441///////////////////////////////////////////////////////////////////////////////
1442
1443// Returns NULL on failure
1444// Call must still manage its ownership of provider
1445static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1446 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
1447 if (NULL == cg) {
1448 return NULL;
1449 }
1450 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
bungeman78884012015-06-08 13:39:12 -07001451 return ct ? NewFromFontRef(ct, NULL, NULL, true) : NULL;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001452}
1453
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001454// Web fonts added to the the CTFont registry do not return their character set.
1455// Iterate through the font in this case. The existing caller caches the result,
1456// so the performance impact isn't too bad.
1457static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1458 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001459 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001460 SkUnichar* out = glyphToUnicode->begin();
1461 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1462 UniChar unichar = 0;
1463 while (glyphCount > 0) {
1464 CGGlyph glyph;
1465 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1466 out[glyph] = unichar;
1467 --glyphCount;
1468 }
1469 if (++unichar == 0) {
1470 break;
1471 }
1472 }
1473}
1474
1475// Construct Glyph to Unicode table.
1476// Unicode code points that require conjugate pairs in utf16 are not
1477// supported.
1478static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1479 SkTDArray<SkUnichar>* glyphToUnicode) {
1480 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1481 if (!charSet) {
1482 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1483 return;
1484 }
1485
1486 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1487 charSet));
1488 if (!bitmap) {
1489 return;
1490 }
1491 CFIndex length = CFDataGetLength(bitmap);
1492 if (!length) {
1493 return;
1494 }
1495 if (length > 8192) {
1496 // TODO: Add support for Unicode above 0xFFFF
1497 // Consider only the BMP portion of the Unicode character points.
1498 // The bitmap may contain other planes, up to plane 16.
1499 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1500 length = 8192;
1501 }
1502 const UInt8* bits = CFDataGetBytePtr(bitmap);
reed@google.com7fa2a652014-01-27 13:42:58 +00001503 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001504 SkUnichar* out = glyphToUnicode->begin();
1505 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1506 for (int i = 0; i < length; i++) {
1507 int mask = bits[i];
1508 if (!mask) {
1509 continue;
1510 }
1511 for (int j = 0; j < 8; j++) {
1512 CGGlyph glyph;
1513 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1514 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1515 out[glyph] = unichar;
1516 }
1517 }
1518 }
1519}
1520
1521static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1522 CGSize advance;
1523 advance.width = 0;
1524 CGGlyph glyph = gId;
1525 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1526 *data = sk_float_round2int(advance.width);
1527 return true;
1528}
1529
bungeman256b3512014-07-02 07:57:59 -07001530/** Assumes src and dst are not NULL. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001531static void CFStringToSkString(CFStringRef src, SkString* dst) {
1532 // Reserve enough room for the worst-case string,
1533 // plus 1 byte for the trailing null.
1534 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1535 kCFStringEncodingUTF8) + 1;
1536 dst->resize(length);
1537 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1538 // Resize to the actual UTF-8 length used, stripping the null character.
1539 dst->resize(strlen(dst->c_str()));
1540}
1541
reed@google.com2689f612013-03-20 20:01:47 +00001542SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -07001543 PerGlyphInfo perGlyphInfo,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001544 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001545 uint32_t glyphIDsCount) const {
1546
1547 CTFontRef originalCTFont = fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001548 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
1549 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
1550 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1551
1552 {
1553 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
bungeman256b3512014-07-02 07:57:59 -07001554 if (fontName.get()) {
1555 CFStringToSkString(fontName, &info->fFontName);
1556 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001557 }
1558
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001559 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1560 info->fLastGlyphID = SkToU16(glyphCount - 1);
1561 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
vandebo0f9bad02014-06-19 11:05:39 -07001562 info->fFlags = SkAdvancedTypefaceMetrics::kEmpty_FontFlag;
1563 info->fStyle = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001564
reed39a9a502015-05-12 09:50:04 -07001565 if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001566 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1567 }
1568
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001569 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1570 // fonts always have both glyf and loca tables. At the least, this is what
1571 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1572 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001573 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001574 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1575 info->fItalicAngle = 0;
1576 info->fAscent = 0;
1577 info->fDescent = 0;
1578 info->fStemV = 0;
1579 info->fCapHeight = 0;
1580 info->fBBox = SkIRect::MakeEmpty();
1581 return info;
1582 }
1583
1584 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1585 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1586 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1587 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1588 }
1589 if (symbolicTraits & kCTFontItalicTrait) {
1590 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1591 }
1592 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001593 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1594 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1595 } else if (stylisticClass & kCTFontScriptsClass) {
1596 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1597 }
1598 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1599 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1600 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1601 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1602 CGRect bbox = CTFontGetBoundingBox(ctFont);
1603
1604 SkRect r;
1605 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1606 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1607 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1608 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1609
1610 r.roundOut(&(info->fBBox));
1611
1612 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1613 // This probably isn't very good with an italic font.
1614 int16_t min_width = SHRT_MAX;
1615 info->fStemV = 0;
1616 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1617 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1618 CGGlyph glyphs[count];
1619 CGRect boundingRects[count];
1620 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1621 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1622 glyphs, boundingRects, count);
1623 for (size_t i = 0; i < count; i++) {
1624 int16_t width = (int16_t) boundingRects[i].size.width;
1625 if (width > 0 && width < min_width) {
1626 min_width = width;
1627 info->fStemV = min_width;
1628 }
1629 }
1630 }
1631
reed39a9a502015-05-12 09:50:04 -07001632 if (perGlyphInfo & kHAdvance_PerGlyphInfo) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001633 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1634 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1635 info->fGlyphWidths->fAdvance.append(1, &min_width);
1636 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1637 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1638 } else {
1639 info->fGlyphWidths.reset(
1640 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
reed@google.com7fa2a652014-01-27 13:42:58 +00001641 SkToInt(glyphCount),
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001642 glyphIDs,
1643 glyphIDsCount,
1644 &getWidthAdvance));
1645 }
1646 }
1647 return info;
1648}
1649
1650///////////////////////////////////////////////////////////////////////////////
1651
reed@google.comcc9aad52013-03-21 19:28:10 +00001652static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1653 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001654 AutoCFRelease<CFNumberRef> fontFormatRef(
1655 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1656 if (!fontFormatRef) {
1657 return 0;
1658 }
1659
1660 SInt32 fontFormatValue;
1661 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1662 return 0;
1663 }
1664
1665 switch (fontFormatValue) {
1666 case kCTFontFormatOpenTypePostScript:
1667 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1668 case kCTFontFormatOpenTypeTrueType:
1669 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1670 case kCTFontFormatTrueType:
1671 return SkSFNTHeader::fontType_MacTrueType::TAG;
1672 case kCTFontFormatPostScript:
1673 return SkSFNTHeader::fontType_PostScript::TAG;
1674 case kCTFontFormatBitmap:
1675 return SkSFNTHeader::fontType_MacTrueType::TAG;
1676 case kCTFontFormatUnrecognized:
1677 default:
1678 //CT seems to be unreliable in being able to obtain the type,
1679 //even if all we want is the first four bytes of the font resource.
1680 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1681 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1682 }
1683}
1684
bungeman5f213d92015-01-27 05:39:10 -08001685SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
reed@google.comcc9aad52013-03-21 19:28:10 +00001686 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001687 if (0 == fontType) {
1688 return NULL;
1689 }
1690
1691 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001692 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001693 SkTDArray<SkFontTableTag> tableTags;
1694 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001695 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001696
1697 // calc total size for font, save sizes
1698 SkTDArray<size_t> tableSizes;
1699 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1700 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001701 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001702 totalSize += (tableSize + 3) & ~3;
1703 *tableSizes.append() = tableSize;
1704 }
1705
1706 // reserve memory for stream, and zero it (tables must be zero padded)
1707 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1708 char* dataStart = (char*)stream->getMemoryBase();
1709 sk_bzero(dataStart, totalSize);
1710 char* dataPtr = dataStart;
1711
1712 // compute font header entries
1713 uint16_t entrySelector = 0;
1714 uint16_t searchRange = 1;
1715 while (searchRange < numTables >> 1) {
1716 entrySelector++;
1717 searchRange <<= 1;
1718 }
1719 searchRange <<= 4;
1720 uint16_t rangeShift = (numTables << 4) - searchRange;
1721
1722 // write font header
1723 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1724 header->fontType = fontType;
1725 header->numTables = SkEndian_SwapBE16(numTables);
1726 header->searchRange = SkEndian_SwapBE16(searchRange);
1727 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1728 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1729 dataPtr += sizeof(SkSFNTHeader);
1730
1731 // write tables
1732 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1733 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1734 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1735 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001736 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001737 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1738 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1739 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001740 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1741 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001742
1743 dataPtr += (tableSize + 3) & ~3;
1744 ++entry;
1745 }
1746
bungemanb3310c22015-03-02 09:05:36 -08001747 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001748 return stream;
1749}
1750
bungeman41868fe2015-05-20 09:21:04 -07001751struct NonDefaultAxesContext {
1752 SkFixed* axisValue;
1753 CFArrayRef cgAxes;
1754};
1755static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1756 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1757
1758 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1759 return;
1760 }
1761
1762 // The key is a CFString which is a string from the 'name' table.
1763 // Search the cgAxes for an axis with this name, and use its index to store the value.
1764 CFIndex keyIndex = -1;
1765 CFStringRef keyString = static_cast<CFStringRef>(key);
1766 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1767 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1768 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1769 continue;
1770 }
1771
1772 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1773 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1774 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1775 continue;
1776 }
1777 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1778 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1779 keyIndex = i;
1780 break;
1781 }
1782 }
1783 if (keyIndex == -1) {
1784 return;
1785 }
1786
1787 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1788 double valueDouble;
1789 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1790 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1791 {
1792 return;
1793 }
1794 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1795}
1796static bool get_variations(CTFontRef fFontRef, CFIndex* cgAxisCount,
1797 SkAutoSTMalloc<4, SkFixed>* axisValues)
1798{
1799 // CTFontCopyVariationAxes and CTFontCopyVariation do not work when applied to fonts which
1800 // started life with CGFontCreateWithDataProvider (they simply always return NULL).
1801 // As a result, we are limited to CGFontCopyVariationAxes and CGFontCopyVariations.
1802 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1803
1804 AutoCFRelease<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont));
1805 // If a font has no variations CGFontCopyVariations returns NULL (instead of an empty dict).
1806 if (!cgVariations.get()) {
1807 return false;
1808 }
1809
1810 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont));
1811 *cgAxisCount = CFArrayGetCount(cgAxes);
1812 axisValues->reset(*cgAxisCount);
1813
1814 // Set all of the axes to their default values.
1815 // Fail if any default value cannot be determined.
1816 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
1817 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
1818 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1819 return false;
1820 }
1821
1822 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1823 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1824 kCGFontVariationAxisDefaultValue);
1825 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1826 return false;
1827 }
1828 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
1829 double axisDefaultValueDouble;
1830 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
1831 {
1832 return false;
1833 }
1834 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
1835 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
1836 {
1837 return false;
1838 }
1839 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
1840 }
1841
1842 // Override the default values with the given font's stated axis values.
1843 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
1844 CFDictionaryApplyFunction(cgVariations, set_non_default_axes, &c);
1845
1846 return true;
1847}
1848SkFontData* SkTypeface_Mac::onCreateFontData() const {
1849 int index;
1850 SkAutoTDelete<SkStreamAsset> stream(this->onOpenStream(&index));
1851
1852 CFIndex cgAxisCount;
1853 SkAutoSTMalloc<4, SkFixed> axisValues;
1854 if (get_variations(fFontRef, &cgAxisCount, &axisValues)) {
1855 return new SkFontData(stream.detach(), index, axisValues.get(), cgAxisCount);
1856 }
1857 return new SkFontData(stream.detach(), index, NULL, 0);
1858}
1859
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001860///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001861///////////////////////////////////////////////////////////////////////////////
1862
1863int SkTypeface_Mac::onGetUPEM() const {
1864 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1865 return CGFontGetUnitsPerEm(cgFont);
1866}
1867
bungeman@google.com839702b2013-08-07 17:09:22 +00001868SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001869 SkTypeface::LocalizedStrings* nameIter =
1870 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
1871 if (NULL == nameIter) {
1872 AutoCFRelease<CFStringRef> cfLanguage;
1873 AutoCFRelease<CFStringRef> cfFamilyName(
1874 CTFontCopyLocalizedName(fFontRef, kCTFontFamilyNameKey, &cfLanguage));
1875
1876 SkString skLanguage;
1877 SkString skFamilyName;
1878 if (cfLanguage.get()) {
1879 CFStringToSkString(cfLanguage.get(), &skLanguage);
1880 } else {
1881 skLanguage = "und"; //undetermined
1882 }
1883 if (cfFamilyName.get()) {
1884 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1885 }
1886
1887 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1888 }
1889 return nameIter;
1890}
1891
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001892// If, as is the case with web fonts, the CTFont data isn't available,
1893// the CGFont data may work. While the CGFont may always provide the
1894// right result, leave the CTFont code path to minimize disruption.
1895static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1896 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1897 kCTFontTableOptionNoOptions);
1898 if (NULL == data) {
1899 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
1900 data = CGFontCopyTableForTag(cgFont, tag);
1901 }
1902 return data;
1903}
1904
1905int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1906 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1907 kCTFontTableOptionNoOptions));
1908 if (NULL == cfArray) {
1909 return 0;
1910 }
reed@google.com7fa2a652014-01-27 13:42:58 +00001911 int count = SkToInt(CFArrayGetCount(cfArray));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001912 if (tags) {
1913 for (int i = 0; i < count; ++i) {
1914 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1915 tags[i] = static_cast<SkFontTableTag>(fontTag);
1916 }
1917 }
1918 return count;
1919}
1920
1921size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1922 size_t length, void* dstData) const {
1923 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
1924 if (NULL == srcData) {
1925 return 0;
1926 }
1927
1928 size_t srcSize = CFDataGetLength(srcData);
1929 if (offset >= srcSize) {
1930 return 0;
1931 }
1932 if (length > srcSize - offset) {
1933 length = srcSize - offset;
1934 }
1935 if (dstData) {
1936 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1937 }
1938 return length;
1939}
1940
1941SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001942 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001943}
1944
1945void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001946 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1947 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1948 {
1949 rec->fMaskFormat = SkMask::kA8_Format;
1950 // Render the glyphs as close as possible to what was requested.
1951 // The above turns off subpixel rendering, but the user requested it.
1952 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1953 // See comments below for more details.
1954 rec->setHinting(SkPaint::kNormal_Hinting);
1955 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001956
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001957 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00001958 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001959 SkScalerContext::kLCD_BGROrder_Flag |
1960 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001961
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001962 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001963
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001964 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001965
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001966 // Only two levels of hinting are supported.
1967 // kNo_Hinting means avoid CoreGraphics outline dilation.
1968 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1969 // If there is no lcd support, hinting (dilation) cannot be supported.
1970 SkPaint::Hinting hinting = rec->getHinting();
1971 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1972 hinting = SkPaint::kNo_Hinting;
1973 } else if (SkPaint::kFull_Hinting == hinting) {
1974 hinting = SkPaint::kNormal_Hinting;
1975 }
1976 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001977
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001978 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1979 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1980 // There is no current means to honor a request for unhinted lcd,
1981 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001982
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001983 // Hinting and smoothing should be orthogonal, but currently they are not.
1984 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
1985 // output is drawn from auto-dilated outlines (the amount of which is
1986 // determined by AppleFontSmoothing). Its regular anti-aliased output is
1987 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001988
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001989 // The behavior of Skia is as follows:
1990 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
1991 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
1992 // channel. This matches [LCD][yes-hint] in weight.
1993 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
1994 // Currenly side with LCD, effectively ignoring the hinting setting.
1995 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001996
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001997 if (isLCDFormat(rec->fMaskFormat)) {
1998 if (lcdSupport) {
1999 //CoreGraphics creates 555 masks for smoothed text anyway.
2000 rec->fMaskFormat = SkMask::kLCD16_Format;
2001 rec->setHinting(SkPaint::kNormal_Hinting);
2002 } else {
2003 rec->fMaskFormat = SkMask::kA8_Format;
2004 }
2005 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002006
bungeman34902632014-12-10 21:43:27 -08002007 // CoreText provides no information as to whether a glyph will be color or not.
2008 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
2009 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08002010 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08002011 rec->fMaskFormat = SkMask::kARGB32_Format;
2012 }
2013
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002014 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2015 // All other masks can use regular gamma.
2016 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
2017#ifndef SK_GAMMA_APPLY_TO_A8
2018 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002019#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002020 } else {
2021 //CoreGraphics dialates smoothed text as needed.
2022 rec->setContrast(0);
2023 }
2024}
2025
2026// we take ownership of the ref
2027static const char* get_str(CFStringRef ref, SkString* str) {
bungeman256b3512014-07-02 07:57:59 -07002028 if (NULL == ref) {
2029 return NULL;
2030 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002031 CFStringToSkString(ref, str);
2032 CFSafeRelease(ref);
2033 return str->c_str();
2034}
2035
bungemanb374d6a2014-09-17 07:48:59 -07002036void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
2037 get_str(CTFontCopyFamilyName(fFontRef), familyName);
2038}
2039
reed@google.com5526ede2013-03-25 13:03:37 +00002040void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2041 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002042 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002043
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002044 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
2045 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
2046 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
caseq26337e92014-06-30 12:14:52 -07002047 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002048}
reed@google.com5526ede2013-03-25 13:03:37 +00002049
reed@google.combcb42ae2013-07-02 13:56:39 +00002050int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002051 uint16_t glyphs[], int glyphCount) const
2052{
2053 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
2054 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
2055 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
skia.committer@gmail.com0673efe2013-10-26 07:01:53 +00002056
reed@google.combcb42ae2013-07-02 13:56:39 +00002057 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002058 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
2059 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00002060 switch (encoding) {
2061 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002062 const char* utf8 = reinterpret_cast<const char*>(chars);
2063 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2064 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00002065 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002066 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
2067 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00002068 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002069 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002070 break;
2071 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002072 case kUTF16_Encoding: {
2073 src = reinterpret_cast<const UniChar*>(chars);
2074 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00002075 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002076 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2077 ++extra;
2078 }
reed@google.combcb42ae2013-07-02 13:56:39 +00002079 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002080 srcCount = glyphCount + extra;
2081 break;
2082 }
2083 case kUTF32_Encoding: {
2084 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
2085 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2086 src = utf16;
2087 for (int i = 0; i < glyphCount; ++i) {
2088 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
2089 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002090 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002091 break;
2092 }
2093 }
2094
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002095 // If glyphs is NULL, CT still needs glyph storage for finding the first failure.
2096 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002097 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2098 uint16_t* macGlyphs = glyphs;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002099 if (NULL == macGlyphs || srcCount > glyphCount) {
2100 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002101 }
2102
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002103 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, srcCount);
2104
2105 // If there were any non-bmp, then copy and compact.
2106 // If 'glyphs' is NULL, then compact glyphStorage in-place.
2107 // If all are bmp and 'glyphs' is non-NULL, 'glyphs' already contains the compact glyphs.
2108 // If some are non-bmp and 'glyphs' is non-NULL, copy and compact into 'glyphs'.
2109 uint16_t* compactedGlyphs = glyphs;
2110 if (NULL == compactedGlyphs) {
2111 compactedGlyphs = macGlyphs;
2112 }
2113 if (srcCount > glyphCount) {
2114 int extra = 0;
2115 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002116 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002117 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2118 ++extra;
2119 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002120 }
2121 }
2122
2123 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002124 return glyphCount;
2125 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002126
2127 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002128 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002129 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002130 return i;
2131 }
2132 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002133 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002134 return glyphCount;
2135}
2136
2137int SkTypeface_Mac::onCountGlyphs() const {
reed@google.com7fa2a652014-01-27 13:42:58 +00002138 return SkToInt(CTFontGetGlyphCount(fFontRef));
reed@google.combcb42ae2013-07-02 13:56:39 +00002139}
2140
reed@google.com95625db2013-03-25 20:44:02 +00002141///////////////////////////////////////////////////////////////////////////////
2142///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002143
2144static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
2145 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
2146 if (NULL == ref.get()) {
2147 return false;
2148 }
2149 CFStringToSkString(ref, value);
2150 return true;
2151}
2152
reed@google.com95625db2013-03-25 20:44:02 +00002153#include "SkFontMgr.h"
2154
reed@google.com964988f2013-03-29 14:57:22 +00002155static inline int sqr(int value) {
2156 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2157 return value * value;
2158}
2159
2160// We normalize each axis (weight, width, italic) to be base-900
2161static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2162 return sqr(a.weight() - b.weight()) +
2163 sqr((a.width() - b.width()) * 100) +
2164 sqr((a.isItalic() != b.isItalic()) * 900);
2165}
2166
bungemana4c4a2d2014-10-20 13:33:19 -07002167static SkTypeface* createFromDesc(CFStringRef cfFamilyName, CTFontDescriptorRef desc) {
bungeman967937c2014-10-30 11:49:27 -07002168 NameStyle cacheRequest;
2169 SkString skFamilyName;
2170 CFStringToSkString(cfFamilyName, &skFamilyName);
2171 cacheRequest.fName = skFamilyName.c_str();
bungeman336fdf22014-11-10 07:48:55 -08002172 cacheRequest.fStyle = fontstyle_from_descriptor(desc);
reed@google.comdea7ee02013-03-28 14:12:10 +00002173
bungeman967937c2014-10-30 11:49:27 -07002174 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_NameStyle, &cacheRequest);
reed@google.comdea7ee02013-03-28 14:12:10 +00002175 if (face) {
2176 return face;
2177 }
2178
bungeman64fb51d2015-05-04 12:03:50 -07002179 AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, NULL));
2180 if (!ctFont) {
reed@google.comce8b3de2013-03-26 19:30:16 +00002181 return NULL;
2182 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002183
reed@google.comce8b3de2013-03-26 19:30:16 +00002184 bool isFixedPitch;
bungeman336fdf22014-11-10 07:48:55 -08002185 (void)computeStyleBits(ctFont, &isFixedPitch);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002186
bungeman78884012015-06-08 13:39:12 -07002187 face = SkNEW_ARGS(SkTypeface_Mac, (ctFont.detach(), NULL,
2188 cacheRequest.fStyle, isFixedPitch,
2189 skFamilyName.c_str(), false));
bungemana4c4a2d2014-10-20 13:33:19 -07002190 SkTypefaceCache::Add(face, face->fontStyle());
reed@google.comdea7ee02013-03-28 14:12:10 +00002191 return face;
reed@google.comce8b3de2013-03-26 19:30:16 +00002192}
2193
reed@google.com83787c52013-03-26 17:19:15 +00002194class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002195public:
reed@google.com83787c52013-03-26 17:19:15 +00002196 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
2197 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, NULL))
reed@google.comdea7ee02013-03-28 14:12:10 +00002198 , fFamilyName(familyName)
2199 , fCount(0) {
reed@google.com83787c52013-03-26 17:19:15 +00002200 CFRetain(familyName);
reed@google.comdea7ee02013-03-28 14:12:10 +00002201 if (NULL == fArray) {
2202 fArray = CFArrayCreate(NULL, NULL, 0, NULL);
2203 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002204 fCount = SkToInt(CFArrayGetCount(fArray));
reed@google.com83787c52013-03-26 17:19:15 +00002205 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002206
reed@google.com83787c52013-03-26 17:19:15 +00002207 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002208 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002209 CFRelease(fFamilyName);
2210 }
2211
mtklein36352bf2015-03-25 18:17:31 -07002212 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002213 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002214 }
2215
mtklein36352bf2015-03-25 18:17:31 -07002216 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002217 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002218 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2219 if (style) {
bungeman336fdf22014-11-10 07:48:55 -08002220 *style = fontstyle_from_descriptor(desc);
reed@google.com83787c52013-03-26 17:19:15 +00002221 }
2222 if (name) {
2223 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2224 name->reset();
2225 }
2226 }
2227 }
2228
mtklein36352bf2015-03-25 18:17:31 -07002229 SkTypeface* createTypeface(int index) override {
reed@google.com83787c52013-03-26 17:19:15 +00002230 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2231 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002232
reed@google.com83787c52013-03-26 17:19:15 +00002233 return createFromDesc(fFamilyName, desc);
2234 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002235
mtklein36352bf2015-03-25 18:17:31 -07002236 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002237 if (0 == fCount) {
2238 return NULL;
2239 }
2240 return createFromDesc(fFamilyName, findMatchingDesc(pattern));
2241 }
2242
reed@google.com83787c52013-03-26 17:19:15 +00002243private:
2244 CFArrayRef fArray;
2245 CFStringRef fFamilyName;
reed@google.comdea7ee02013-03-28 14:12:10 +00002246 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002247
2248 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2249 int bestMetric = SK_MaxS32;
2250 CTFontDescriptorRef bestDesc = NULL;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002251
reed@google.com964988f2013-03-29 14:57:22 +00002252 for (int i = 0; i < fCount; ++i) {
2253 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
bungeman336fdf22014-11-10 07:48:55 -08002254 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc));
reed@google.com964988f2013-03-29 14:57:22 +00002255 if (0 == metric) {
2256 return desc;
2257 }
2258 if (metric < bestMetric) {
2259 bestMetric = metric;
2260 bestDesc = desc;
2261 }
2262 }
2263 SkASSERT(bestDesc);
2264 return bestDesc;
2265 }
reed@google.com83787c52013-03-26 17:19:15 +00002266};
2267
2268class SkFontMgr_Mac : public SkFontMgr {
reed@google.com83787c52013-03-26 17:19:15 +00002269 CFArrayRef fNames;
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002270 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002271
2272 CFStringRef stringAt(int index) const {
2273 SkASSERT((unsigned)index < (unsigned)fCount);
2274 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2275 }
2276
reed@google.com964988f2013-03-29 14:57:22 +00002277 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2278 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2279 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2280 &kCFTypeDictionaryKeyCallBacks,
2281 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002282
reed@google.com964988f2013-03-29 14:57:22 +00002283 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002284
reed@google.com964988f2013-03-29 14:57:22 +00002285 AutoCFRelease<CTFontDescriptorRef> desc(
2286 CTFontDescriptorCreateWithAttributes(cfAttr));
2287 return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc));
2288 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002289
reed@google.com83787c52013-03-26 17:19:15 +00002290public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002291 SkFontMgr_Mac()
2292 : fNames(SkCTFontManagerCopyAvailableFontFamilyNames())
2293 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames)) : 0) {}
reed@google.com83787c52013-03-26 17:19:15 +00002294
2295 virtual ~SkFontMgr_Mac() {
2296 CFSafeRelease(fNames);
2297 }
reed@google.com95625db2013-03-25 20:44:02 +00002298
2299protected:
mtklein36352bf2015-03-25 18:17:31 -07002300 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002301 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002302 }
2303
mtklein36352bf2015-03-25 18:17:31 -07002304 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002305 if ((unsigned)index < (unsigned)fCount) {
2306 CFStringToSkString(this->stringAt(index), familyName);
2307 } else {
2308 familyName->reset();
2309 }
reed@google.com95625db2013-03-25 20:44:02 +00002310 }
2311
mtklein36352bf2015-03-25 18:17:31 -07002312 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002313 if ((unsigned)index >= (unsigned)fCount) {
2314 return NULL;
2315 }
reed@google.com964988f2013-03-29 14:57:22 +00002316 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002317 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002318
mtklein36352bf2015-03-25 18:17:31 -07002319 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
reed@google.com964988f2013-03-29 14:57:22 +00002320 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2321 return CreateSet(cfName);
2322 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002323
reed@google.com95625db2013-03-25 20:44:02 +00002324 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002325 const SkFontStyle&) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002326 return NULL;
2327 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002328
djsollen33068c12014-11-14 10:52:53 -08002329 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2330 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002331 SkUnichar character) const override {
djsollen33068c12014-11-14 10:52:53 -08002332 return NULL;
2333 }
2334
reed@google.com95625db2013-03-25 20:44:02 +00002335 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002336 const SkFontStyle&) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002337 return NULL;
2338 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002339
mtklein36352bf2015-03-25 18:17:31 -07002340 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002341 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
2342 if (NULL == pr) {
2343 return NULL;
2344 }
2345 return create_from_dataProvider(pr);
2346 }
2347
mtklein36352bf2015-03-25 18:17:31 -07002348 SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002349 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
2350 if (NULL == pr) {
2351 return NULL;
2352 }
2353 return create_from_dataProvider(pr);
2354 }
2355
bungeman41868fe2015-05-20 09:21:04 -07002356 static CFDictionaryRef get_axes(CGFontRef cg, SkFontData* fontData) {
2357 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
2358 if (!cgAxes) {
2359 return NULL;
2360 }
2361
2362 CFIndex axisCount = CFArrayGetCount(cgAxes);
2363 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
2364 return NULL;
2365 }
2366
2367 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2368 &kCFTypeDictionaryKeyCallBacks,
2369 &kCFTypeDictionaryValueCallBacks);
2370 for (int i = 0; i < fontData->getAxisCount(); ++i) {
2371 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i);
2372 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2373 return NULL;
2374 }
2375 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2376
2377 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2378 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2379 return NULL;
2380 }
2381
2382 // The variation axes can be set to any value, but cg will effectively pin them.
2383 // Pin them here to normalize.
2384 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2385 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2386 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2387 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2388 {
2389 return NULL;
2390 }
2391 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2392 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2393 double minDouble;
2394 double maxDouble;
2395 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2396 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2397 {
2398 return NULL;
2399 }
2400 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
2401 CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType,
2402 &value);
2403
2404 CFDictionaryAddValue(dict, axisName, valueNumber);
2405 CFRelease(valueNumber);
2406 }
2407 return dict;
2408 }
2409 SkTypeface* onCreateFromFontData(SkFontData* data) const override {
2410 SkAutoTDelete<SkFontData> fontData(data);
2411 SkStreamAsset* stream = fontData->detachStream();
2412 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
2413 if (NULL == provider) {
2414 return NULL;
2415 }
2416 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
2417 if (NULL == cg) {
2418 return NULL;
2419 }
2420
2421 AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, fontData));
2422 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2423 // created from a data provider does not appear to have any ownership of the underlying
2424 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
2425 AutoCFRelease<CGFontRef> cgVariant;
2426 if (cgVariations) {
2427 cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations));
2428 } else {
2429 cgVariant.reset(cg.detach());
2430 }
2431
2432 CTFontRef ct = CTFontCreateWithGraphicsFont(cgVariant, 0, NULL, NULL);
2433 if (!ct) {
2434 return NULL;
2435 }
bungeman78884012015-06-08 13:39:12 -07002436 return NewFromFontRef(ct, cg.detach(), NULL, true);
bungeman41868fe2015-05-20 09:21:04 -07002437 }
2438
mtklein36352bf2015-03-25 18:17:31 -07002439 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002440 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2441 if (NULL == pr) {
2442 return NULL;
2443 }
2444 return create_from_dataProvider(pr);
2445 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002446
reed@google.com7fdcd442013-07-30 21:25:49 +00002447 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002448 unsigned styleBits) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002449
2450 SkFontStyle style = SkFontStyle((SkTypeface::Style)styleBits);
2451 if (familyName) {
2452 familyName = map_css_names(familyName);
2453 }
2454
2455 if (!familyName || !*familyName) {
2456 familyName = FONT_DEFAULT_NAME;
2457 }
2458
bungeman967937c2014-10-30 11:49:27 -07002459 NameStyle cacheRequest = { familyName, style };
2460 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_NameStyle, &cacheRequest);
bungemana4c4a2d2014-10-20 13:33:19 -07002461
2462 if (NULL == face) {
2463 face = NewFromName(familyName, style);
2464 if (face) {
2465 SkTypefaceCache::Add(face, style);
2466 } else {
2467 face = GetDefaultFace();
2468 face->ref();
2469 }
2470 }
2471 return face;
reed@google.com7fdcd442013-07-30 21:25:49 +00002472 }
reed@google.com95625db2013-03-25 20:44:02 +00002473};
2474
reed@google.com7fdcd442013-07-30 21:25:49 +00002475///////////////////////////////////////////////////////////////////////////////
2476
reed@google.com95625db2013-03-25 20:44:02 +00002477SkFontMgr* SkFontMgr::Factory() {
2478 return SkNEW(SkFontMgr_Mac);
2479}