blob: 7a46d40ec649c8d6c2c57a4063dd33e06e991042 [file] [log] [blame]
reed@android.com0680d6c2008-12-19 19:46:15 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
mtkleinde9e7a72015-03-11 12:01:25 -07008#include "SkTypes.h" // Keep this before any #ifdef ...
mtklein1ee76512015-11-02 10:20:27 -08009#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
mtkleinde9e7a72015-03-11 12:01:25 -070010
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"
Hal Canary95e3c052017-01-11 12:44:43 -050023#include "SkAutoMalloc.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000024#include "SkCGUtils.h"
25#include "SkColorPriv.h"
26#include "SkDescriptor.h"
27#include "SkEndian.h"
jvanverth02802f62015-07-02 06:42:49 -070028#include "SkFloatingPoint.h"
mtklein1b249332015-07-07 12:21:21 -070029#include "SkFontDescriptor.h"
30#include "SkFontMgr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000031#include "SkGlyph.h"
bungemanf93d7112016-09-16 06:24:20 -070032#include "SkMakeUnique.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000033#include "SkMaskGamma.h"
halcanary4dbbd042016-06-07 17:21:10 -070034#include "SkMathPriv.h"
mtklein1b249332015-07-07 12:21:21 -070035#include "SkMutex.h"
bungeman4ec46aa2017-02-15 17:49:12 -050036#include "SkOTTable_OS_2.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000037#include "SkOTUtils.h"
mtkleinffa4a922016-05-05 16:05:56 -070038#include "SkOnce.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000039#include "SkPaint.h"
40#include "SkPath.h"
mtklein1b249332015-07-07 12:21:21 -070041#include "SkSFNTHeader.h"
jvanverth02802f62015-07-02 06:42:49 -070042#include "SkStream.h"
mtklein1b249332015-07-07 12:21:21 -070043#include "SkString.h"
bungemane280d062016-03-24 11:27:05 -070044#include "SkTemplates.h"
mtklein1b249332015-07-07 12:21:21 -070045#include "SkTypefaceCache.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000046#include "SkTypeface_mac.h"
47#include "SkUtils.h"
reed@google.comf77b35d2013-05-02 20:39:44 +000048
reedd0f41732015-07-10 12:08:38 -070049// Experimental code to use a global lock whenever we access CG, to see if this reduces
50// crashes in Chrome
51#define USE_GLOBAL_MUTEX_FOR_CG_ACCESS
52
53#ifdef USE_GLOBAL_MUTEX_FOR_CG_ACCESS
reed086eea92016-05-04 17:12:46 -070054 SK_DECLARE_STATIC_MUTEX(gCGMutex);
reedd0f41732015-07-10 12:08:38 -070055 #define AUTO_CG_LOCK() SkAutoMutexAcquire amc(gCGMutex)
56#else
57 #define AUTO_CG_LOCK()
58#endif
59
bungeman3b4b66c2015-01-08 08:33:44 -080060// Set to make glyph bounding boxes visible.
61#define SK_SHOW_TEXT_BLIT_COVERAGE 0
62
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000063class SkScalerContext_Mac;
64
bungemanc292b5f2016-12-20 12:04:29 -050065struct CFSafeRelease {
66 void operator()(CFTypeRef cfTypeRef) {
67 if (cfTypeRef) {
68 CFRelease(cfTypeRef);
69 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000070 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000071};
bungemanc292b5f2016-12-20 12:04:29 -050072template <typename CFRef> using UniqueCFRef =
73 std::unique_ptr<skstd::remove_pointer_t<CFRef>, CFSafeRelease>;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000074
bungemanc292b5f2016-12-20 12:04:29 -050075static UniqueCFRef<CFStringRef> make_CFString(const char str[]) {
76 return UniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, str, kCFStringEncodingUTF8));
reed@google.com964988f2013-03-29 14:57:22 +000077}
78
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000079// inline versions of these rect helpers
80
81static bool CGRectIsEmpty_inline(const CGRect& rect) {
82 return rect.size.width <= 0 || rect.size.height <= 0;
83}
84
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000085static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
86 return rect.origin.x;
87}
88
89static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
90 return rect.origin.x + rect.size.width;
91}
92
93static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
94 return rect.origin.y;
95}
96
97static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
98 return rect.origin.y + rect.size.height;
99}
100
101static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
102 return rect.size.width;
103}
104
105///////////////////////////////////////////////////////////////////////////////
106
107static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
reed@google.com7fa2a652014-01-27 13:42:58 +0000108 int width, int height, size_t rowBytes) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000109 SkASSERT(width);
110 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
111
112 if (width >= 32) {
113 while (height) {
114 sk_memset32(ptr, value, width);
115 ptr = (uint32_t*)((char*)ptr + rowBytes);
116 height -= 1;
117 }
118 return;
119 }
120
121 rowBytes -= width * sizeof(uint32_t);
122
123 if (width >= 8) {
124 while (height) {
125 int w = width;
126 do {
127 *ptr++ = value; *ptr++ = value;
128 *ptr++ = value; *ptr++ = value;
129 *ptr++ = value; *ptr++ = value;
130 *ptr++ = value; *ptr++ = value;
131 w -= 8;
132 } while (w >= 8);
133 while (--w >= 0) {
134 *ptr++ = value;
135 }
136 ptr = (uint32_t*)((char*)ptr + rowBytes);
137 height -= 1;
138 }
139 } else {
140 while (height) {
141 int w = width;
142 do {
143 *ptr++ = value;
144 } while (--w > 0);
145 ptr = (uint32_t*)((char*)ptr + rowBytes);
146 height -= 1;
147 }
148 }
149}
150
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000151typedef uint32_t CGRGBPixel;
152
153static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
154 return pixel & 0xFF;
155}
156
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000157static CGFloat ScalarToCG(SkScalar scalar) {
158 if (sizeof(CGFloat) == sizeof(float)) {
159 return SkScalarToFloat(scalar);
160 } else {
161 SkASSERT(sizeof(CGFloat) == sizeof(double));
162 return (CGFloat) SkScalarToDouble(scalar);
163 }
164}
165
166static SkScalar CGToScalar(CGFloat cgFloat) {
167 if (sizeof(CGFloat) == sizeof(float)) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700168 return SkFloatToScalar(cgFloat);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000169 } else {
170 SkASSERT(sizeof(CGFloat) == sizeof(double));
171 return SkDoubleToScalar(cgFloat);
172 }
173}
174
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700175static float CGToFloat(CGFloat cgFloat) {
176 if (sizeof(CGFloat) == sizeof(float)) {
177 return cgFloat;
178 } else {
179 SkASSERT(sizeof(CGFloat) == sizeof(double));
180 return static_cast<float>(cgFloat);
181 }
182}
183
bungemanc292b5f2016-12-20 12:04:29 -0500184static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) {
185 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX]),
186 -ScalarToCG(matrix[SkMatrix::kMSkewY] ),
187 -ScalarToCG(matrix[SkMatrix::kMSkewX] ),
188 ScalarToCG(matrix[SkMatrix::kMScaleY]),
189 ScalarToCG(matrix[SkMatrix::kMTransX]),
190 ScalarToCG(matrix[SkMatrix::kMTransY]));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000191}
192
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000193///////////////////////////////////////////////////////////////////////////////
194
195#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000196
197/**
198 * There does not appear to be a publicly accessable API for determining if lcd
199 * font smoothing will be applied if we request it. The main issue is that if
200 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
201 */
202static bool supports_LCD() {
203 static int gSupportsLCD = -1;
204 if (gSupportsLCD >= 0) {
205 return (bool) gSupportsLCD;
206 }
207 uint32_t rgb = 0;
bungemanc292b5f2016-12-20 12:04:29 -0500208 UniqueCFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
209 UniqueCFRef<CGContextRef> cgContext(
210 CGBitmapContextCreate(&rgb, 1, 1, 8, 4, colorspace.get(), BITMAP_INFO_RGB));
211 UniqueCFRef<CTFontRef> ctFont(CTFontCreateWithName(CFSTR("Helvetica"), 16, nullptr));
212 CGContextSetShouldSmoothFonts(cgContext.get(), true);
213 CGContextSetShouldAntialias(cgContext.get(), true);
214 CGContextSetTextDrawingMode(cgContext.get(), kCGTextFill);
215 CGContextSetGrayFillColor(cgContext.get(), 1, 1);
ccameronf8ee5b42016-10-04 15:02:02 -0700216 CGPoint point = CGPointMake(-1, 0);
217 static const UniChar pipeChar = '|';
218 CGGlyph pipeGlyph;
bungemanc292b5f2016-12-20 12:04:29 -0500219 CTFontGetGlyphsForCharacters(ctFont.get(), &pipeChar, &pipeGlyph, 1);
220 CTFontDrawGlyphs(ctFont.get(), &pipeGlyph, &point, 1, cgContext.get());
ccameronf8ee5b42016-10-04 15:02:02 -0700221
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000222 uint32_t r = (rgb >> 16) & 0xFF;
223 uint32_t g = (rgb >> 8) & 0xFF;
224 uint32_t b = (rgb >> 0) & 0xFF;
225 gSupportsLCD = (r != g || r != b);
226 return (bool) gSupportsLCD;
227}
228
229class Offscreen {
230public:
bungeman34902632014-12-10 21:43:27 -0800231 Offscreen()
halcanary96fcdcc2015-08-27 07:41:13 -0700232 : fRGBSpace(nullptr)
233 , fCG(nullptr)
bungeman34902632014-12-10 21:43:27 -0800234 , fDoAA(false)
235 , fDoLCD(false)
236 {
237 fSize.set(0, 0);
238 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000239
240 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
bungemanc292b5f2016-12-20 12:04:29 -0500241 CGGlyph glyphID, size_t* rowBytesPtr, bool generateA8FromLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000242
243private:
244 enum {
245 kSize = 32 * 32 * sizeof(CGRGBPixel)
246 };
247 SkAutoSMalloc<kSize> fImageStorage;
bungemanc292b5f2016-12-20 12:04:29 -0500248 UniqueCFRef<CGColorSpaceRef> fRGBSpace;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000249
250 // cached state
bungemanc292b5f2016-12-20 12:04:29 -0500251 UniqueCFRef<CGContextRef> fCG;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000252 SkISize fSize;
253 bool fDoAA;
254 bool fDoLCD;
255
256 static int RoundSize(int dimension) {
257 return SkNextPow2(dimension);
258 }
259};
260
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000261///////////////////////////////////////////////////////////////////////////////
262
bungeman6e45bda2016-07-25 15:11:49 -0700263static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
bungemana4c4a2d2014-10-20 13:33:19 -0700264 CFNumberRef num;
265 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
bungemanc292b5f2016-12-20 12:04:29 -0500266 && CFNumberIsFloatType(num)
267 && CFNumberGetValue(num, kCFNumberCGFloatType, value);
bungemana4c4a2d2014-10-20 13:33:19 -0700268}
269
bungeman6e45bda2016-07-25 15:11:49 -0700270template <typename S, typename D, typename C> struct LinearInterpolater {
271 struct Mapping {
272 S src_val;
273 D dst_val;
274 };
275 constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
276 : fMapping(mapping), fMappingCount(mappingCount) {}
277
278 static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
279 SkASSERT(src_min < src_max);
280 SkASSERT(dst_min <= dst_max);
281 return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000282 }
bungeman6e45bda2016-07-25 15:11:49 -0700283
bungemanf1ef1fa2017-02-09 14:23:46 -0500284 D map(S val) const {
bungeman6e45bda2016-07-25 15:11:49 -0700285 // -Inf to [0]
286 if (val < fMapping[0].src_val) {
287 return fMapping[0].dst_val;
288 }
289
290 // Linear from [i] to [i+1]
291 for (int i = 0; i < fMappingCount - 1; ++i) {
292 if (val < fMapping[i+1].src_val) {
293 return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
294 fMapping[i].dst_val, fMapping[i+1].dst_val);
295 }
296 }
297
298 // From [n] to +Inf
299 // if (fcweight < Inf)
300 return fMapping[fMappingCount - 1].dst_val;
301 }
302
303 Mapping const * fMapping;
304 int fMappingCount;
305};
306
307struct RoundCGFloatToInt {
308 int operator()(CGFloat s) { return s + 0.5; }
309};
bungemanf1ef1fa2017-02-09 14:23:46 -0500310struct CGFloatIdentity {
311 CGFloat operator()(CGFloat s) { return s; }
312};
313
314/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
315 *
316 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
317 * CTFont is native or created from a CGDataProvider.
318 */
319static CGFloat fontstyle_to_ct_weight(int fontstyleWeight) {
320 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
321
322 // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
323 // However, on this end we can't tell, so this is ignored.
324
325 /** This mapping for native fonts is determined by running the following in an .mm file
326 * #include <AppKit/AppKit>
327 * printf("{ 100, % #.2f },\n", NSFontWeightUltraLight);
328 * printf("{ 200, % #.2f },\n", NSFontWeightThin);
329 * printf("{ 300, % #.2f },\n", NSFontWeightLight);
330 * printf("{ 400, % #.2f },\n", NSFontWeightRegular);
331 * printf("{ 500, % #.2f },\n", NSFontWeightMedium);
332 * printf("{ 600, % #.2f },\n", NSFontWeightSemibold);
333 * printf("{ 700, % #.2f },\n", NSFontWeightBold);
334 * printf("{ 800, % #.2f },\n", NSFontWeightHeavy);
335 * printf("{ 900, % #.2f },\n", NSFontWeightBlack);
336 */
337 static constexpr Interpolator::Mapping nativeWeightMappings[] = {
338 { 0, -1.00 },
339 { 100, -0.80 },
340 { 200, -0.60 },
341 { 300, -0.40 },
342 { 400, 0.00 },
343 { 500, 0.23 },
344 { 600, 0.30 },
345 { 700, 0.40 },
346 { 800, 0.56 },
347 { 900, 0.62 },
348 { 1000, 1.00 },
349 };
350 static constexpr Interpolator nativeInterpolator(
351 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
352
353 return nativeInterpolator.map(fontstyleWeight);
354}
355
bungeman6e45bda2016-07-25 15:11:49 -0700356
bungeman2873c762017-01-13 11:40:21 -0500357/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
358 *
359 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
360 * CTFont is native or created from a CGDataProvider.
361 */
362static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
bungeman6e45bda2016-07-25 15:11:49 -0700363 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
364
bungeman6e45bda2016-07-25 15:11:49 -0700365 // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
bungeman2873c762017-01-13 11:40:21 -0500366 // However, on this end we can't tell, so this is ignored.
367
368 /** This mapping for CGDataProvider created fonts is determined by creating font data with every
369 * weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
370 * in tests/TypefaceTest.cpp for the code used to determine these values.
371 */
372 static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
bungeman6e45bda2016-07-25 15:11:49 -0700373 { -1.00, 0 },
374 { -0.70, 100 },
375 { -0.50, 200 },
376 { -0.23, 300 },
377 { 0.00, 400 },
378 { 0.20, 500 },
379 { 0.30, 600 },
380 { 0.40, 700 },
381 { 0.60, 800 },
382 { 0.80, 900 },
383 { 1.00, 1000 },
384 };
bungeman2873c762017-01-13 11:40:21 -0500385 static constexpr Interpolator dataProviderInterpolator(
386 dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
387
388 /** This mapping for native fonts is determined by running the following in an .mm file
389 * #include <AppKit/AppKit>
390 * printf("{ % #.2f, 100 },\n", NSFontWeightUltraLight);
391 * printf("{ % #.2f, 200 },\n", NSFontWeightThin);
392 * printf("{ % #.2f, 300 },\n", NSFontWeightLight);
393 * printf("{ % #.2f, 400 },\n", NSFontWeightRegular);
394 * printf("{ % #.2f, 500 },\n", NSFontWeightMedium);
395 * printf("{ % #.2f, 600 },\n", NSFontWeightSemibold);
396 * printf("{ % #.2f, 700 },\n", NSFontWeightBold);
397 * printf("{ % #.2f, 800 },\n", NSFontWeightHeavy);
398 * printf("{ % #.2f, 900 },\n", NSFontWeightBlack);
399 */
400 static constexpr Interpolator::Mapping nativeWeightMappings[] = {
401 { -1.00, 0 },
402 { -0.80, 100 },
403 { -0.60, 200 },
404 { -0.40, 300 },
405 { 0.00, 400 },
406 { 0.23, 500 },
407 { 0.30, 600 },
408 { 0.40, 700 },
409 { 0.56, 800 },
410 { 0.62, 900 },
411 { 1.00, 1000 },
412 };
413 static constexpr Interpolator nativeInterpolator(
414 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
415
416 return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
417 : nativeInterpolator.map(cgWeight);
bungemana4c4a2d2014-10-20 13:33:19 -0700418}
419
bungemanf1ef1fa2017-02-09 14:23:46 -0500420/** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
421static int fontstyle_to_ct_width(int fontstyleWidth) {
422 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
423
424 // Values determined by creating font data with every width, creating a CTFont,
425 // and asking the CTFont for its width. See TypefaceStyle test for basics.
426 static constexpr Interpolator::Mapping widthMappings[] = {
427 { 0, -0.5 },
428 { 10, 0.5 },
429 };
430 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
431 return interpolator.map(fontstyleWidth);
432}
433
434/** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
bungeman6e45bda2016-07-25 15:11:49 -0700435static int ct_width_to_fontstyle(CGFloat cgWidth) {
436 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
437
438 // Values determined by creating font data with every width, creating a CTFont,
439 // and asking the CTFont for its width. See TypefaceStyle test for basics.
440 static constexpr Interpolator::Mapping widthMappings[] = {
441 { -0.5, 0 },
442 { 0.5, 10 },
443 };
bungeman2873c762017-01-13 11:40:21 -0500444 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
445 return interpolator.map(cgWidth);
bungemana4c4a2d2014-10-20 13:33:19 -0700446}
447
bungeman2873c762017-01-13 11:40:21 -0500448static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc, bool fromDataProvider) {
bungemanc292b5f2016-12-20 12:04:29 -0500449 UniqueCFRef<CFTypeRef> fontTraits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
450 if (!fontTraits || CFDictionaryGetTypeID() != CFGetTypeID(fontTraits.get())) {
bungemana4c4a2d2014-10-20 13:33:19 -0700451 return SkFontStyle();
452 }
bungemanc292b5f2016-12-20 12:04:29 -0500453 UniqueCFRef<CFDictionaryRef> fontTraitsDict(static_cast<CFDictionaryRef>(fontTraits.release()));
bungemana4c4a2d2014-10-20 13:33:19 -0700454
bungeman6e45bda2016-07-25 15:11:49 -0700455 CGFloat weight, width, slant;
bungemanc292b5f2016-12-20 12:04:29 -0500456 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800457 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700458 }
bungemanc292b5f2016-12-20 12:04:29 -0500459 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWidthTrait, &width)) {
bungemana4c4a2d2014-10-20 13:33:19 -0700460 width = 0;
461 }
bungemanc292b5f2016-12-20 12:04:29 -0500462 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800463 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700464 }
465
bungeman2873c762017-01-13 11:40:21 -0500466 return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
bungeman6e45bda2016-07-25 15:11:49 -0700467 ct_width_to_fontstyle(width),
bungemana4c4a2d2014-10-20 13:33:19 -0700468 slant ? SkFontStyle::kItalic_Slant
bungemanb4bb7d82016-04-27 10:21:04 -0700469 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000470}
471
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000472class SkTypeface_Mac : public SkTypeface {
473public:
bungemanc292b5f2016-12-20 12:04:29 -0500474 SkTypeface_Mac(UniqueCFRef<CTFontRef> fontRef, UniqueCFRef<CFTypeRef> resourceRef,
bungeman78884012015-06-08 13:39:12 -0700475 const SkFontStyle& fs, bool isFixedPitch,
bungeman53d5c6e2016-04-08 07:22:29 -0700476 bool isLocalStream)
bungemane3aea102016-07-13 05:16:58 -0700477 : SkTypeface(fs, isFixedPitch)
bungemanc292b5f2016-12-20 12:04:29 -0500478 , fFontRef(std::move(fontRef))
479 , fOriginatingCFTypeRef(std::move(resourceRef))
480 , fHasColorGlyphs(
481 SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
caseq26337e92014-06-30 12:14:52 -0700482 , fIsLocalStream(isLocalStream)
reed@google.comce8b3de2013-03-26 19:30:16 +0000483 {
bungemanc292b5f2016-12-20 12:04:29 -0500484 SkASSERT(fFontRef);
reed@google.comce8b3de2013-03-26 19:30:16 +0000485 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000486
bungemanc292b5f2016-12-20 12:04:29 -0500487 UniqueCFRef<CTFontRef> fFontRef;
488 UniqueCFRef<CFTypeRef> fOriginatingCFTypeRef;
bungemane3bea5c2015-04-07 07:34:36 -0700489 const bool fHasColorGlyphs;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000490
491protected:
mtklein36352bf2015-03-25 18:17:31 -0700492 int onGetUPEM() const override;
493 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
bungemanf93d7112016-09-16 06:24:20 -0700494 std::unique_ptr<SkFontData> onMakeFontData() const override;
mtklein36352bf2015-03-25 18:17:31 -0700495 void onGetFamilyName(SkString* familyName) const override;
496 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
497 int onGetTableTags(SkFontTableTag tags[]) const override;
bungemanc292b5f2016-12-20 12:04:29 -0500498 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
reeda9322c22016-04-12 06:47:05 -0700499 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
500 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700501 void onFilterRec(SkScalerContextRec*) const override;
502 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
bungemanc292b5f2016-12-20 12:04:29 -0500503 SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
504 PerGlyphInfo, const uint32_t* glyphIDs, uint32_t glyphIDsCount) const override;
505 int onCharsToGlyphs(const void* chars, Encoding,
506 uint16_t glyphs[], int glyphCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700507 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000508
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000509private:
caseq26337e92014-06-30 12:14:52 -0700510 bool fIsLocalStream;
reed@google.comce8b3de2013-03-26 19:30:16 +0000511
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000512 typedef SkTypeface INHERITED;
513};
514
bungeman82a455f2016-04-14 08:04:45 -0700515static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
bungeman64fb51d2015-05-04 12:03:50 -0700516 CTFontRef self = (CTFontRef)context;
bungemanc292b5f2016-12-20 12:04:29 -0500517 CTFontRef other = ((SkTypeface_Mac*)cached)->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000518
bungeman64fb51d2015-05-04 12:03:50 -0700519 return CFEqual(self, other);
520}
521
bungemanc292b5f2016-12-20 12:04:29 -0500522/** Creates a typeface, searching the cache if isLocalStream is false. */
523static SkTypeface* create_from_CTFontRef(UniqueCFRef<CTFontRef> font,
524 UniqueCFRef<CFTypeRef> resource,
525 bool isLocalStream) {
526 SkASSERT(font);
bungemanbea97482016-08-24 08:29:50 -0700527
528 if (!isLocalStream) {
529 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)font.get());
530 if (face) {
531 return face;
532 }
533 }
534
bungemanc292b5f2016-12-20 12:04:29 -0500535 UniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
bungeman2873c762017-01-13 11:40:21 -0500536 SkFontStyle style = fontstyle_from_descriptor(desc.get(), isLocalStream);
bungemanc292b5f2016-12-20 12:04:29 -0500537 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
bungemanbea97482016-08-24 08:29:50 -0700538 bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
539
bungemanc292b5f2016-12-20 12:04:29 -0500540 SkTypeface* face = new SkTypeface_Mac(std::move(font), std::move(resource),
bungemanbea97482016-08-24 08:29:50 -0700541 style, isFixedPitch, isLocalStream);
542 if (!isLocalStream) {
543 SkTypefaceCache::Add(face);
544 }
545 return face;
546}
547
548/** Creates a typeface from a descriptor, searching the cache. */
549static SkTypeface* create_from_desc(CTFontDescriptorRef desc) {
bungemanc292b5f2016-12-20 12:04:29 -0500550 UniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -0700551 if (!ctFont) {
552 return nullptr;
553 }
554
bungemanc292b5f2016-12-20 12:04:29 -0500555 return create_from_CTFontRef(std::move(ctFont), nullptr, false);
bungemanbea97482016-08-24 08:29:50 -0700556}
557
bungemanc292b5f2016-12-20 12:04:29 -0500558static UniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
559 const SkFontStyle& style) {
bungemanc292b5f2016-12-20 12:04:29 -0500560 UniqueCFRef<CFMutableDictionaryRef> cfAttributes(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000561 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
562 &kCFTypeDictionaryKeyCallBacks,
563 &kCFTypeDictionaryValueCallBacks));
564
bungemanc292b5f2016-12-20 12:04:29 -0500565 UniqueCFRef<CFMutableDictionaryRef> cfTraits(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000566 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
567 &kCFTypeDictionaryKeyCallBacks,
568 &kCFTypeDictionaryValueCallBacks));
569
bungeman83f1f442017-02-06 13:21:33 -0500570 if (!cfAttributes || !cfTraits) {
halcanary96fcdcc2015-08-27 07:41:13 -0700571 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000572 }
573
bungemanf1ef1fa2017-02-09 14:23:46 -0500574 // CTFontTraits (symbolic)
bungeman83f1f442017-02-06 13:21:33 -0500575 CTFontSymbolicTraits ctFontTraits = 0;
576 if (style.weight() >= SkFontStyle::kBold_Weight) {
577 ctFontTraits |= kCTFontBoldTrait;
578 }
579 if (style.slant() != SkFontStyle::kUpright_Slant) {
580 ctFontTraits |= kCTFontItalicTrait;
581 }
582 UniqueCFRef<CFNumberRef> cfFontTraits(
583 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
584 if (cfFontTraits) {
585 CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get());
bungeman83f1f442017-02-06 13:21:33 -0500586 }
bungemanf1ef1fa2017-02-09 14:23:46 -0500587 // CTFontTraits (weight)
588 CGFloat ctWeight = fontstyle_to_ct_weight(style.weight());
589 UniqueCFRef<CFNumberRef> cfFontWeight(
590 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
591 if (cfFontWeight) {
592 CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
593 }
594 // CTFontTraits (width)
595 CGFloat ctWidth = fontstyle_to_ct_width(style.weight());
596 UniqueCFRef<CFNumberRef> cfFontWidth(
597 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
598 if (cfFontWidth) {
599 CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
600 }
601 // CTFontTraits (slant)
602 CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1;
603 UniqueCFRef<CFNumberRef> cfFontSlant(
604 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
605 if (cfFontSlant) {
606 CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
607 }
608 // CTFontTraits
609 CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
bungeman83f1f442017-02-06 13:21:33 -0500610
611 // CTFontFamilyName
612 if (familyName) {
613 UniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
614 if (cfFontName) {
615 CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
616 }
617 }
bungeman64fb51d2015-05-04 12:03:50 -0700618
bungemanc292b5f2016-12-20 12:04:29 -0500619 return UniqueCFRef<CTFontDescriptorRef>(
620 CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
bungemanbea97482016-08-24 08:29:50 -0700621}
622
623/** Creates a typeface from a name, searching the cache. */
624static SkTypeface* create_from_name(const char familyName[], const SkFontStyle& style) {
bungemanc292b5f2016-12-20 12:04:29 -0500625 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
bungemanbea97482016-08-24 08:29:50 -0700626 if (!desc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700627 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700628 }
bungemanc292b5f2016-12-20 12:04:29 -0500629 return create_from_desc(desc.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000630}
631
632///////////////////////////////////////////////////////////////////////////////
633
634extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
635CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
636 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
halcanary96fcdcc2015-08-27 07:41:13 -0700637 return macface ? macface->fFontRef.get() : nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000638}
639
640/* This function is visible on the outside. It first searches the cache, and if
641 * not found, returns a new entry (after adding it to the cache).
642 */
bungemanc292b5f2016-12-20 12:04:29 -0500643SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef font, CFTypeRef resource) {
644 CFRetain(font);
645 if (resource) {
646 CFRetain(resource);
bungeman53d5c6e2016-04-08 07:22:29 -0700647 }
bungemanc292b5f2016-12-20 12:04:29 -0500648 return create_from_CTFontRef(UniqueCFRef<CTFontRef>(font),
649 UniqueCFRef<CFTypeRef>(resource),
650 false);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000651}
652
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000653static const char* map_css_names(const char* name) {
654 static const struct {
655 const char* fFrom; // name the caller specified
656 const char* fTo; // "canonical" name we map to
657 } gPairs[] = {
658 { "sans-serif", "Helvetica" },
659 { "serif", "Times" },
660 { "monospace", "Courier" }
661 };
662
663 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
664 if (strcmp(name, gPairs[i].fFrom) == 0) {
665 return gPairs[i].fTo;
666 }
667 }
668 return name; // no change
669}
670
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000671///////////////////////////////////////////////////////////////////////////////
672
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000673class SkScalerContext_Mac : public SkScalerContext {
674public:
bungeman7cfd46a2016-10-20 16:06:52 -0400675 SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000676
677protected:
mtklein36352bf2015-03-25 18:17:31 -0700678 unsigned generateGlyphCount(void) override;
679 uint16_t generateCharToGlyph(SkUnichar uni) override;
680 void generateAdvance(SkGlyph* glyph) override;
681 void generateMetrics(SkGlyph* glyph) override;
682 void generateImage(const SkGlyph& glyph) override;
Ben Wagner6e9ac122016-11-11 14:31:06 -0500683 void generatePath(SkGlyphID glyph, SkPath* path) override;
mtklein36352bf2015-03-25 18:17:31 -0700684 void generateFontMetrics(SkPaint::FontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000685
686private:
687 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000688
bungeman@google.comcefd9812013-05-15 15:07:32 +0000689 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
690 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000691
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000692 Offscreen fOffscreen;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000693
bungeman3b4b66c2015-01-08 08:33:44 -0800694 /** Unrotated variant of fCTFont.
695 *
696 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
697 * advances, but always sets the height to 0. This font is used to get the advances of the
698 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000699 *
700 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
bungeman18ec7b92017-02-09 17:15:59 -0500701 * This makes kCTFontOrientationDefault dangerous, because the metrics from
702 * kCTFontOrientationHorizontal are in a different space from kCTFontOrientationVertical.
703 * With kCTFontOrientationVertical the advances must be unrotated.
bungemanef27ce32015-10-29 09:30:32 -0700704 *
705 * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
706 * different underlying font data. As a result, avoid ever creating more than one CTFont per
707 * SkScalerContext to ensure that only one CTFont is used.
708 *
709 * As a result of the above (and other constraints) this font contains the size, but not the
710 * transform. The transform must always be applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000711 */
bungemanc292b5f2016-12-20 12:04:29 -0500712 UniqueCFRef<CTFontRef> fCTFont;
bungemanef27ce32015-10-29 09:30:32 -0700713
714 /** The transform without the font size. */
715 CGAffineTransform fTransform;
716 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000717
bungemanc292b5f2016-12-20 12:04:29 -0500718 UniqueCFRef<CGFontRef> fCGFont;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000719 uint16_t fGlyphCount;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000720 const bool fDoSubPosition;
721 const bool fVertical;
722
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000723 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000724
725 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000726};
727
bungeman7cbeaae2015-09-22 09:54:56 -0700728// CTFontCreateCopyWithAttributes or CTFontCreateCopyWithSymbolicTraits cannot be used on 10.10
bungeman05846312015-09-23 12:51:28 -0700729// and later, as they will return different underlying fonts depending on the size requested.
730// It is not possible to use descriptors with CTFontCreateWithFontDescriptor, since that does not
731// work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
732// CGFont.
bungemanc292b5f2016-12-20 12:04:29 -0500733static UniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
734 const CGAffineTransform* transform)
bungeman7cbeaae2015-09-22 09:54:56 -0700735{
bungemanc292b5f2016-12-20 12:04:29 -0500736 UniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700737
bungeman05846312015-09-23 12:51:28 -0700738 // The last parameter (CTFontDescriptorRef attributes) *must* be nullptr.
739 // If non-nullptr then with fonts with variation axes, the copy will fail in
740 // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
741 // which it quite obviously is not.
bungeman7cbeaae2015-09-22 09:54:56 -0700742
bungeman05846312015-09-23 12:51:28 -0700743 // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
744 // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
745 // the scaler context, since they aren't 'normal'.
bungemanc292b5f2016-12-20 12:04:29 -0500746 return UniqueCFRef<CTFontRef>(
747 CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, transform, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700748}
749
bungeman7cfd46a2016-10-20 16:06:52 -0400750SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
reeda9322c22016-04-12 06:47:05 -0700751 const SkScalerContextEffects& effects,
reed@google.com0da48612013-03-19 16:06:52 +0000752 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400753 : INHERITED(std::move(typeface), effects, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000754 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
755 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
756
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000757{
reedd0f41732015-07-10 12:08:38 -0700758 AUTO_CG_LOCK();
759
bungeman7cfd46a2016-10-20 16:06:52 -0400760 CTFontRef ctFont = static_cast<SkTypeface_Mac*>(this->getTypeface())->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000761 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000762 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
763 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000764
bungeman34902632014-12-10 21:43:27 -0800765 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
766 // As a result, it is necessary to know the actual device size and request that.
767 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800768 SkMatrix skTransform;
bungemane55131c2016-08-24 12:01:31 -0700769 bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
bungeman4ec46aa2017-02-15 17:49:12 -0500770 &scale, &skTransform, nullptr, nullptr, nullptr);
bungemanaae30912015-03-02 13:43:26 -0800771 fTransform = MatrixToCGAffineTransform(skTransform);
bungeman1f0e78d2016-08-23 13:19:01 -0700772 // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
773 // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
bungemane55131c2016-08-24 12:01:31 -0700774 if (invertible) {
bungeman1f0e78d2016-08-23 13:19:01 -0700775 fInvTransform = CGAffineTransformInvert(fTransform);
776 } else {
777 fInvTransform = fTransform;
778 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000779
bungemanbe2284d2014-11-25 08:08:09 -0800780 // The transform contains everything except the requested text size.
781 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -0800782 CGFloat textSize = ScalarToCG(scale.y());
bungemanc292b5f2016-12-20 12:04:29 -0500783 fCTFont = ctfont_create_exact_copy(ctFont, textSize, nullptr);
784 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000785}
786
787CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
788 CGGlyph glyphID, size_t* rowBytesPtr,
mtkleinbbd40182015-09-08 08:19:33 -0700789 bool generateA8FromLCD) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000790 if (!fRGBSpace) {
791 //It doesn't appear to matter what color space is specified.
792 //Regular blends and antialiased text are always (s*a + d*(1-a))
793 //and smoothed text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -0700794 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000795 }
796
797 // default to kBW_Format
798 bool doAA = false;
799 bool doLCD = false;
800
801 if (SkMask::kBW_Format != glyph.fMaskFormat) {
802 doLCD = true;
803 doAA = true;
804 }
805
806 // FIXME: lcd smoothed un-hinted rasterization unsupported.
807 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
808 doLCD = false;
809 doAA = true;
810 }
811
bungeman34902632014-12-10 21:43:27 -0800812 // If this font might have color glyphs, disable LCD as there's no way to support it.
813 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
bungemane280d062016-03-24 11:27:05 -0700814 // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
bungeman34902632014-12-10 21:43:27 -0800815 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
816 doLCD = false;
817 }
818
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000819 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
820 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
821 if (fSize.fWidth < glyph.fWidth) {
822 fSize.fWidth = RoundSize(glyph.fWidth);
823 }
824 if (fSize.fHeight < glyph.fHeight) {
825 fSize.fHeight = RoundSize(glyph.fHeight);
826 }
827
828 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
829 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -0800830 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
831 ? kCGImageAlphaPremultipliedFirst
832 : kCGImageAlphaNoneSkipFirst;
833 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -0700834 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungemanc292b5f2016-12-20 12:04:29 -0500835 rowBytes, fRGBSpace.get(), bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000836
bungeman34902632014-12-10 21:43:27 -0800837 // Skia handles quantization and subpixel positioning,
838 // so disable quantization and enabe subpixel positioning in CG.
bungemanc292b5f2016-12-20 12:04:29 -0500839 CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
840 CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000841
bungeman@google.comcefd9812013-05-15 15:07:32 +0000842 // Because CG always draws from the horizontal baseline,
843 // if there is a non-integral translation from the horizontal origin to the vertical origin,
844 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungemanc292b5f2016-12-20 12:04:29 -0500845 CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
846 CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
bungeman34902632014-12-10 21:43:27 -0800847
bungemanc292b5f2016-12-20 12:04:29 -0500848 CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000849
bungemane280d062016-03-24 11:27:05 -0700850 // Draw black on white to create mask. (Special path exists to speed this up in CG.)
bungemanc292b5f2016-12-20 12:04:29 -0500851 CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000852
853 // force our checks below to happen
854 fDoAA = !doAA;
855 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -0800856
bungemanc292b5f2016-12-20 12:04:29 -0500857 CGContextSetTextMatrix(fCG.get(), context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000858 }
859
860 if (fDoAA != doAA) {
bungemanc292b5f2016-12-20 12:04:29 -0500861 CGContextSetShouldAntialias(fCG.get(), doAA);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000862 fDoAA = doAA;
863 }
864 if (fDoLCD != doLCD) {
bungemanc292b5f2016-12-20 12:04:29 -0500865 CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000866 fDoLCD = doLCD;
867 }
868
869 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
870 // skip rows based on the glyph's height
871 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
872
bungemane280d062016-03-24 11:27:05 -0700873 // Erase to white (or transparent black if it's a color glyph, to not composite against white).
874 uint32_t bgColor = (SkMask::kARGB32_Format != glyph.fMaskFormat) ? 0xFFFFFFFF : 0x00000000;
875 sk_memset_rect32(image, bgColor, glyph.fWidth, glyph.fHeight, rowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000876
877 float subX = 0;
878 float subY = 0;
879 if (context.fDoSubPosition) {
880 subX = SkFixedToFloat(glyph.getSubXFixed());
881 subY = SkFixedToFloat(glyph.getSubYFixed());
882 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000883
bungeman34902632014-12-10 21:43:27 -0800884 // CoreText and CoreGraphics always draw using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000885 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000886 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000887 context.getVerticalOffset(glyphID, &offset);
888 subX += offset.fX;
889 subY += offset.fY;
890 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000891
bungeman34902632014-12-10 21:43:27 -0800892 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -0800893 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
894 // 'positions' which are in text space. The glyph location (in device space) must be
895 // mapped into text space, so that CG can convert it back into device space.
Hal Canary55325b72017-01-03 10:36:17 -0500896 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
bungeman1b5f25c2015-06-08 14:32:24 -0700897 //
bungemanaae30912015-03-02 13:43:26 -0800898 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
899 // So always make the font transform identity and place the transform on the context.
900 point = CGPointApplyAffineTransform(point, context.fInvTransform);
901
bungemanc292b5f2016-12-20 12:04:29 -0500902 CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000903
904 SkASSERT(rowBytesPtr);
905 *rowBytesPtr = rowBytes;
906 return image;
907}
908
bungeman@google.comcefd9812013-05-15 15:07:32 +0000909void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
bungemanc2ba75b2016-12-14 16:53:00 -0500910 // CTFontGetVerticalTranslationsForGlyphs produces cgVertOffset in CG units (pixels, y up).
bungeman@google.comcefd9812013-05-15 15:07:32 +0000911 CGSize cgVertOffset;
bungemanc292b5f2016-12-20 12:04:29 -0500912 CTFontGetVerticalTranslationsForGlyphs(fCTFont.get(), &glyphID, &cgVertOffset, 1);
bungemanef27ce32015-10-29 09:30:32 -0700913 cgVertOffset = CGSizeApplyAffineTransform(cgVertOffset, fTransform);
914 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
915 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
916 skVertOffset.fY = -skVertOffset.fY;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000917 *offset = skVertOffset;
918}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000919
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000920unsigned SkScalerContext_Mac::generateGlyphCount(void) {
921 return fGlyphCount;
922}
923
924uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
reedd0f41732015-07-10 12:08:38 -0700925 AUTO_CG_LOCK();
926
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000927 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000928 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000929
930 // Get the glyph
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000931 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar);
932 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000933
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000934 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
935 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
936 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
bungemanc292b5f2016-12-20 12:04:29 -0500937 CTFontGetGlyphsForCharacters(fCTFont.get(), theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000938 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000939}
940
941void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
942 this->generateMetrics(glyph);
943}
944
bungeman@google.comcefd9812013-05-15 15:07:32 +0000945void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
reedd0f41732015-07-10 12:08:38 -0700946 AUTO_CG_LOCK();
947
djsollen1b277042014-08-06 06:58:06 -0700948 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +0000949 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000950
bungeman@google.comcefd9812013-05-15 15:07:32 +0000951 // The following block produces cgAdvance in CG units (pixels, y up).
952 CGSize cgAdvance;
953 if (fVertical) {
bungeman18ec7b92017-02-09 17:15:59 -0500954 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationVertical,
bungeman@google.comcefd9812013-05-15 15:07:32 +0000955 &cgGlyph, &cgAdvance, 1);
bungeman3b4b66c2015-01-08 08:33:44 -0800956 // Vertical advances are returned as widths instead of heights.
957 SkTSwap(cgAdvance.height, cgAdvance.width);
958 cgAdvance.height = -cgAdvance.height;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000959 } else {
bungeman18ec7b92017-02-09 17:15:59 -0500960 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
bungeman@google.comcefd9812013-05-15 15:07:32 +0000961 &cgGlyph, &cgAdvance, 1);
962 }
bungemanef27ce32015-10-29 09:30:32 -0700963 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700964 glyph->fAdvanceX = CGToFloat(cgAdvance.width);
965 glyph->fAdvanceY = -CGToFloat(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000966
bungeman@google.comcefd9812013-05-15 15:07:32 +0000967 // The following produces skBounds in SkGlyph units (pixels, y down),
968 // or returns early if skBounds would be empty.
969 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000970
bungemanc2ba75b2016-12-14 16:53:00 -0500971 // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
972 // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
973 // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
974 // glyph is vertical. This avoids any diagreement between the various means of retrieving
975 // vertical metrics.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000976 {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000977 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
978 CGRect cgBounds;
bungeman18ec7b92017-02-09 17:15:59 -0500979 CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
bungeman@google.comcefd9812013-05-15 15:07:32 +0000980 &cgGlyph, &cgBounds, 1);
bungemanef27ce32015-10-29 09:30:32 -0700981 cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000982
bungeman@google.comcefd9812013-05-15 15:07:32 +0000983 // BUG?
984 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
985 // it should be empty. So, if we see a zero-advance, we check if it has an
986 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
987 // is rare, so we won't incur a big performance cost for this extra check.
988 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
bungemanc292b5f2016-12-20 12:04:29 -0500989 UniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, nullptr));
990 if (!path || CGPathIsEmpty(path.get())) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000991 return;
992 }
993 }
994
995 if (CGRectIsEmpty_inline(cgBounds)) {
996 return;
997 }
998
999 // Convert cgBounds to SkGlyph units (pixels, y down).
1000 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1001 cgBounds.size.width, cgBounds.size.height);
1002 }
1003
1004 if (fVertical) {
bungemanc2ba75b2016-12-14 16:53:00 -05001005 // Due to possible vertical bounds bugs and simplicity, skBounds is the horizontal bounds.
bungeman@google.comcefd9812013-05-15 15:07:32 +00001006 // Convert these horizontal bounds into vertical bounds.
1007 SkPoint offset;
bungemanc2ba75b2016-12-14 16:53:00 -05001008 this->getVerticalOffset(cgGlyph, &offset);
bungeman@google.comcefd9812013-05-15 15:07:32 +00001009 skBounds.offset(offset);
1010 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001011
bungeman@google.comcefd9812013-05-15 15:07:32 +00001012 // Currently the bounds are based on being rendered at (0,0).
1013 // The top left must not move, since that is the base from which subpixel positioning is offset.
1014 if (fDoSubPosition) {
1015 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1016 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1017 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001018
bungeman@google.comcefd9812013-05-15 15:07:32 +00001019 SkIRect skIBounds;
1020 skBounds.roundOut(&skIBounds);
1021 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1022 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1023 // is not currently known, as CG dilates the outlines by some percentage.
1024 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1025 skIBounds.outset(1, 1);
1026 glyph->fLeft = SkToS16(skIBounds.fLeft);
1027 glyph->fTop = SkToS16(skIBounds.fTop);
1028 glyph->fWidth = SkToU16(skIBounds.width());
1029 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001030}
1031
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001032#include "SkColorPriv.h"
1033
bungemane280d062016-03-24 11:27:05 -07001034static void build_power_table(uint8_t table[]) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001035 for (int i = 0; i < 256; i++) {
1036 float x = i / 255.f;
bungemane280d062016-03-24 11:27:05 -07001037 int xx = SkScalarRoundToInt(x * x * 255);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001038 table[i] = SkToU8(xx);
1039 }
1040}
1041
1042/**
1043 * This will invert the gamma applied by CoreGraphics, so we can get linear
1044 * values.
1045 *
1046 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1047 * The color space used does not appear to affect this choice.
1048 */
1049static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1050 static bool gInited;
1051 static uint8_t gTableCoreGraphicsSmoothing[256];
1052 if (!gInited) {
bungemane280d062016-03-24 11:27:05 -07001053 build_power_table(gTableCoreGraphicsSmoothing);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001054 gInited = true;
1055 }
1056 return gTableCoreGraphicsSmoothing;
1057}
1058
1059static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1060 while (count > 0) {
1061 uint8_t mask = 0;
1062 for (int i = 7; i >= 0; --i) {
bungemane280d062016-03-24 11:27:05 -07001063 mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001064 if (0 == --count) {
1065 break;
1066 }
1067 }
1068 *dst++ = mask;
1069 }
1070}
1071
1072template<bool APPLY_PREBLEND>
1073static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
bungemane280d062016-03-24 11:27:05 -07001074 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
1075 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
1076 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
bungeman3b4b66c2015-01-08 08:33:44 -08001077 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1078#if SK_SHOW_TEXT_BLIT_COVERAGE
1079 lum = SkTMax(lum, (U8CPU)0x30);
1080#endif
1081 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001082}
1083template<bool APPLY_PREBLEND>
1084static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1085 const SkGlyph& glyph, const uint8_t* table8) {
1086 const int width = glyph.fWidth;
1087 size_t dstRB = glyph.rowBytes();
1088 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1089
1090 for (int y = 0; y < glyph.fHeight; y++) {
1091 for (int i = 0; i < width; ++i) {
1092 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1093 }
bungemanc292b5f2016-12-20 12:04:29 -05001094 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1095 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001096 }
1097}
1098
1099template<bool APPLY_PREBLEND>
1100static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1101 const uint8_t* tableG,
1102 const uint8_t* tableB) {
bungemane280d062016-03-24 11:27:05 -07001103 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
1104 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
1105 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001106#if SK_SHOW_TEXT_BLIT_COVERAGE
1107 r = SkTMax(r, (U8CPU)0x30);
1108 g = SkTMax(g, (U8CPU)0x30);
1109 b = SkTMax(b, (U8CPU)0x30);
1110#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001111 return SkPack888ToRGB16(r, g, b);
1112}
1113template<bool APPLY_PREBLEND>
1114static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1115 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1116 const int width = glyph.fWidth;
1117 size_t dstRB = glyph.rowBytes();
1118 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1119
1120 for (int y = 0; y < glyph.fHeight; y++) {
1121 for (int i = 0; i < width; i++) {
1122 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1123 }
bungemanc292b5f2016-12-20 12:04:29 -05001124 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1125 dst = SkTAddOffset<uint16_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001126 }
1127}
1128
bungeman34902632014-12-10 21:43:27 -08001129static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1130 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001131 U8CPU r = (rgb >> 16) & 0xFF;
1132 U8CPU g = (rgb >> 8) & 0xFF;
1133 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001134#if SK_SHOW_TEXT_BLIT_COVERAGE
1135 a = SkTMax(a, (U8CPU)0x30);
1136#endif
bungeman34902632014-12-10 21:43:27 -08001137 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001138}
reed@google.comf77b35d2013-05-02 20:39:44 +00001139
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001140void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
bungemanc292b5f2016-12-20 12:04:29 -05001141 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001142
1143 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1144 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1145
1146 // Draw the glyph
1147 size_t cgRowBytes;
1148 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
halcanary96fcdcc2015-08-27 07:41:13 -07001149 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001150 return;
1151 }
1152
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001153 // Fix the glyph
bungemanc292b5f2016-12-20 12:04:29 -05001154 if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
1155 (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD))
1156 {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001157 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1158
1159 //Note that the following cannot really be integrated into the
1160 //pre-blend, since we may not be applying the pre-blend; when we aren't
1161 //applying the pre-blend it means that a filter wants linear anyway.
1162 //Other code may also be applying the pre-blend, so we'd need another
1163 //one with this and one without.
1164 CGRGBPixel* addr = cgPixels;
1165 for (int y = 0; y < glyph.fHeight; ++y) {
1166 for (int x = 0; x < glyph.fWidth; ++x) {
1167 int r = (addr[x] >> 16) & 0xFF;
1168 int g = (addr[x] >> 8) & 0xFF;
1169 int b = (addr[x] >> 0) & 0xFF;
1170 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1171 }
bungemane280d062016-03-24 11:27:05 -07001172 addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001173 }
1174 }
1175
1176 // Convert glyph to mask
1177 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001178 case SkMask::kLCD16_Format: {
1179 if (fPreBlend.isApplicable()) {
1180 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1181 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1182 } else {
1183 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1184 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1185 }
1186 } break;
1187 case SkMask::kA8_Format: {
1188 if (fPreBlend.isApplicable()) {
1189 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1190 } else {
1191 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1192 }
1193 } break;
1194 case SkMask::kBW_Format: {
1195 const int width = glyph.fWidth;
1196 size_t dstRB = glyph.rowBytes();
1197 uint8_t* dst = (uint8_t*)glyph.fImage;
1198 for (int y = 0; y < glyph.fHeight; y++) {
1199 cgpixels_to_bits(dst, cgPixels, width);
bungemanc292b5f2016-12-20 12:04:29 -05001200 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1201 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001202 }
1203 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001204 case SkMask::kARGB32_Format: {
1205 const int width = glyph.fWidth;
1206 size_t dstRB = glyph.rowBytes();
1207 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1208 for (int y = 0; y < glyph.fHeight; y++) {
1209 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001210 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001211 }
bungemanc292b5f2016-12-20 12:04:29 -05001212 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1213 dst = SkTAddOffset<SkPMColor>(dst, dstRB);
reed@google.comf77b35d2013-05-02 20:39:44 +00001214 }
1215 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001216 default:
1217 SkDEBUGFAIL("unexpected mask format");
1218 break;
1219 }
1220}
1221
1222/*
1223 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1224 * seems sufficient, and possibly even correct, to allow the hinted outline
1225 * to be subpixel positioned.
1226 */
1227#define kScaleForSubPixelPositionHinting (4.0f)
1228
Ben Wagner6e9ac122016-11-11 14:31:06 -05001229void SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001230 AUTO_CG_LOCK();
1231
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001232 SkScalar scaleX = SK_Scalar1;
1233 SkScalar scaleY = SK_Scalar1;
1234
bungemanef27ce32015-10-29 09:30:32 -07001235 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001236 /*
1237 * For subpixel positioning, we want to return an unhinted outline, so it
1238 * can be positioned nicely at fractional offsets. However, we special-case
1239 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1240 * we want to retain hinting in the direction orthogonal to the baseline.
1241 * e.g. for horizontal baseline, we want to retain hinting in Y.
1242 * The way we remove hinting is to scale the font by some value (4) in that
1243 * direction, ask for the path, and then scale the path back down.
1244 */
bungeman7cbeaae2015-09-22 09:54:56 -07001245 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001246 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001247 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001248 // now see if we need to restore hinting for axis-aligned baselines
bungeman27876bc2016-02-29 11:22:55 -08001249 switch (this->computeAxisAlignmentForHText()) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001250 case kX_SkAxisAlignment:
1251 scaleY = SK_Scalar1; // want hinting in the Y direction
1252 break;
1253 case kY_SkAxisAlignment:
1254 scaleX = SK_Scalar1; // want hinting in the X direction
1255 break;
1256 default:
1257 break;
1258 }
1259
bungemanef27ce32015-10-29 09:30:32 -07001260 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1261 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001262 }
1263
Ben Wagner6e9ac122016-11-11 14:31:06 -05001264 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
bungemanc292b5f2016-12-20 12:04:29 -05001265 UniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001266
1267 path->reset();
halcanary96fcdcc2015-08-27 07:41:13 -07001268 if (cgPath != nullptr) {
bungemanc292b5f2016-12-20 12:04:29 -05001269 CGPathApply(cgPath.get(), path, SkScalerContext_Mac::CTPathElement);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001270 }
1271
bungeman@google.comcefd9812013-05-15 15:07:32 +00001272 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001273 SkMatrix m;
1274 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1275 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001276 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001277 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001278 SkPoint offset;
1279 getVerticalOffset(cgGlyph, &offset);
1280 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001281 }
1282}
1283
bungeman41078062014-07-07 08:16:37 -07001284void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001285 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001286 return;
1287 }
1288
reedd0f41732015-07-10 12:08:38 -07001289 AUTO_CG_LOCK();
1290
bungemanc292b5f2016-12-20 12:04:29 -05001291 CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001292
bungeman41078062014-07-07 08:16:37 -07001293 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001294 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont.get()));
1295 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001296 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001297 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001298 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1299 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1300 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001301 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungemanc292b5f2016-12-20 12:04:29 -05001302 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont.get()));
1303 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont.get()));
1304 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont.get()));
1305 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont.get()));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001306
bungeman41078062014-07-07 08:16:37 -07001307 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
1308 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman4ec46aa2017-02-15 17:49:12 -05001309
1310 // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
1311 // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
1312 // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
1313 // table are read, but then overwritten if the font is not a system font. As a result, if there
1314 // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
1315 struct OS2HeightMetrics {
1316 SK_OT_SHORT sxHeight;
1317 SK_OT_SHORT sCapHeight;
1318 } heights;
1319 size_t bytesRead = this->getTypeface()->getTableData(
1320 SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
1321 sizeof(heights), &heights);
1322 if (bytesRead == sizeof(heights)) {
1323 // 'fontSize' is correct because the entire resolved size is set by the constructor.
1324 CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
1325 unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
1326 unsigned maxSaneHeight = upem * 2;
1327 uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
1328 if (xHeight && xHeight < maxSaneHeight) {
1329 metrics->fXHeight = CGToScalar(xHeight * fontSize / upem);
1330 }
1331 uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
1332 if (capHeight && capHeight < maxSaneHeight) {
1333 metrics->fCapHeight = CGToScalar(capHeight * fontSize / upem);
1334 }
1335 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001336}
1337
1338void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1339 SkPath* skPath = (SkPath*)info;
1340
1341 // Process the path element
1342 switch (element->type) {
1343 case kCGPathElementMoveToPoint:
1344 skPath->moveTo(element->points[0].x, -element->points[0].y);
1345 break;
1346
1347 case kCGPathElementAddLineToPoint:
1348 skPath->lineTo(element->points[0].x, -element->points[0].y);
1349 break;
1350
1351 case kCGPathElementAddQuadCurveToPoint:
1352 skPath->quadTo(element->points[0].x, -element->points[0].y,
1353 element->points[1].x, -element->points[1].y);
1354 break;
1355
1356 case kCGPathElementAddCurveToPoint:
1357 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1358 element->points[1].x, -element->points[1].y,
1359 element->points[2].x, -element->points[2].y);
1360 break;
1361
1362 case kCGPathElementCloseSubpath:
1363 skPath->close();
1364 break;
1365
1366 default:
1367 SkDEBUGFAIL("Unknown path element!");
1368 break;
1369 }
1370}
1371
1372
1373///////////////////////////////////////////////////////////////////////////////
1374
halcanary96fcdcc2015-08-27 07:41:13 -07001375// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001376// Call must still manage its ownership of provider
Ben Wagner97043292017-02-23 20:48:43 +00001377static SkTypeface* create_from_dataProvider(UniqueCFRef<CGDataProviderRef> provider) {
bungemanc292b5f2016-12-20 12:04:29 -05001378 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
1379 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07001380 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001381 }
bungemanc292b5f2016-12-20 12:04:29 -05001382 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg.get(), 0, nullptr, nullptr));
1383 if (!ct) {
1384 return nullptr;
1385 }
1386 return create_from_CTFontRef(std::move(ct), nullptr, true);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001387}
1388
bungemanc292b5f2016-12-20 12:04:29 -05001389// Web fonts added to the CTFont registry do not return their character set.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001390// Iterate through the font in this case. The existing caller caches the result,
1391// so the performance impact isn't too bad.
1392static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1393 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001394 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001395 SkUnichar* out = glyphToUnicode->begin();
1396 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1397 UniChar unichar = 0;
1398 while (glyphCount > 0) {
1399 CGGlyph glyph;
1400 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1401 out[glyph] = unichar;
1402 --glyphCount;
1403 }
1404 if (++unichar == 0) {
1405 break;
1406 }
1407 }
1408}
1409
1410// Construct Glyph to Unicode table.
1411// Unicode code points that require conjugate pairs in utf16 are not
1412// supported.
1413static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1414 SkTDArray<SkUnichar>* glyphToUnicode) {
bungemanc292b5f2016-12-20 12:04:29 -05001415 UniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001416 if (!charSet) {
1417 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1418 return;
1419 }
1420
bungemanc292b5f2016-12-20 12:04:29 -05001421 UniqueCFRef<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001422 if (!bitmap) {
1423 return;
1424 }
bungemanc292b5f2016-12-20 12:04:29 -05001425 CFIndex length = CFDataGetLength(bitmap.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001426 if (!length) {
1427 return;
1428 }
1429 if (length > 8192) {
1430 // TODO: Add support for Unicode above 0xFFFF
1431 // Consider only the BMP portion of the Unicode character points.
1432 // The bitmap may contain other planes, up to plane 16.
1433 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1434 length = 8192;
1435 }
bungemanc292b5f2016-12-20 12:04:29 -05001436 const UInt8* bits = CFDataGetBytePtr(bitmap.get());
reed@google.com7fa2a652014-01-27 13:42:58 +00001437 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001438 SkUnichar* out = glyphToUnicode->begin();
1439 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1440 for (int i = 0; i < length; i++) {
1441 int mask = bits[i];
1442 if (!mask) {
1443 continue;
1444 }
1445 for (int j = 0; j < 8; j++) {
1446 CGGlyph glyph;
1447 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1448 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1449 out[glyph] = unichar;
1450 }
1451 }
1452 }
1453}
1454
halcanary96fcdcc2015-08-27 07:41:13 -07001455/** Assumes src and dst are not nullptr. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001456static void CFStringToSkString(CFStringRef src, SkString* dst) {
1457 // Reserve enough room for the worst-case string,
1458 // plus 1 byte for the trailing null.
1459 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1460 kCFStringEncodingUTF8) + 1;
1461 dst->resize(length);
1462 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1463 // Resize to the actual UTF-8 length used, stripping the null character.
1464 dst->resize(strlen(dst->c_str()));
1465}
1466
reed@google.com2689f612013-03-20 20:01:47 +00001467SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -07001468 PerGlyphInfo perGlyphInfo,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001469 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001470 uint32_t glyphIDsCount) const {
1471
reedd0f41732015-07-10 12:08:38 -07001472 AUTO_CG_LOCK();
1473
bungemanc292b5f2016-12-20 12:04:29 -05001474 UniqueCFRef<CTFontRef> ctFont =
1475 ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()), nullptr);
bungeman7cbeaae2015-09-22 09:54:56 -07001476
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001477 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1478
1479 {
bungemanc292b5f2016-12-20 12:04:29 -05001480 UniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
bungeman256b3512014-07-02 07:57:59 -07001481 if (fontName.get()) {
bungemanc292b5f2016-12-20 12:04:29 -05001482 CFStringToSkString(fontName.get(), &info->fFontName);
bungeman256b3512014-07-02 07:57:59 -07001483 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001484 }
1485
bungemanc292b5f2016-12-20 12:04:29 -05001486 CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001487 info->fLastGlyphID = SkToU16(glyphCount - 1);
bungemanc292b5f2016-12-20 12:04:29 -05001488 info->fEmSize = CTFontGetUnitsPerEm(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001489
reed39a9a502015-05-12 09:50:04 -07001490 if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
bungemanc292b5f2016-12-20 12:04:29 -05001491 populate_glyph_to_unicode(ctFont.get(), glyphCount, &info->fGlyphToUnicode);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001492 }
1493
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001494 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1495 // fonts always have both glyf and loca tables. At the least, this is what
1496 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1497 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001498 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001499 return info;
1500 }
1501
1502 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
bungemanc292b5f2016-12-20 12:04:29 -05001503 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001504 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1505 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1506 }
1507 if (symbolicTraits & kCTFontItalicTrait) {
1508 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1509 }
1510 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001511 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1512 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1513 } else if (stylisticClass & kCTFontScriptsClass) {
1514 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1515 }
bungemanc292b5f2016-12-20 12:04:29 -05001516 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
1517 info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
1518 info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
1519 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
1520 CGRect bbox = CTFontGetBoundingBox(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001521
1522 SkRect r;
1523 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1524 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1525 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1526 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1527
1528 r.roundOut(&(info->fBBox));
1529
1530 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1531 // This probably isn't very good with an italic font.
1532 int16_t min_width = SHRT_MAX;
1533 info->fStemV = 0;
1534 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1535 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1536 CGGlyph glyphs[count];
1537 CGRect boundingRects[count];
bungemanc292b5f2016-12-20 12:04:29 -05001538 if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
bungeman18ec7b92017-02-09 17:15:59 -05001539 CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001540 glyphs, boundingRects, count);
1541 for (size_t i = 0; i < count; i++) {
1542 int16_t width = (int16_t) boundingRects[i].size.width;
1543 if (width > 0 && width < min_width) {
1544 min_width = width;
1545 info->fStemV = min_width;
1546 }
1547 }
1548 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001549 return info;
1550}
1551
1552///////////////////////////////////////////////////////////////////////////////
1553
reed@google.comcc9aad52013-03-21 19:28:10 +00001554static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1555 CTFontRef ctFont = typeface->fFontRef.get();
bungemanc292b5f2016-12-20 12:04:29 -05001556 UniqueCFRef<CFNumberRef> fontFormatRef(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001557 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1558 if (!fontFormatRef) {
1559 return 0;
1560 }
1561
1562 SInt32 fontFormatValue;
bungemanc292b5f2016-12-20 12:04:29 -05001563 if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001564 return 0;
1565 }
1566
1567 switch (fontFormatValue) {
1568 case kCTFontFormatOpenTypePostScript:
1569 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1570 case kCTFontFormatOpenTypeTrueType:
1571 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1572 case kCTFontFormatTrueType:
1573 return SkSFNTHeader::fontType_MacTrueType::TAG;
1574 case kCTFontFormatPostScript:
1575 return SkSFNTHeader::fontType_PostScript::TAG;
1576 case kCTFontFormatBitmap:
1577 return SkSFNTHeader::fontType_MacTrueType::TAG;
1578 case kCTFontFormatUnrecognized:
1579 default:
1580 //CT seems to be unreliable in being able to obtain the type,
1581 //even if all we want is the first four bytes of the font resource.
1582 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1583 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1584 }
1585}
1586
bungeman5f213d92015-01-27 05:39:10 -08001587SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
reed@google.comcc9aad52013-03-21 19:28:10 +00001588 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001589 if (0 == fontType) {
halcanary96fcdcc2015-08-27 07:41:13 -07001590 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001591 }
1592
1593 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001594 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001595 SkTDArray<SkFontTableTag> tableTags;
1596 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001597 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001598
1599 // calc total size for font, save sizes
1600 SkTDArray<size_t> tableSizes;
1601 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1602 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001603 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001604 totalSize += (tableSize + 3) & ~3;
1605 *tableSizes.append() = tableSize;
1606 }
1607
1608 // reserve memory for stream, and zero it (tables must be zero padded)
1609 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1610 char* dataStart = (char*)stream->getMemoryBase();
1611 sk_bzero(dataStart, totalSize);
1612 char* dataPtr = dataStart;
1613
1614 // compute font header entries
1615 uint16_t entrySelector = 0;
1616 uint16_t searchRange = 1;
1617 while (searchRange < numTables >> 1) {
1618 entrySelector++;
1619 searchRange <<= 1;
1620 }
1621 searchRange <<= 4;
1622 uint16_t rangeShift = (numTables << 4) - searchRange;
1623
1624 // write font header
1625 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1626 header->fontType = fontType;
1627 header->numTables = SkEndian_SwapBE16(numTables);
1628 header->searchRange = SkEndian_SwapBE16(searchRange);
1629 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1630 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1631 dataPtr += sizeof(SkSFNTHeader);
1632
1633 // write tables
1634 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1635 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1636 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1637 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001638 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001639 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1640 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1641 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001642 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1643 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001644
1645 dataPtr += (tableSize + 3) & ~3;
1646 ++entry;
1647 }
1648
bungemanb3310c22015-03-02 09:05:36 -08001649 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001650 return stream;
1651}
1652
bungeman41868fe2015-05-20 09:21:04 -07001653struct NonDefaultAxesContext {
1654 SkFixed* axisValue;
1655 CFArrayRef cgAxes;
1656};
1657static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1658 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1659
1660 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1661 return;
1662 }
1663
1664 // The key is a CFString which is a string from the 'name' table.
1665 // Search the cgAxes for an axis with this name, and use its index to store the value.
1666 CFIndex keyIndex = -1;
1667 CFStringRef keyString = static_cast<CFStringRef>(key);
1668 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1669 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1670 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1671 continue;
1672 }
1673
1674 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1675 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1676 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1677 continue;
1678 }
1679 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1680 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1681 keyIndex = i;
1682 break;
1683 }
1684 }
1685 if (keyIndex == -1) {
1686 return;
1687 }
1688
1689 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1690 double valueDouble;
1691 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1692 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1693 {
1694 return;
1695 }
1696 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1697}
Ben Wagner97043292017-02-23 20:48:43 +00001698static bool get_variations(CTFontRef fFontRef, CFIndex* cgAxisCount,
bungeman41868fe2015-05-20 09:21:04 -07001699 SkAutoSTMalloc<4, SkFixed>* axisValues)
1700{
Ben Wagner97043292017-02-23 20:48:43 +00001701 // CTFontCopyVariationAxes and CTFontCopyVariation do not work when applied to fonts which
1702 // started life with CGFontCreateWithDataProvider (they simply always return nullptr).
1703 // As a result, we are limited to CGFontCopyVariationAxes and CGFontCopyVariations.
1704 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05001705 if (!cgFont) {
bungeman41868fe2015-05-20 09:21:04 -07001706 return false;
1707 }
1708
bungemanc292b5f2016-12-20 12:04:29 -05001709 UniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
1710 // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
1711 if (!cgVariations) {
1712 return false;
1713 }
1714
1715 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
1716 if (!cgAxes) {
1717 return false;
1718 }
1719 *cgAxisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07001720 axisValues->reset(*cgAxisCount);
1721
1722 // Set all of the axes to their default values.
1723 // Fail if any default value cannot be determined.
1724 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001725 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07001726 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1727 return false;
1728 }
1729
1730 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1731 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1732 kCGFontVariationAxisDefaultValue);
1733 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1734 return false;
1735 }
1736 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
1737 double axisDefaultValueDouble;
1738 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
1739 {
1740 return false;
1741 }
1742 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
1743 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
1744 {
1745 return false;
1746 }
1747 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
1748 }
1749
1750 // Override the default values with the given font's stated axis values.
1751 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
bungemanc292b5f2016-12-20 12:04:29 -05001752 CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
bungeman41868fe2015-05-20 09:21:04 -07001753
1754 return true;
1755}
bungemanf93d7112016-09-16 06:24:20 -07001756std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
bungeman41868fe2015-05-20 09:21:04 -07001757 int index;
bungemanf93d7112016-09-16 06:24:20 -07001758 std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
bungeman41868fe2015-05-20 09:21:04 -07001759
1760 CFIndex cgAxisCount;
1761 SkAutoSTMalloc<4, SkFixed> axisValues;
bungemanc292b5f2016-12-20 12:04:29 -05001762 if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
bungemanf93d7112016-09-16 06:24:20 -07001763 return skstd::make_unique<SkFontData>(std::move(stream), index,
1764 axisValues.get(), cgAxisCount);
bungeman41868fe2015-05-20 09:21:04 -07001765 }
bungemanf93d7112016-09-16 06:24:20 -07001766 return skstd::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
bungeman41868fe2015-05-20 09:21:04 -07001767}
1768
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001769///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001770///////////////////////////////////////////////////////////////////////////////
1771
1772int SkTypeface_Mac::onGetUPEM() const {
bungemanc292b5f2016-12-20 12:04:29 -05001773 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
1774 return CGFontGetUnitsPerEm(cgFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001775}
1776
bungeman@google.com839702b2013-08-07 17:09:22 +00001777SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001778 SkTypeface::LocalizedStrings* nameIter =
bungemanc292b5f2016-12-20 12:04:29 -05001779 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07001780 if (nullptr == nameIter) {
bungemanc292b5f2016-12-20 12:04:29 -05001781 CFStringRef cfLanguageRaw;
1782 UniqueCFRef<CFStringRef> cfFamilyName(
1783 CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
1784 UniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
bungeman@google.coma9802692013-08-07 02:45:25 +00001785
1786 SkString skLanguage;
1787 SkString skFamilyName;
bungemanc292b5f2016-12-20 12:04:29 -05001788 if (cfLanguage) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001789 CFStringToSkString(cfLanguage.get(), &skLanguage);
1790 } else {
1791 skLanguage = "und"; //undetermined
1792 }
bungemanc292b5f2016-12-20 12:04:29 -05001793 if (cfFamilyName) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001794 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1795 }
1796
1797 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1798 }
1799 return nameIter;
1800}
1801
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001802int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
bungemanc292b5f2016-12-20 12:04:29 -05001803 UniqueCFRef<CFArrayRef> cfArray(
1804 CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
1805 if (!cfArray) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001806 return 0;
1807 }
bungemanc292b5f2016-12-20 12:04:29 -05001808 int count = SkToInt(CFArrayGetCount(cfArray.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001809 if (tags) {
1810 for (int i = 0; i < count; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001811 uintptr_t fontTag = reinterpret_cast<uintptr_t>(
1812 CFArrayGetValueAtIndex(cfArray.get(), i));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001813 tags[i] = static_cast<SkFontTableTag>(fontTag);
1814 }
1815 }
1816 return count;
1817}
1818
bungemanc292b5f2016-12-20 12:04:29 -05001819// If, as is the case with web fonts, the CTFont data isn't available,
1820// the CGFont data may work. While the CGFont may always provide the
1821// right result, leave the CTFont code path to minimize disruption.
1822static UniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
1823 UniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1824 kCTFontTableOptionNoOptions));
1825 if (!data) {
1826 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
1827 data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
1828 }
1829 return data;
1830}
1831
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001832size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1833 size_t length, void* dstData) const {
bungemanc292b5f2016-12-20 12:04:29 -05001834 UniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
1835 if (!srcData) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001836 return 0;
1837 }
1838
bungemanc292b5f2016-12-20 12:04:29 -05001839 size_t srcSize = CFDataGetLength(srcData.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001840 if (offset >= srcSize) {
1841 return 0;
1842 }
1843 if (length > srcSize - offset) {
1844 length = srcSize - offset;
1845 }
1846 if (dstData) {
bungemanc292b5f2016-12-20 12:04:29 -05001847 memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001848 }
1849 return length;
1850}
1851
reeda9322c22016-04-12 06:47:05 -07001852SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
1853 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04001854 return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001855}
1856
1857void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001858 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1859 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1860 {
1861 rec->fMaskFormat = SkMask::kA8_Format;
1862 // Render the glyphs as close as possible to what was requested.
1863 // The above turns off subpixel rendering, but the user requested it.
1864 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1865 // See comments below for more details.
1866 rec->setHinting(SkPaint::kNormal_Hinting);
1867 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001868
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001869 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00001870 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001871 SkScalerContext::kLCD_BGROrder_Flag |
1872 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001873
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001874 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001875
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001876 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001877
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001878 // Only two levels of hinting are supported.
1879 // kNo_Hinting means avoid CoreGraphics outline dilation.
1880 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1881 // If there is no lcd support, hinting (dilation) cannot be supported.
1882 SkPaint::Hinting hinting = rec->getHinting();
1883 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1884 hinting = SkPaint::kNo_Hinting;
1885 } else if (SkPaint::kFull_Hinting == hinting) {
1886 hinting = SkPaint::kNormal_Hinting;
1887 }
1888 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001889
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001890 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1891 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1892 // There is no current means to honor a request for unhinted lcd,
1893 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001894
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001895 // Hinting and smoothing should be orthogonal, but currently they are not.
1896 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
1897 // output is drawn from auto-dilated outlines (the amount of which is
1898 // determined by AppleFontSmoothing). Its regular anti-aliased output is
1899 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001900
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001901 // The behavior of Skia is as follows:
1902 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
1903 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
1904 // channel. This matches [LCD][yes-hint] in weight.
1905 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
1906 // Currenly side with LCD, effectively ignoring the hinting setting.
1907 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001908
bungemanc292b5f2016-12-20 12:04:29 -05001909 if (rec->fMaskFormat == SkMask::kLCD16_Format) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001910 if (lcdSupport) {
1911 //CoreGraphics creates 555 masks for smoothed text anyway.
1912 rec->fMaskFormat = SkMask::kLCD16_Format;
1913 rec->setHinting(SkPaint::kNormal_Hinting);
1914 } else {
1915 rec->fMaskFormat = SkMask::kA8_Format;
1916 }
1917 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001918
bungeman34902632014-12-10 21:43:27 -08001919 // CoreText provides no information as to whether a glyph will be color or not.
1920 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
1921 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08001922 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08001923 rec->fMaskFormat = SkMask::kARGB32_Format;
1924 }
1925
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001926 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1927 // All other masks can use regular gamma.
1928 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
1929#ifndef SK_GAMMA_APPLY_TO_A8
brianosmana1e8f8d2016-04-08 06:47:54 -07001930 // SRGBTODO: Is this correct? Do we want contrast boost?
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001931 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00001932#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001933 } else {
1934 //CoreGraphics dialates smoothed text as needed.
1935 rec->setContrast(0);
1936 }
1937}
1938
bungemanc292b5f2016-12-20 12:04:29 -05001939/** Takes ownership of the CFStringRef. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001940static const char* get_str(CFStringRef ref, SkString* str) {
halcanary96fcdcc2015-08-27 07:41:13 -07001941 if (nullptr == ref) {
1942 return nullptr;
bungeman256b3512014-07-02 07:57:59 -07001943 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001944 CFStringToSkString(ref, str);
bungemanc292b5f2016-12-20 12:04:29 -05001945 CFRelease(ref);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001946 return str->c_str();
1947}
1948
bungemanb374d6a2014-09-17 07:48:59 -07001949void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
bungemanc292b5f2016-12-20 12:04:29 -05001950 get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
bungemanb374d6a2014-09-17 07:48:59 -07001951}
1952
reed@google.com5526ede2013-03-25 13:03:37 +00001953void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
1954 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001955 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001956
bungemanc292b5f2016-12-20 12:04:29 -05001957 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
1958 desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
1959 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
bungemanb8113782016-07-25 16:54:59 -07001960 desc->setStyle(this->fontStyle());
caseq26337e92014-06-30 12:14:52 -07001961 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001962}
reed@google.com5526ede2013-03-25 13:03:37 +00001963
reed@google.combcb42ae2013-07-02 13:56:39 +00001964int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001965 uint16_t glyphs[], int glyphCount) const
1966{
1967 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
1968 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1969 // 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 +00001970
reed@google.combcb42ae2013-07-02 13:56:39 +00001971 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001972 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
1973 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00001974 switch (encoding) {
1975 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001976 const char* utf8 = reinterpret_cast<const char*>(chars);
1977 UniChar* utf16 = charStorage.reset(2 * glyphCount);
1978 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00001979 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001980 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
1981 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00001982 }
reed@google.com7fa2a652014-01-27 13:42:58 +00001983 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00001984 break;
1985 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001986 case kUTF16_Encoding: {
1987 src = reinterpret_cast<const UniChar*>(chars);
1988 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00001989 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001990 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
1991 ++extra;
1992 }
reed@google.combcb42ae2013-07-02 13:56:39 +00001993 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001994 srcCount = glyphCount + extra;
1995 break;
1996 }
1997 case kUTF32_Encoding: {
1998 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
1999 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2000 src = utf16;
2001 for (int i = 0; i < glyphCount; ++i) {
2002 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
2003 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002004 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002005 break;
2006 }
2007 }
2008
halcanary96fcdcc2015-08-27 07:41:13 -07002009 // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002010 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002011 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2012 uint16_t* macGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002013 if (nullptr == macGlyphs || srcCount > glyphCount) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002014 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002015 }
2016
bungemanc292b5f2016-12-20 12:04:29 -05002017 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002018
2019 // If there were any non-bmp, then copy and compact.
halcanary96fcdcc2015-08-27 07:41:13 -07002020 // If 'glyphs' is nullptr, then compact glyphStorage in-place.
2021 // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
2022 // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002023 uint16_t* compactedGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002024 if (nullptr == compactedGlyphs) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002025 compactedGlyphs = macGlyphs;
2026 }
2027 if (srcCount > glyphCount) {
2028 int extra = 0;
2029 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002030 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002031 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2032 ++extra;
2033 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002034 }
2035 }
2036
2037 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002038 return glyphCount;
2039 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002040
2041 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002042 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002043 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002044 return i;
2045 }
2046 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002047 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002048 return glyphCount;
2049}
2050
2051int SkTypeface_Mac::onCountGlyphs() const {
bungemanc292b5f2016-12-20 12:04:29 -05002052 return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
reed@google.combcb42ae2013-07-02 13:56:39 +00002053}
2054
reed@google.com95625db2013-03-25 20:44:02 +00002055///////////////////////////////////////////////////////////////////////////////
2056///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002057
2058static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
bungemanc292b5f2016-12-20 12:04:29 -05002059 UniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
2060 if (!ref) {
reed@google.com83787c52013-03-26 17:19:15 +00002061 return false;
2062 }
bungemanc292b5f2016-12-20 12:04:29 -05002063 CFStringToSkString(ref.get(), value);
reed@google.com83787c52013-03-26 17:19:15 +00002064 return true;
2065}
2066
reed@google.com95625db2013-03-25 20:44:02 +00002067#include "SkFontMgr.h"
2068
reed@google.com964988f2013-03-29 14:57:22 +00002069static inline int sqr(int value) {
2070 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2071 return value * value;
2072}
2073
2074// We normalize each axis (weight, width, italic) to be base-900
2075static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2076 return sqr(a.weight() - b.weight()) +
2077 sqr((a.width() - b.width()) * 100) +
bungemanb4bb7d82016-04-27 10:21:04 -07002078 sqr((a.slant() != b.slant()) * 900);
reed@google.com964988f2013-03-29 14:57:22 +00002079}
2080
reed@google.com83787c52013-03-26 17:19:15 +00002081class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002082public:
bungeman53d5c6e2016-04-08 07:22:29 -07002083 SkFontStyleSet_Mac(CTFontDescriptorRef desc)
halcanary96fcdcc2015-08-27 07:41:13 -07002084 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
bungemanc292b5f2016-12-20 12:04:29 -05002085 , fCount(0)
2086 {
2087 if (!fArray) {
2088 fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
reed@google.comdea7ee02013-03-28 14:12:10 +00002089 }
bungemanc292b5f2016-12-20 12:04:29 -05002090 fCount = SkToInt(CFArrayGetCount(fArray.get()));
reed@google.com83787c52013-03-26 17:19:15 +00002091 }
2092
mtklein36352bf2015-03-25 18:17:31 -07002093 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002094 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002095 }
2096
mtklein36352bf2015-03-25 18:17:31 -07002097 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002098 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002099 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002100 if (style) {
bungeman2873c762017-01-13 11:40:21 -05002101 *style = fontstyle_from_descriptor(desc, false);
reed@google.com83787c52013-03-26 17:19:15 +00002102 }
2103 if (name) {
2104 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2105 name->reset();
2106 }
2107 }
2108 }
2109
mtklein36352bf2015-03-25 18:17:31 -07002110 SkTypeface* createTypeface(int index) override {
bungemanc292b5f2016-12-20 12:04:29 -05002111 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
2112 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002113
bungemanbea97482016-08-24 08:29:50 -07002114 return create_from_desc(desc);
reed@google.com83787c52013-03-26 17:19:15 +00002115 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002116
mtklein36352bf2015-03-25 18:17:31 -07002117 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002118 if (0 == fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002119 return nullptr;
reed@google.com964988f2013-03-29 14:57:22 +00002120 }
bungemanbea97482016-08-24 08:29:50 -07002121 return create_from_desc(findMatchingDesc(pattern));
reed@google.com964988f2013-03-29 14:57:22 +00002122 }
2123
reed@google.com83787c52013-03-26 17:19:15 +00002124private:
bungemanc292b5f2016-12-20 12:04:29 -05002125 UniqueCFRef<CFArrayRef> fArray;
2126 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002127
2128 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2129 int bestMetric = SK_MaxS32;
halcanary96fcdcc2015-08-27 07:41:13 -07002130 CTFontDescriptorRef bestDesc = nullptr;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002131
reed@google.com964988f2013-03-29 14:57:22 +00002132 for (int i = 0; i < fCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002133 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
bungeman2873c762017-01-13 11:40:21 -05002134 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc, false));
reed@google.com964988f2013-03-29 14:57:22 +00002135 if (0 == metric) {
2136 return desc;
2137 }
2138 if (metric < bestMetric) {
2139 bestMetric = metric;
2140 bestDesc = desc;
2141 }
2142 }
2143 SkASSERT(bestDesc);
2144 return bestDesc;
2145 }
reed@google.com83787c52013-03-26 17:19:15 +00002146};
2147
2148class SkFontMgr_Mac : public SkFontMgr {
bungemanc292b5f2016-12-20 12:04:29 -05002149 UniqueCFRef<CFArrayRef> fNames;
2150 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002151
bungemanc292b5f2016-12-20 12:04:29 -05002152 CFStringRef getFamilyNameAt(int index) const {
reed@google.com83787c52013-03-26 17:19:15 +00002153 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002154 return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002155 }
2156
reed@google.com964988f2013-03-29 14:57:22 +00002157 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
bungemanc292b5f2016-12-20 12:04:29 -05002158 UniqueCFRef<CFMutableDictionaryRef> cfAttr(
reed@google.com964988f2013-03-29 14:57:22 +00002159 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2160 &kCFTypeDictionaryKeyCallBacks,
2161 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002162
bungemanc292b5f2016-12-20 12:04:29 -05002163 CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002164
bungemanc292b5f2016-12-20 12:04:29 -05002165 UniqueCFRef<CTFontDescriptorRef> desc(
2166 CTFontDescriptorCreateWithAttributes(cfAttr.get()));
2167 return new SkFontStyleSet_Mac(desc.get());
2168 }
2169
2170 /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
2171 * provide a wrapper here that will return an empty array if need be.
2172 */
2173 static UniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
2174#ifdef SK_BUILD_FOR_IOS
2175 return UniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
2176#else
2177 return UniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
2178#endif
reed@google.com964988f2013-03-29 14:57:22 +00002179 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002180
reed@google.com83787c52013-03-26 17:19:15 +00002181public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002182 SkFontMgr_Mac()
bungemanc292b5f2016-12-20 12:04:29 -05002183 : fNames(CopyAvailableFontFamilyNames())
2184 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0) {}
reed@google.com95625db2013-03-25 20:44:02 +00002185
2186protected:
mtklein36352bf2015-03-25 18:17:31 -07002187 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002188 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002189 }
2190
mtklein36352bf2015-03-25 18:17:31 -07002191 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002192 if ((unsigned)index < (unsigned)fCount) {
bungemanc292b5f2016-12-20 12:04:29 -05002193 CFStringToSkString(this->getFamilyNameAt(index), familyName);
reed@google.com83787c52013-03-26 17:19:15 +00002194 } else {
2195 familyName->reset();
2196 }
reed@google.com95625db2013-03-25 20:44:02 +00002197 }
2198
mtklein36352bf2015-03-25 18:17:31 -07002199 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002200 if ((unsigned)index >= (unsigned)fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002201 return nullptr;
reed@google.com83787c52013-03-26 17:19:15 +00002202 }
bungemanc292b5f2016-12-20 12:04:29 -05002203 return CreateSet(this->getFamilyNameAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002204 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002205
mtklein36352bf2015-03-25 18:17:31 -07002206 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002207 UniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
2208 return CreateSet(cfName.get());
reed@google.com964988f2013-03-29 14:57:22 +00002209 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002210
bungemanc292b5f2016-12-20 12:04:29 -05002211 SkTypeface* onMatchFamilyStyle(const char familyName[],
2212 const SkFontStyle& fontStyle) const override {
Hal Canary67b39de2016-11-07 11:47:44 -05002213 sk_sp<SkFontStyleSet> sset(this->matchFamily(familyName));
bungeman5eff9e72016-01-21 08:18:42 -08002214 return sset->matchStyle(fontStyle);
reed@google.com95625db2013-03-25 20:44:02 +00002215 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002216
bungemanc292b5f2016-12-20 12:04:29 -05002217 SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
2218 const SkFontStyle& style,
2219 const char* bcp47[], int bcp47Count,
2220 SkUnichar character) const override {
2221 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
2222 UniqueCFRef<CTFontRef> currentFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -07002223
2224 // kCFStringEncodingUTF32 is BE unless there is a BOM.
2225 // Since there is no machine endian option, explicitly state machine endian.
2226#ifdef SK_CPU_LENDIAN
2227 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
2228#else
2229 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
2230#endif
bungemanc292b5f2016-12-20 12:04:29 -05002231 UniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
bungemanbea97482016-08-24 08:29:50 -07002232 kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
2233 encoding, false));
bungemanc292b5f2016-12-20 12:04:29 -05002234 CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units.
2235 UniqueCFRef<CTFontRef> fallbackFont(
2236 CTFontCreateForString(currentFont.get(), string.get(), range));
2237 return create_from_CTFontRef(std::move(fallbackFont), nullptr, false);
djsollen33068c12014-11-14 10:52:53 -08002238 }
2239
bungemanc292b5f2016-12-20 12:04:29 -05002240 SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2241 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002242 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002243 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002244
mtklein36352bf2015-03-25 18:17:31 -07002245 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002246 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromData(sk_ref_sp(data)));
2247 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002248 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002249 }
Ben Wagner97043292017-02-23 20:48:43 +00002250 return create_from_dataProvider(std::move(pr));
reed@google.com95625db2013-03-25 20:44:02 +00002251 }
2252
bungemanf93d7112016-09-16 06:24:20 -07002253 SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
2254 std::unique_ptr<SkStreamAsset> stream(bareStream);
bungemanc292b5f2016-12-20 12:04:29 -05002255 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromStream(std::move(stream)));
2256 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002257 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002258 }
Ben Wagner97043292017-02-23 20:48:43 +00002259 return create_from_dataProvider(std::move(pr));
reed@google.com95625db2013-03-25 20:44:02 +00002260 }
2261
Ben Wagner97043292017-02-23 20:48:43 +00002262 static CFNumberRef get_tag_for_name(CFStringRef name, CFArrayRef ctAxes) {
2263 CFIndex ctAxisCount = CFArrayGetCount(ctAxes);
2264 for (int i = 0; i < ctAxisCount; ++i) {
2265 CFTypeRef ctAxisInfo = CFArrayGetValueAtIndex(ctAxes, i);
2266 if (CFDictionaryGetTypeID() != CFGetTypeID(ctAxisInfo)) {
2267 return nullptr;
2268 }
2269 CFDictionaryRef ctAxisInfoDict = static_cast<CFDictionaryRef>(ctAxisInfo);
2270
2271 CFTypeRef ctAxisName = CFDictionaryGetValue(ctAxisInfoDict,
2272 kCTFontVariationAxisNameKey);
2273 if (!ctAxisName || CFGetTypeID(ctAxisName) != CFStringGetTypeID()) {
2274 return nullptr;
2275 }
2276
2277 if (CFEqual(name, ctAxisName)) {
2278 CFTypeRef tag = CFDictionaryGetValue(ctAxisInfoDict,
2279 kCTFontVariationAxisIdentifierKey);
2280 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2281 return nullptr;
2282 }
2283 return static_cast<CFNumberRef>(tag);
2284 }
2285 }
2286 return nullptr;
2287 }
2288 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, const FontParameters& params) {
2289 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
2290 if (!cgAxes) {
2291 return nullptr;
2292 }
2293 CFIndex axisCount = CFArrayGetCount(cgAxes.get());
2294
2295 // The CGFont variation data is keyed by name, and lacks the tag.
bungemanf6c71072016-01-21 14:17:47 -08002296 // The CTFont variation data is keyed by tag, and also has the name.
Ben Wagner97043292017-02-23 20:48:43 +00002297 // We would like to work with CTFont variaitons, but creating a CTFont font with
bungemanf6c71072016-01-21 14:17:47 -08002298 // CTFont variation dictionary runs into bugs. So use the CTFont variation data
2299 // to match names to tags to create the appropriate CGFont.
bungemanc292b5f2016-12-20 12:04:29 -05002300 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
2301 UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct.get()));
Ben Wagner97043292017-02-23 20:48:43 +00002302 if (!ctAxes || CFArrayGetCount(ctAxes.get()) != axisCount) {
bungemanf6c71072016-01-21 14:17:47 -08002303 return nullptr;
2304 }
2305
Ben Wagner97043292017-02-23 20:48:43 +00002306 int paramAxisCount;
2307 const FontParameters::Axis* paramAxes = params.getAxes(&paramAxisCount);
bungemanf6c71072016-01-21 14:17:47 -08002308
bungemanc292b5f2016-12-20 12:04:29 -05002309 UniqueCFRef<CFMutableDictionaryRef> dict(
2310 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2311 &kCFTypeDictionaryKeyCallBacks,
2312 &kCFTypeDictionaryValueCallBacks));
2313
bungemanf6c71072016-01-21 14:17:47 -08002314 for (int i = 0; i < axisCount; ++i) {
Ben Wagner97043292017-02-23 20:48:43 +00002315 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungemanf6c71072016-01-21 14:17:47 -08002316 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2317 return nullptr;
2318 }
2319 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2320
Ben Wagner97043292017-02-23 20:48:43 +00002321 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
bungemanf6c71072016-01-21 14:17:47 -08002322 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2323 return nullptr;
2324 }
2325
Ben Wagner97043292017-02-23 20:48:43 +00002326 CFNumberRef tagNumber =
2327 get_tag_for_name(static_cast<CFStringRef>(axisName), ctAxes.get());
2328 if (!tagNumber) {
2329 // Could not find a tag to go with the name of this index.
2330 // This would be a bug in CG/CT.
2331 continue;
bungemanf6c71072016-01-21 14:17:47 -08002332 }
2333 int64_t tagLong;
2334 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2335 return nullptr;
2336 }
2337
2338 // The variation axes can be set to any value, but cg will effectively pin them.
2339 // Pin them here to normalize.
Ben Wagner97043292017-02-23 20:48:43 +00002340 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2341 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2342 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisDefaultValue);
bungemanf6c71072016-01-21 14:17:47 -08002343 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2344 !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
2345 !def || CFGetTypeID(def) != CFNumberGetTypeID())
2346 {
2347 return nullptr;
2348 }
2349 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2350 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2351 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2352 double minDouble;
2353 double maxDouble;
2354 double defDouble;
2355 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2356 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
2357 !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
2358 {
2359 return nullptr;
2360 }
2361
2362 double value = defDouble;
Ben Wagner97043292017-02-23 20:48:43 +00002363 for (int j = 0; j < paramAxisCount; ++j) {
2364 if (paramAxes[j].fTag == tagLong) {
2365 value = SkTPin(SkScalarToDouble(paramAxes[j].fStyleValue),minDouble,maxDouble);
bungemanf6c71072016-01-21 14:17:47 -08002366 break;
2367 }
2368 }
bungemanc292b5f2016-12-20 12:04:29 -05002369 UniqueCFRef<CFNumberRef> valueNumber(
2370 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2371 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungemanf6c71072016-01-21 14:17:47 -08002372 }
bungemanc292b5f2016-12-20 12:04:29 -05002373 return std::move(dict);
bungemanf6c71072016-01-21 14:17:47 -08002374 }
Ben Wagner97043292017-02-23 20:48:43 +00002375 SkTypeface* onCreateFromStream(SkStreamAsset* bs, const FontParameters& params) const override {
bungemanf93d7112016-09-16 06:24:20 -07002376 std::unique_ptr<SkStreamAsset> s(bs);
bungemanc292b5f2016-12-20 12:04:29 -05002377 UniqueCFRef<CGDataProviderRef> provider(SkCreateDataProviderFromStream(std::move(s)));
2378 if (!provider) {
bungemanf6c71072016-01-21 14:17:47 -08002379 return nullptr;
2380 }
bungemanc292b5f2016-12-20 12:04:29 -05002381 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2382 if (!cg) {
bungemanf6c71072016-01-21 14:17:47 -08002383 return nullptr;
2384 }
2385
Ben Wagner97043292017-02-23 20:48:43 +00002386 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), params);
bungemanf6c71072016-01-21 14:17:47 -08002387 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2388 // created from a data provider does not appear to have any ownership of the underlying
2389 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002390 UniqueCFRef<CGFontRef> cgVariant;
bungemanf6c71072016-01-21 14:17:47 -08002391 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002392 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungemanf6c71072016-01-21 14:17:47 -08002393 } else {
mtklein18300a32016-03-16 13:53:35 -07002394 cgVariant.reset(cg.release());
bungemanf6c71072016-01-21 14:17:47 -08002395 }
2396
bungemanc292b5f2016-12-20 12:04:29 -05002397 UniqueCFRef<CTFontRef> ct(
2398 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungemanf6c71072016-01-21 14:17:47 -08002399 if (!ct) {
2400 return nullptr;
2401 }
bungemanc292b5f2016-12-20 12:04:29 -05002402 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungemanf6c71072016-01-21 14:17:47 -08002403 }
2404
bungemanc292b5f2016-12-20 12:04:29 -05002405 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, SkFontData* fontData) {
2406 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
bungeman41868fe2015-05-20 09:21:04 -07002407 if (!cgAxes) {
halcanary96fcdcc2015-08-27 07:41:13 -07002408 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002409 }
2410
bungemanc292b5f2016-12-20 12:04:29 -05002411 CFIndex axisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07002412 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002413 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002414 }
2415
bungemanc292b5f2016-12-20 12:04:29 -05002416 UniqueCFRef<CFMutableDictionaryRef> dict(
2417 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2418 &kCFTypeDictionaryKeyCallBacks,
2419 &kCFTypeDictionaryValueCallBacks));
2420
bungeman41868fe2015-05-20 09:21:04 -07002421 for (int i = 0; i < fontData->getAxisCount(); ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002422 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07002423 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002424 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002425 }
2426 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2427
2428 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2429 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002430 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002431 }
2432
2433 // The variation axes can be set to any value, but cg will effectively pin them.
2434 // Pin them here to normalize.
2435 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2436 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2437 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2438 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2439 {
halcanary96fcdcc2015-08-27 07:41:13 -07002440 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002441 }
2442 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2443 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2444 double minDouble;
2445 double maxDouble;
2446 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2447 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2448 {
halcanary96fcdcc2015-08-27 07:41:13 -07002449 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002450 }
2451 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
bungemanc292b5f2016-12-20 12:04:29 -05002452 UniqueCFRef<CFNumberRef> valueNumber(
2453 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2454 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungeman41868fe2015-05-20 09:21:04 -07002455 }
bungemanc292b5f2016-12-20 12:04:29 -05002456 return std::move(dict);
bungeman41868fe2015-05-20 09:21:04 -07002457 }
bungemanf93d7112016-09-16 06:24:20 -07002458 SkTypeface* onCreateFromFontData(std::unique_ptr<SkFontData> fontData) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002459 UniqueCFRef<CGDataProviderRef> provider(
bungemanf93d7112016-09-16 06:24:20 -07002460 SkCreateDataProviderFromStream(fontData->detachStream()));
bungemanc292b5f2016-12-20 12:04:29 -05002461 if (!provider) {
halcanary96fcdcc2015-08-27 07:41:13 -07002462 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002463 }
bungemanc292b5f2016-12-20 12:04:29 -05002464 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2465 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07002466 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002467 }
2468
bungemanc292b5f2016-12-20 12:04:29 -05002469 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), fontData.get());
bungeman41868fe2015-05-20 09:21:04 -07002470 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2471 // created from a data provider does not appear to have any ownership of the underlying
2472 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002473 UniqueCFRef<CGFontRef> cgVariant;
bungeman41868fe2015-05-20 09:21:04 -07002474 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002475 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungeman41868fe2015-05-20 09:21:04 -07002476 } else {
mtklein18300a32016-03-16 13:53:35 -07002477 cgVariant.reset(cg.release());
bungeman41868fe2015-05-20 09:21:04 -07002478 }
2479
bungemanc292b5f2016-12-20 12:04:29 -05002480 UniqueCFRef<CTFontRef> ct(
2481 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07002482 if (!ct) {
halcanary96fcdcc2015-08-27 07:41:13 -07002483 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002484 }
bungemanc292b5f2016-12-20 12:04:29 -05002485 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungeman41868fe2015-05-20 09:21:04 -07002486 }
2487
mtklein36352bf2015-03-25 18:17:31 -07002488 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002489 UniqueCFRef<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2490 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002491 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002492 }
Ben Wagner97043292017-02-23 20:48:43 +00002493 return create_from_dataProvider(std::move(pr));
reed@google.com95625db2013-03-25 20:44:02 +00002494 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002495
bungeman11a77c62016-04-12 13:45:06 -07002496 SkTypeface* onLegacyCreateTypeface(const char familyName[], SkFontStyle style) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002497 if (familyName) {
2498 familyName = map_css_names(familyName);
2499 }
2500
bungemanbea97482016-08-24 08:29:50 -07002501 SkTypeface* face = create_from_name(familyName, style);
bungeman53d5c6e2016-04-08 07:22:29 -07002502 if (face) {
2503 return face;
bungemana4c4a2d2014-10-20 13:33:19 -07002504 }
bungeman53d5c6e2016-04-08 07:22:29 -07002505
bungemanc292b5f2016-12-20 12:04:29 -05002506 static SkTypeface* gDefaultFace;
2507 static SkOnce lookupDefault;
bungeman83f1f442017-02-06 13:21:33 -05002508 static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
bungemanc292b5f2016-12-20 12:04:29 -05002509 lookupDefault([]{
2510 gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle());
2511 });
2512 return SkSafeRef(gDefaultFace);
reed@google.com7fdcd442013-07-30 21:25:49 +00002513 }
reed@google.com95625db2013-03-25 20:44:02 +00002514};
2515
reed@google.com7fdcd442013-07-30 21:25:49 +00002516///////////////////////////////////////////////////////////////////////////////
2517
Ben Wagner3546ff12017-01-03 13:32:36 -05002518sk_sp<SkFontMgr> SkFontMgr::Factory() { return sk_make_sp<SkFontMgr_Mac>(); }
mtklein1ee76512015-11-02 10:20:27 -08002519
2520#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)