blob: 92334cb6bc08f2abae2b69fd2c2af392c9f65cab [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
bungeman3e306f62017-03-29 11:32:02 -040049#include <dlfcn.h>
50
reedd0f41732015-07-10 12:08:38 -070051// Experimental code to use a global lock whenever we access CG, to see if this reduces
52// crashes in Chrome
53#define USE_GLOBAL_MUTEX_FOR_CG_ACCESS
54
55#ifdef USE_GLOBAL_MUTEX_FOR_CG_ACCESS
reed086eea92016-05-04 17:12:46 -070056 SK_DECLARE_STATIC_MUTEX(gCGMutex);
reedd0f41732015-07-10 12:08:38 -070057 #define AUTO_CG_LOCK() SkAutoMutexAcquire amc(gCGMutex)
58#else
59 #define AUTO_CG_LOCK()
60#endif
61
bungeman3b4b66c2015-01-08 08:33:44 -080062// Set to make glyph bounding boxes visible.
63#define SK_SHOW_TEXT_BLIT_COVERAGE 0
64
Mike Reed342a9fa2017-05-03 15:48:22 -040065CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
66 return face ? (CTFontRef)face->internal_private_getCTFontRef() : nullptr;
67}
68
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000069class SkScalerContext_Mac;
70
bungemanc292b5f2016-12-20 12:04:29 -050071struct CFSafeRelease {
72 void operator()(CFTypeRef cfTypeRef) {
73 if (cfTypeRef) {
74 CFRelease(cfTypeRef);
75 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000076 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000077};
bungemanc292b5f2016-12-20 12:04:29 -050078template <typename CFRef> using UniqueCFRef =
79 std::unique_ptr<skstd::remove_pointer_t<CFRef>, CFSafeRelease>;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000080
bungemanc292b5f2016-12-20 12:04:29 -050081static UniqueCFRef<CFStringRef> make_CFString(const char str[]) {
82 return UniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, str, kCFStringEncodingUTF8));
reed@google.com964988f2013-03-29 14:57:22 +000083}
84
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000085// inline versions of these rect helpers
86
87static bool CGRectIsEmpty_inline(const CGRect& rect) {
88 return rect.size.width <= 0 || rect.size.height <= 0;
89}
90
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000091static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
92 return rect.origin.x;
93}
94
95static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
96 return rect.origin.x + rect.size.width;
97}
98
99static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
100 return rect.origin.y;
101}
102
103static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
104 return rect.origin.y + rect.size.height;
105}
106
107static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
108 return rect.size.width;
109}
110
111///////////////////////////////////////////////////////////////////////////////
112
113static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
reed@google.com7fa2a652014-01-27 13:42:58 +0000114 int width, int height, size_t rowBytes) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000115 SkASSERT(width);
116 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
117
118 if (width >= 32) {
119 while (height) {
120 sk_memset32(ptr, value, width);
121 ptr = (uint32_t*)((char*)ptr + rowBytes);
122 height -= 1;
123 }
124 return;
125 }
126
127 rowBytes -= width * sizeof(uint32_t);
128
129 if (width >= 8) {
130 while (height) {
131 int w = width;
132 do {
133 *ptr++ = value; *ptr++ = value;
134 *ptr++ = value; *ptr++ = value;
135 *ptr++ = value; *ptr++ = value;
136 *ptr++ = value; *ptr++ = value;
137 w -= 8;
138 } while (w >= 8);
139 while (--w >= 0) {
140 *ptr++ = value;
141 }
142 ptr = (uint32_t*)((char*)ptr + rowBytes);
143 height -= 1;
144 }
145 } else {
146 while (height) {
147 int w = width;
148 do {
149 *ptr++ = value;
150 } while (--w > 0);
151 ptr = (uint32_t*)((char*)ptr + rowBytes);
152 height -= 1;
153 }
154 }
155}
156
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000157typedef uint32_t CGRGBPixel;
158
159static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
160 return pixel & 0xFF;
161}
162
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000163static CGFloat ScalarToCG(SkScalar scalar) {
164 if (sizeof(CGFloat) == sizeof(float)) {
165 return SkScalarToFloat(scalar);
166 } else {
167 SkASSERT(sizeof(CGFloat) == sizeof(double));
168 return (CGFloat) SkScalarToDouble(scalar);
169 }
170}
171
172static SkScalar CGToScalar(CGFloat cgFloat) {
173 if (sizeof(CGFloat) == sizeof(float)) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700174 return SkFloatToScalar(cgFloat);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000175 } else {
176 SkASSERT(sizeof(CGFloat) == sizeof(double));
177 return SkDoubleToScalar(cgFloat);
178 }
179}
180
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700181static float CGToFloat(CGFloat cgFloat) {
182 if (sizeof(CGFloat) == sizeof(float)) {
183 return cgFloat;
184 } else {
185 SkASSERT(sizeof(CGFloat) == sizeof(double));
186 return static_cast<float>(cgFloat);
187 }
188}
189
bungemanc292b5f2016-12-20 12:04:29 -0500190static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) {
191 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX]),
192 -ScalarToCG(matrix[SkMatrix::kMSkewY] ),
193 -ScalarToCG(matrix[SkMatrix::kMSkewX] ),
194 ScalarToCG(matrix[SkMatrix::kMScaleY]),
195 ScalarToCG(matrix[SkMatrix::kMTransX]),
196 ScalarToCG(matrix[SkMatrix::kMTransY]));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000197}
198
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000199///////////////////////////////////////////////////////////////////////////////
200
201#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000202
203/**
204 * There does not appear to be a publicly accessable API for determining if lcd
205 * font smoothing will be applied if we request it. The main issue is that if
206 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
207 */
208static bool supports_LCD() {
209 static int gSupportsLCD = -1;
210 if (gSupportsLCD >= 0) {
211 return (bool) gSupportsLCD;
212 }
213 uint32_t rgb = 0;
bungemanc292b5f2016-12-20 12:04:29 -0500214 UniqueCFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
215 UniqueCFRef<CGContextRef> cgContext(
216 CGBitmapContextCreate(&rgb, 1, 1, 8, 4, colorspace.get(), BITMAP_INFO_RGB));
217 UniqueCFRef<CTFontRef> ctFont(CTFontCreateWithName(CFSTR("Helvetica"), 16, nullptr));
218 CGContextSetShouldSmoothFonts(cgContext.get(), true);
219 CGContextSetShouldAntialias(cgContext.get(), true);
220 CGContextSetTextDrawingMode(cgContext.get(), kCGTextFill);
221 CGContextSetGrayFillColor(cgContext.get(), 1, 1);
ccameronf8ee5b42016-10-04 15:02:02 -0700222 CGPoint point = CGPointMake(-1, 0);
223 static const UniChar pipeChar = '|';
224 CGGlyph pipeGlyph;
bungemanc292b5f2016-12-20 12:04:29 -0500225 CTFontGetGlyphsForCharacters(ctFont.get(), &pipeChar, &pipeGlyph, 1);
226 CTFontDrawGlyphs(ctFont.get(), &pipeGlyph, &point, 1, cgContext.get());
ccameronf8ee5b42016-10-04 15:02:02 -0700227
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000228 uint32_t r = (rgb >> 16) & 0xFF;
229 uint32_t g = (rgb >> 8) & 0xFF;
230 uint32_t b = (rgb >> 0) & 0xFF;
231 gSupportsLCD = (r != g || r != b);
232 return (bool) gSupportsLCD;
233}
234
235class Offscreen {
236public:
bungeman34902632014-12-10 21:43:27 -0800237 Offscreen()
halcanary96fcdcc2015-08-27 07:41:13 -0700238 : fRGBSpace(nullptr)
239 , fCG(nullptr)
bungeman34902632014-12-10 21:43:27 -0800240 , fDoAA(false)
241 , fDoLCD(false)
242 {
243 fSize.set(0, 0);
244 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000245
246 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
bungemanc292b5f2016-12-20 12:04:29 -0500247 CGGlyph glyphID, size_t* rowBytesPtr, bool generateA8FromLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000248
249private:
250 enum {
251 kSize = 32 * 32 * sizeof(CGRGBPixel)
252 };
253 SkAutoSMalloc<kSize> fImageStorage;
bungemanc292b5f2016-12-20 12:04:29 -0500254 UniqueCFRef<CGColorSpaceRef> fRGBSpace;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000255
256 // cached state
bungemanc292b5f2016-12-20 12:04:29 -0500257 UniqueCFRef<CGContextRef> fCG;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000258 SkISize fSize;
259 bool fDoAA;
260 bool fDoLCD;
261
262 static int RoundSize(int dimension) {
263 return SkNextPow2(dimension);
264 }
265};
266
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000267///////////////////////////////////////////////////////////////////////////////
268
bungeman6e45bda2016-07-25 15:11:49 -0700269static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
bungemana4c4a2d2014-10-20 13:33:19 -0700270 CFNumberRef num;
271 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
bungemanc292b5f2016-12-20 12:04:29 -0500272 && CFNumberIsFloatType(num)
273 && CFNumberGetValue(num, kCFNumberCGFloatType, value);
bungemana4c4a2d2014-10-20 13:33:19 -0700274}
275
bungeman6e45bda2016-07-25 15:11:49 -0700276template <typename S, typename D, typename C> struct LinearInterpolater {
277 struct Mapping {
278 S src_val;
279 D dst_val;
280 };
281 constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
282 : fMapping(mapping), fMappingCount(mappingCount) {}
283
284 static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
285 SkASSERT(src_min < src_max);
286 SkASSERT(dst_min <= dst_max);
287 return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000288 }
bungeman6e45bda2016-07-25 15:11:49 -0700289
bungemanf1ef1fa2017-02-09 14:23:46 -0500290 D map(S val) const {
bungeman6e45bda2016-07-25 15:11:49 -0700291 // -Inf to [0]
292 if (val < fMapping[0].src_val) {
293 return fMapping[0].dst_val;
294 }
295
296 // Linear from [i] to [i+1]
297 for (int i = 0; i < fMappingCount - 1; ++i) {
298 if (val < fMapping[i+1].src_val) {
299 return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
300 fMapping[i].dst_val, fMapping[i+1].dst_val);
301 }
302 }
303
304 // From [n] to +Inf
305 // if (fcweight < Inf)
306 return fMapping[fMappingCount - 1].dst_val;
307 }
308
309 Mapping const * fMapping;
310 int fMappingCount;
311};
312
313struct RoundCGFloatToInt {
314 int operator()(CGFloat s) { return s + 0.5; }
315};
bungemanf1ef1fa2017-02-09 14:23:46 -0500316struct CGFloatIdentity {
317 CGFloat operator()(CGFloat s) { return s; }
318};
319
bungeman3e306f62017-03-29 11:32:02 -0400320/** Returns the [-1, 1] CTFontDescriptor weights for the
321 * <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
322 *
323 * It is assumed that the values will be interpolated linearly between these points.
324 * NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
325 * The actual values appear to be stable, but they may change in the future without notice.
326 */
327static CGFloat(&get_NSFontWeight_mapping())[11] {
328
329 // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
330#ifdef SK_BUILD_FOR_MAC
331# define SK_KIT_FONT_WEIGHT_PREFIX "NS"
332#endif
333#ifdef SK_BUILD_FOR_IOS
334# define SK_KIT_FONT_WEIGHT_PREFIX "UI"
335#endif
336 static constexpr struct {
337 CGFloat defaultValue;
338 const char* name;
339 } nsFontWeightLoaderInfos[] = {
340 { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
341 { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
342 { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
343 { 0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
344 { 0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
345 { 0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
346 { 0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
347 { 0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
348 { 0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
349 };
350
351 static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
352 static CGFloat nsFontWeights[11];
353 static SkOnce once;
354 once([&] {
355 size_t i = 0;
356 nsFontWeights[i++] = -1.00;
357 for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
358 void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
359 if (nsFontWeightValuePtr) {
360 nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
361 } else {
362 nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
363 }
364 }
365 nsFontWeights[i++] = 1.00;
366 });
367 return nsFontWeights;
368}
369
bungemanf1ef1fa2017-02-09 14:23:46 -0500370/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
371 *
372 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
373 * CTFont is native or created from a CGDataProvider.
374 */
375static CGFloat fontstyle_to_ct_weight(int fontstyleWeight) {
376 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
377
378 // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
379 // However, on this end we can't tell, so this is ignored.
380
bungeman3e306f62017-03-29 11:32:02 -0400381 static Interpolator::Mapping nativeWeightMappings[11];
382 static SkOnce once;
383 once([&] {
384 CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
385 for (int i = 0; i < 11; ++i) {
386 nativeWeightMappings[i].src_val = i * 100;
387 nativeWeightMappings[i].dst_val = nsFontWeights[i];
388 }
389 });
bungemanf1ef1fa2017-02-09 14:23:46 -0500390 static constexpr Interpolator nativeInterpolator(
391 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
392
393 return nativeInterpolator.map(fontstyleWeight);
394}
395
bungeman6e45bda2016-07-25 15:11:49 -0700396
bungeman2873c762017-01-13 11:40:21 -0500397/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
398 *
399 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
400 * CTFont is native or created from a CGDataProvider.
401 */
402static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
bungeman6e45bda2016-07-25 15:11:49 -0700403 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
404
bungeman6e45bda2016-07-25 15:11:49 -0700405 // 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 -0500406 // However, on this end we can't tell, so this is ignored.
407
408 /** This mapping for CGDataProvider created fonts is determined by creating font data with every
409 * weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
410 * in tests/TypefaceTest.cpp for the code used to determine these values.
411 */
412 static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
bungeman6e45bda2016-07-25 15:11:49 -0700413 { -1.00, 0 },
414 { -0.70, 100 },
415 { -0.50, 200 },
416 { -0.23, 300 },
417 { 0.00, 400 },
418 { 0.20, 500 },
419 { 0.30, 600 },
420 { 0.40, 700 },
421 { 0.60, 800 },
422 { 0.80, 900 },
423 { 1.00, 1000 },
424 };
bungeman2873c762017-01-13 11:40:21 -0500425 static constexpr Interpolator dataProviderInterpolator(
426 dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
427
bungeman3e306f62017-03-29 11:32:02 -0400428 static Interpolator::Mapping nativeWeightMappings[11];
429 static SkOnce once;
430 once([&] {
431 CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
432 for (int i = 0; i < 11; ++i) {
433 nativeWeightMappings[i].src_val = nsFontWeights[i];
434 nativeWeightMappings[i].dst_val = i * 100;
435 }
436 });
bungeman2873c762017-01-13 11:40:21 -0500437 static constexpr Interpolator nativeInterpolator(
438 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
439
440 return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
441 : nativeInterpolator.map(cgWeight);
bungemana4c4a2d2014-10-20 13:33:19 -0700442}
443
bungemanf1ef1fa2017-02-09 14:23:46 -0500444/** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
445static int fontstyle_to_ct_width(int fontstyleWidth) {
446 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
447
448 // Values determined by creating font data with every width, creating a CTFont,
449 // and asking the CTFont for its width. See TypefaceStyle test for basics.
450 static constexpr Interpolator::Mapping widthMappings[] = {
451 { 0, -0.5 },
452 { 10, 0.5 },
453 };
454 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
455 return interpolator.map(fontstyleWidth);
456}
457
458/** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
bungeman6e45bda2016-07-25 15:11:49 -0700459static int ct_width_to_fontstyle(CGFloat cgWidth) {
460 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
461
462 // Values determined by creating font data with every width, creating a CTFont,
463 // and asking the CTFont for its width. See TypefaceStyle test for basics.
464 static constexpr Interpolator::Mapping widthMappings[] = {
465 { -0.5, 0 },
466 { 0.5, 10 },
467 };
bungeman2873c762017-01-13 11:40:21 -0500468 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
469 return interpolator.map(cgWidth);
bungemana4c4a2d2014-10-20 13:33:19 -0700470}
471
bungeman2873c762017-01-13 11:40:21 -0500472static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc, bool fromDataProvider) {
bungemanc292b5f2016-12-20 12:04:29 -0500473 UniqueCFRef<CFTypeRef> fontTraits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
474 if (!fontTraits || CFDictionaryGetTypeID() != CFGetTypeID(fontTraits.get())) {
bungemana4c4a2d2014-10-20 13:33:19 -0700475 return SkFontStyle();
476 }
bungemanc292b5f2016-12-20 12:04:29 -0500477 UniqueCFRef<CFDictionaryRef> fontTraitsDict(static_cast<CFDictionaryRef>(fontTraits.release()));
bungemana4c4a2d2014-10-20 13:33:19 -0700478
bungeman6e45bda2016-07-25 15:11:49 -0700479 CGFloat weight, width, slant;
bungemanc292b5f2016-12-20 12:04:29 -0500480 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800481 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700482 }
bungemanc292b5f2016-12-20 12:04:29 -0500483 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWidthTrait, &width)) {
bungemana4c4a2d2014-10-20 13:33:19 -0700484 width = 0;
485 }
bungemanc292b5f2016-12-20 12:04:29 -0500486 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800487 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700488 }
489
bungeman2873c762017-01-13 11:40:21 -0500490 return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
bungeman6e45bda2016-07-25 15:11:49 -0700491 ct_width_to_fontstyle(width),
bungemana4c4a2d2014-10-20 13:33:19 -0700492 slant ? SkFontStyle::kItalic_Slant
bungemanb4bb7d82016-04-27 10:21:04 -0700493 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000494}
495
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000496class SkTypeface_Mac : public SkTypeface {
497public:
bungemanc292b5f2016-12-20 12:04:29 -0500498 SkTypeface_Mac(UniqueCFRef<CTFontRef> fontRef, UniqueCFRef<CFTypeRef> resourceRef,
bungeman78884012015-06-08 13:39:12 -0700499 const SkFontStyle& fs, bool isFixedPitch,
bungeman53d5c6e2016-04-08 07:22:29 -0700500 bool isLocalStream)
bungemane3aea102016-07-13 05:16:58 -0700501 : SkTypeface(fs, isFixedPitch)
bungemanc292b5f2016-12-20 12:04:29 -0500502 , fFontRef(std::move(fontRef))
503 , fOriginatingCFTypeRef(std::move(resourceRef))
504 , fHasColorGlyphs(
505 SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
caseq26337e92014-06-30 12:14:52 -0700506 , fIsLocalStream(isLocalStream)
reed@google.comce8b3de2013-03-26 19:30:16 +0000507 {
bungemanc292b5f2016-12-20 12:04:29 -0500508 SkASSERT(fFontRef);
reed@google.comce8b3de2013-03-26 19:30:16 +0000509 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000510
bungemanc292b5f2016-12-20 12:04:29 -0500511 UniqueCFRef<CTFontRef> fFontRef;
512 UniqueCFRef<CFTypeRef> fOriginatingCFTypeRef;
bungemane3bea5c2015-04-07 07:34:36 -0700513 const bool fHasColorGlyphs;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000514
515protected:
mtklein36352bf2015-03-25 18:17:31 -0700516 int onGetUPEM() const override;
517 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
bungemanf93d7112016-09-16 06:24:20 -0700518 std::unique_ptr<SkFontData> onMakeFontData() const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500519 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
520 int coordinateCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700521 void onGetFamilyName(SkString* familyName) const override;
522 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
523 int onGetTableTags(SkFontTableTag tags[]) const override;
bungemanc292b5f2016-12-20 12:04:29 -0500524 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
reeda9322c22016-04-12 06:47:05 -0700525 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
526 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700527 void onFilterRec(SkScalerContextRec*) const override;
528 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
Hal Canary209e4b12017-05-04 14:23:55 -0400529 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
bungemanc292b5f2016-12-20 12:04:29 -0500530 int onCharsToGlyphs(const void* chars, Encoding,
531 uint16_t glyphs[], int glyphCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700532 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000533
Mike Reed342a9fa2017-05-03 15:48:22 -0400534 void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
535
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000536private:
caseq26337e92014-06-30 12:14:52 -0700537 bool fIsLocalStream;
reed@google.comce8b3de2013-03-26 19:30:16 +0000538
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000539 typedef SkTypeface INHERITED;
540};
541
bungeman82a455f2016-04-14 08:04:45 -0700542static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
bungeman64fb51d2015-05-04 12:03:50 -0700543 CTFontRef self = (CTFontRef)context;
Mike Reed342a9fa2017-05-03 15:48:22 -0400544 CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000545
bungeman64fb51d2015-05-04 12:03:50 -0700546 return CFEqual(self, other);
547}
548
bungemanc292b5f2016-12-20 12:04:29 -0500549/** Creates a typeface, searching the cache if isLocalStream is false. */
550static SkTypeface* create_from_CTFontRef(UniqueCFRef<CTFontRef> font,
551 UniqueCFRef<CFTypeRef> resource,
552 bool isLocalStream) {
553 SkASSERT(font);
bungemanbea97482016-08-24 08:29:50 -0700554
555 if (!isLocalStream) {
556 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)font.get());
557 if (face) {
558 return face;
559 }
560 }
561
bungemanc292b5f2016-12-20 12:04:29 -0500562 UniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
bungeman2873c762017-01-13 11:40:21 -0500563 SkFontStyle style = fontstyle_from_descriptor(desc.get(), isLocalStream);
bungemanc292b5f2016-12-20 12:04:29 -0500564 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
bungemanbea97482016-08-24 08:29:50 -0700565 bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
566
bungemanc292b5f2016-12-20 12:04:29 -0500567 SkTypeface* face = new SkTypeface_Mac(std::move(font), std::move(resource),
bungemanbea97482016-08-24 08:29:50 -0700568 style, isFixedPitch, isLocalStream);
569 if (!isLocalStream) {
570 SkTypefaceCache::Add(face);
571 }
572 return face;
573}
574
575/** Creates a typeface from a descriptor, searching the cache. */
576static SkTypeface* create_from_desc(CTFontDescriptorRef desc) {
bungemanc292b5f2016-12-20 12:04:29 -0500577 UniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -0700578 if (!ctFont) {
579 return nullptr;
580 }
581
bungemanc292b5f2016-12-20 12:04:29 -0500582 return create_from_CTFontRef(std::move(ctFont), nullptr, false);
bungemanbea97482016-08-24 08:29:50 -0700583}
584
bungemanc292b5f2016-12-20 12:04:29 -0500585static UniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
586 const SkFontStyle& style) {
bungemanc292b5f2016-12-20 12:04:29 -0500587 UniqueCFRef<CFMutableDictionaryRef> cfAttributes(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000588 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
589 &kCFTypeDictionaryKeyCallBacks,
590 &kCFTypeDictionaryValueCallBacks));
591
bungemanc292b5f2016-12-20 12:04:29 -0500592 UniqueCFRef<CFMutableDictionaryRef> cfTraits(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000593 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
594 &kCFTypeDictionaryKeyCallBacks,
595 &kCFTypeDictionaryValueCallBacks));
596
bungeman83f1f442017-02-06 13:21:33 -0500597 if (!cfAttributes || !cfTraits) {
halcanary96fcdcc2015-08-27 07:41:13 -0700598 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000599 }
600
bungemanf1ef1fa2017-02-09 14:23:46 -0500601 // CTFontTraits (symbolic)
bungeman83f1f442017-02-06 13:21:33 -0500602 CTFontSymbolicTraits ctFontTraits = 0;
603 if (style.weight() >= SkFontStyle::kBold_Weight) {
604 ctFontTraits |= kCTFontBoldTrait;
605 }
606 if (style.slant() != SkFontStyle::kUpright_Slant) {
607 ctFontTraits |= kCTFontItalicTrait;
608 }
609 UniqueCFRef<CFNumberRef> cfFontTraits(
610 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
611 if (cfFontTraits) {
612 CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get());
bungeman83f1f442017-02-06 13:21:33 -0500613 }
bungemanf1ef1fa2017-02-09 14:23:46 -0500614 // CTFontTraits (weight)
615 CGFloat ctWeight = fontstyle_to_ct_weight(style.weight());
616 UniqueCFRef<CFNumberRef> cfFontWeight(
617 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
618 if (cfFontWeight) {
619 CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
620 }
621 // CTFontTraits (width)
622 CGFloat ctWidth = fontstyle_to_ct_width(style.weight());
623 UniqueCFRef<CFNumberRef> cfFontWidth(
624 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
625 if (cfFontWidth) {
626 CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
627 }
628 // CTFontTraits (slant)
629 CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1;
630 UniqueCFRef<CFNumberRef> cfFontSlant(
631 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
632 if (cfFontSlant) {
633 CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
634 }
635 // CTFontTraits
636 CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
bungeman83f1f442017-02-06 13:21:33 -0500637
638 // CTFontFamilyName
639 if (familyName) {
640 UniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
641 if (cfFontName) {
642 CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
643 }
644 }
bungeman64fb51d2015-05-04 12:03:50 -0700645
bungemanc292b5f2016-12-20 12:04:29 -0500646 return UniqueCFRef<CTFontDescriptorRef>(
647 CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
bungemanbea97482016-08-24 08:29:50 -0700648}
649
650/** Creates a typeface from a name, searching the cache. */
651static SkTypeface* create_from_name(const char familyName[], const SkFontStyle& style) {
bungemanc292b5f2016-12-20 12:04:29 -0500652 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
bungemanbea97482016-08-24 08:29:50 -0700653 if (!desc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700654 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700655 }
bungemanc292b5f2016-12-20 12:04:29 -0500656 return create_from_desc(desc.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000657}
658
659///////////////////////////////////////////////////////////////////////////////
660
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000661/* This function is visible on the outside. It first searches the cache, and if
662 * not found, returns a new entry (after adding it to the cache).
663 */
bungemanc292b5f2016-12-20 12:04:29 -0500664SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef font, CFTypeRef resource) {
665 CFRetain(font);
666 if (resource) {
667 CFRetain(resource);
bungeman53d5c6e2016-04-08 07:22:29 -0700668 }
bungemanc292b5f2016-12-20 12:04:29 -0500669 return create_from_CTFontRef(UniqueCFRef<CTFontRef>(font),
670 UniqueCFRef<CFTypeRef>(resource),
671 false);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000672}
673
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000674static const char* map_css_names(const char* name) {
675 static const struct {
676 const char* fFrom; // name the caller specified
677 const char* fTo; // "canonical" name we map to
678 } gPairs[] = {
679 { "sans-serif", "Helvetica" },
680 { "serif", "Times" },
681 { "monospace", "Courier" }
682 };
683
684 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
685 if (strcmp(name, gPairs[i].fFrom) == 0) {
686 return gPairs[i].fTo;
687 }
688 }
689 return name; // no change
690}
691
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000692///////////////////////////////////////////////////////////////////////////////
693
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000694class SkScalerContext_Mac : public SkScalerContext {
695public:
bungeman7cfd46a2016-10-20 16:06:52 -0400696 SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000697
698protected:
mtklein36352bf2015-03-25 18:17:31 -0700699 unsigned generateGlyphCount(void) override;
700 uint16_t generateCharToGlyph(SkUnichar uni) override;
701 void generateAdvance(SkGlyph* glyph) override;
702 void generateMetrics(SkGlyph* glyph) override;
703 void generateImage(const SkGlyph& glyph) override;
Ben Wagner6e9ac122016-11-11 14:31:06 -0500704 void generatePath(SkGlyphID glyph, SkPath* path) override;
mtklein36352bf2015-03-25 18:17:31 -0700705 void generateFontMetrics(SkPaint::FontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000706
707private:
708 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000709
bungeman@google.comcefd9812013-05-15 15:07:32 +0000710 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
711 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000712
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000713 Offscreen fOffscreen;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000714
bungeman3b4b66c2015-01-08 08:33:44 -0800715 /** Unrotated variant of fCTFont.
716 *
717 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
718 * advances, but always sets the height to 0. This font is used to get the advances of the
719 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000720 *
721 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
bungeman18ec7b92017-02-09 17:15:59 -0500722 * This makes kCTFontOrientationDefault dangerous, because the metrics from
723 * kCTFontOrientationHorizontal are in a different space from kCTFontOrientationVertical.
724 * With kCTFontOrientationVertical the advances must be unrotated.
bungemanef27ce32015-10-29 09:30:32 -0700725 *
726 * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
727 * different underlying font data. As a result, avoid ever creating more than one CTFont per
728 * SkScalerContext to ensure that only one CTFont is used.
729 *
730 * As a result of the above (and other constraints) this font contains the size, but not the
731 * transform. The transform must always be applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000732 */
bungemanc292b5f2016-12-20 12:04:29 -0500733 UniqueCFRef<CTFontRef> fCTFont;
bungemanef27ce32015-10-29 09:30:32 -0700734
735 /** The transform without the font size. */
736 CGAffineTransform fTransform;
737 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000738
bungemanc292b5f2016-12-20 12:04:29 -0500739 UniqueCFRef<CGFontRef> fCGFont;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000740 uint16_t fGlyphCount;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000741 const bool fDoSubPosition;
742 const bool fVertical;
743
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000744 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000745
746 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000747};
748
bungeman7cbeaae2015-09-22 09:54:56 -0700749// CTFontCreateCopyWithAttributes or CTFontCreateCopyWithSymbolicTraits cannot be used on 10.10
bungeman05846312015-09-23 12:51:28 -0700750// and later, as they will return different underlying fonts depending on the size requested.
751// It is not possible to use descriptors with CTFontCreateWithFontDescriptor, since that does not
752// work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
753// CGFont.
bungemanc292b5f2016-12-20 12:04:29 -0500754static UniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
755 const CGAffineTransform* transform)
bungeman7cbeaae2015-09-22 09:54:56 -0700756{
bungemanc292b5f2016-12-20 12:04:29 -0500757 UniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700758
bungeman05846312015-09-23 12:51:28 -0700759 // The last parameter (CTFontDescriptorRef attributes) *must* be nullptr.
760 // If non-nullptr then with fonts with variation axes, the copy will fail in
761 // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
762 // which it quite obviously is not.
bungeman7cbeaae2015-09-22 09:54:56 -0700763
bungeman05846312015-09-23 12:51:28 -0700764 // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
765 // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
766 // the scaler context, since they aren't 'normal'.
bungemanc292b5f2016-12-20 12:04:29 -0500767 return UniqueCFRef<CTFontRef>(
768 CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, transform, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700769}
770
bungeman7cfd46a2016-10-20 16:06:52 -0400771SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
reeda9322c22016-04-12 06:47:05 -0700772 const SkScalerContextEffects& effects,
reed@google.com0da48612013-03-19 16:06:52 +0000773 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400774 : INHERITED(std::move(typeface), effects, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000775 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
776 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
777
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000778{
reedd0f41732015-07-10 12:08:38 -0700779 AUTO_CG_LOCK();
780
Mike Reed342a9fa2017-05-03 15:48:22 -0400781 CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000782 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000783 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
784 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000785
bungeman34902632014-12-10 21:43:27 -0800786 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
787 // As a result, it is necessary to know the actual device size and request that.
788 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800789 SkMatrix skTransform;
bungemane55131c2016-08-24 12:01:31 -0700790 bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
bungeman4ec46aa2017-02-15 17:49:12 -0500791 &scale, &skTransform, nullptr, nullptr, nullptr);
bungemanaae30912015-03-02 13:43:26 -0800792 fTransform = MatrixToCGAffineTransform(skTransform);
bungeman1f0e78d2016-08-23 13:19:01 -0700793 // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
794 // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
bungemane55131c2016-08-24 12:01:31 -0700795 if (invertible) {
bungeman1f0e78d2016-08-23 13:19:01 -0700796 fInvTransform = CGAffineTransformInvert(fTransform);
797 } else {
798 fInvTransform = fTransform;
799 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000800
bungemanbe2284d2014-11-25 08:08:09 -0800801 // The transform contains everything except the requested text size.
802 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -0800803 CGFloat textSize = ScalarToCG(scale.y());
bungemanc292b5f2016-12-20 12:04:29 -0500804 fCTFont = ctfont_create_exact_copy(ctFont, textSize, nullptr);
805 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000806}
807
808CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
809 CGGlyph glyphID, size_t* rowBytesPtr,
mtkleinbbd40182015-09-08 08:19:33 -0700810 bool generateA8FromLCD) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000811 if (!fRGBSpace) {
812 //It doesn't appear to matter what color space is specified.
813 //Regular blends and antialiased text are always (s*a + d*(1-a))
814 //and smoothed text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -0700815 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000816 }
817
818 // default to kBW_Format
819 bool doAA = false;
820 bool doLCD = false;
821
822 if (SkMask::kBW_Format != glyph.fMaskFormat) {
823 doLCD = true;
824 doAA = true;
825 }
826
827 // FIXME: lcd smoothed un-hinted rasterization unsupported.
828 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
829 doLCD = false;
830 doAA = true;
831 }
832
bungeman34902632014-12-10 21:43:27 -0800833 // If this font might have color glyphs, disable LCD as there's no way to support it.
834 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
bungemane280d062016-03-24 11:27:05 -0700835 // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
bungeman34902632014-12-10 21:43:27 -0800836 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
837 doLCD = false;
838 }
839
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000840 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
841 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
842 if (fSize.fWidth < glyph.fWidth) {
843 fSize.fWidth = RoundSize(glyph.fWidth);
844 }
845 if (fSize.fHeight < glyph.fHeight) {
846 fSize.fHeight = RoundSize(glyph.fHeight);
847 }
848
849 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
850 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -0800851 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
852 ? kCGImageAlphaPremultipliedFirst
853 : kCGImageAlphaNoneSkipFirst;
854 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -0700855 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungemanc292b5f2016-12-20 12:04:29 -0500856 rowBytes, fRGBSpace.get(), bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000857
bungeman34902632014-12-10 21:43:27 -0800858 // Skia handles quantization and subpixel positioning,
859 // so disable quantization and enabe subpixel positioning in CG.
bungemanc292b5f2016-12-20 12:04:29 -0500860 CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
861 CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000862
bungeman@google.comcefd9812013-05-15 15:07:32 +0000863 // Because CG always draws from the horizontal baseline,
864 // if there is a non-integral translation from the horizontal origin to the vertical origin,
865 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungemanc292b5f2016-12-20 12:04:29 -0500866 CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
867 CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
bungeman34902632014-12-10 21:43:27 -0800868
bungemanc292b5f2016-12-20 12:04:29 -0500869 CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000870
bungemane280d062016-03-24 11:27:05 -0700871 // Draw black on white to create mask. (Special path exists to speed this up in CG.)
bungemanc292b5f2016-12-20 12:04:29 -0500872 CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000873
874 // force our checks below to happen
875 fDoAA = !doAA;
876 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -0800877
bungemanc292b5f2016-12-20 12:04:29 -0500878 CGContextSetTextMatrix(fCG.get(), context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000879 }
880
881 if (fDoAA != doAA) {
bungemanc292b5f2016-12-20 12:04:29 -0500882 CGContextSetShouldAntialias(fCG.get(), doAA);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000883 fDoAA = doAA;
884 }
885 if (fDoLCD != doLCD) {
bungemanc292b5f2016-12-20 12:04:29 -0500886 CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000887 fDoLCD = doLCD;
888 }
889
890 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
891 // skip rows based on the glyph's height
892 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
893
bungemane280d062016-03-24 11:27:05 -0700894 // Erase to white (or transparent black if it's a color glyph, to not composite against white).
895 uint32_t bgColor = (SkMask::kARGB32_Format != glyph.fMaskFormat) ? 0xFFFFFFFF : 0x00000000;
896 sk_memset_rect32(image, bgColor, glyph.fWidth, glyph.fHeight, rowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000897
898 float subX = 0;
899 float subY = 0;
900 if (context.fDoSubPosition) {
901 subX = SkFixedToFloat(glyph.getSubXFixed());
902 subY = SkFixedToFloat(glyph.getSubYFixed());
903 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000904
bungeman34902632014-12-10 21:43:27 -0800905 // CoreText and CoreGraphics always draw using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000906 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000907 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000908 context.getVerticalOffset(glyphID, &offset);
909 subX += offset.fX;
910 subY += offset.fY;
911 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000912
bungeman34902632014-12-10 21:43:27 -0800913 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -0800914 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
915 // 'positions' which are in text space. The glyph location (in device space) must be
916 // mapped into text space, so that CG can convert it back into device space.
Hal Canary55325b72017-01-03 10:36:17 -0500917 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
bungeman1b5f25c2015-06-08 14:32:24 -0700918 //
bungemanaae30912015-03-02 13:43:26 -0800919 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
920 // So always make the font transform identity and place the transform on the context.
921 point = CGPointApplyAffineTransform(point, context.fInvTransform);
922
bungemanc292b5f2016-12-20 12:04:29 -0500923 CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000924
925 SkASSERT(rowBytesPtr);
926 *rowBytesPtr = rowBytes;
927 return image;
928}
929
bungeman@google.comcefd9812013-05-15 15:07:32 +0000930void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
bungemanc2ba75b2016-12-14 16:53:00 -0500931 // CTFontGetVerticalTranslationsForGlyphs produces cgVertOffset in CG units (pixels, y up).
bungeman@google.comcefd9812013-05-15 15:07:32 +0000932 CGSize cgVertOffset;
bungemanc292b5f2016-12-20 12:04:29 -0500933 CTFontGetVerticalTranslationsForGlyphs(fCTFont.get(), &glyphID, &cgVertOffset, 1);
bungemanef27ce32015-10-29 09:30:32 -0700934 cgVertOffset = CGSizeApplyAffineTransform(cgVertOffset, fTransform);
935 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
936 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
937 skVertOffset.fY = -skVertOffset.fY;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000938 *offset = skVertOffset;
939}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000940
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000941unsigned SkScalerContext_Mac::generateGlyphCount(void) {
942 return fGlyphCount;
943}
944
945uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
reedd0f41732015-07-10 12:08:38 -0700946 AUTO_CG_LOCK();
947
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000948 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000949 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000950
951 // Get the glyph
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000952 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar);
953 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000954
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000955 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
956 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
957 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
bungemanc292b5f2016-12-20 12:04:29 -0500958 CTFontGetGlyphsForCharacters(fCTFont.get(), theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000959 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000960}
961
962void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
963 this->generateMetrics(glyph);
964}
965
bungeman@google.comcefd9812013-05-15 15:07:32 +0000966void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
reedd0f41732015-07-10 12:08:38 -0700967 AUTO_CG_LOCK();
968
djsollen1b277042014-08-06 06:58:06 -0700969 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +0000970 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000971
bungeman@google.comcefd9812013-05-15 15:07:32 +0000972 // The following block produces cgAdvance in CG units (pixels, y up).
973 CGSize cgAdvance;
974 if (fVertical) {
bungeman18ec7b92017-02-09 17:15:59 -0500975 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationVertical,
bungeman@google.comcefd9812013-05-15 15:07:32 +0000976 &cgGlyph, &cgAdvance, 1);
bungeman3b4b66c2015-01-08 08:33:44 -0800977 // Vertical advances are returned as widths instead of heights.
978 SkTSwap(cgAdvance.height, cgAdvance.width);
979 cgAdvance.height = -cgAdvance.height;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000980 } else {
bungeman18ec7b92017-02-09 17:15:59 -0500981 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
bungeman@google.comcefd9812013-05-15 15:07:32 +0000982 &cgGlyph, &cgAdvance, 1);
983 }
bungemanef27ce32015-10-29 09:30:32 -0700984 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700985 glyph->fAdvanceX = CGToFloat(cgAdvance.width);
986 glyph->fAdvanceY = -CGToFloat(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000987
bungeman@google.comcefd9812013-05-15 15:07:32 +0000988 // The following produces skBounds in SkGlyph units (pixels, y down),
989 // or returns early if skBounds would be empty.
990 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000991
bungemanc2ba75b2016-12-14 16:53:00 -0500992 // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
993 // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
994 // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
995 // glyph is vertical. This avoids any diagreement between the various means of retrieving
996 // vertical metrics.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000997 {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000998 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
999 CGRect cgBounds;
bungeman18ec7b92017-02-09 17:15:59 -05001000 CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001001 &cgGlyph, &cgBounds, 1);
bungemanef27ce32015-10-29 09:30:32 -07001002 cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001003
bungeman@google.comcefd9812013-05-15 15:07:32 +00001004 // BUG?
1005 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1006 // it should be empty. So, if we see a zero-advance, we check if it has an
1007 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1008 // is rare, so we won't incur a big performance cost for this extra check.
1009 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
bungemanc292b5f2016-12-20 12:04:29 -05001010 UniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, nullptr));
1011 if (!path || CGPathIsEmpty(path.get())) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001012 return;
1013 }
1014 }
1015
1016 if (CGRectIsEmpty_inline(cgBounds)) {
1017 return;
1018 }
1019
1020 // Convert cgBounds to SkGlyph units (pixels, y down).
1021 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1022 cgBounds.size.width, cgBounds.size.height);
1023 }
1024
1025 if (fVertical) {
bungemanc2ba75b2016-12-14 16:53:00 -05001026 // Due to possible vertical bounds bugs and simplicity, skBounds is the horizontal bounds.
bungeman@google.comcefd9812013-05-15 15:07:32 +00001027 // Convert these horizontal bounds into vertical bounds.
1028 SkPoint offset;
bungemanc2ba75b2016-12-14 16:53:00 -05001029 this->getVerticalOffset(cgGlyph, &offset);
bungeman@google.comcefd9812013-05-15 15:07:32 +00001030 skBounds.offset(offset);
1031 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001032
bungeman@google.comcefd9812013-05-15 15:07:32 +00001033 // Currently the bounds are based on being rendered at (0,0).
1034 // The top left must not move, since that is the base from which subpixel positioning is offset.
1035 if (fDoSubPosition) {
1036 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1037 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1038 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001039
bungeman@google.comcefd9812013-05-15 15:07:32 +00001040 SkIRect skIBounds;
1041 skBounds.roundOut(&skIBounds);
1042 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1043 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1044 // is not currently known, as CG dilates the outlines by some percentage.
1045 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1046 skIBounds.outset(1, 1);
1047 glyph->fLeft = SkToS16(skIBounds.fLeft);
1048 glyph->fTop = SkToS16(skIBounds.fTop);
1049 glyph->fWidth = SkToU16(skIBounds.width());
1050 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001051}
1052
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001053#include "SkColorPriv.h"
1054
bungemane280d062016-03-24 11:27:05 -07001055static void build_power_table(uint8_t table[]) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001056 for (int i = 0; i < 256; i++) {
1057 float x = i / 255.f;
bungemane280d062016-03-24 11:27:05 -07001058 int xx = SkScalarRoundToInt(x * x * 255);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001059 table[i] = SkToU8(xx);
1060 }
1061}
1062
1063/**
1064 * This will invert the gamma applied by CoreGraphics, so we can get linear
1065 * values.
1066 *
1067 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1068 * The color space used does not appear to affect this choice.
1069 */
1070static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1071 static bool gInited;
1072 static uint8_t gTableCoreGraphicsSmoothing[256];
1073 if (!gInited) {
bungemane280d062016-03-24 11:27:05 -07001074 build_power_table(gTableCoreGraphicsSmoothing);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001075 gInited = true;
1076 }
1077 return gTableCoreGraphicsSmoothing;
1078}
1079
1080static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1081 while (count > 0) {
1082 uint8_t mask = 0;
1083 for (int i = 7; i >= 0; --i) {
bungemane280d062016-03-24 11:27:05 -07001084 mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001085 if (0 == --count) {
1086 break;
1087 }
1088 }
1089 *dst++ = mask;
1090 }
1091}
1092
1093template<bool APPLY_PREBLEND>
1094static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
bungemane280d062016-03-24 11:27:05 -07001095 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
1096 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
1097 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
bungeman3b4b66c2015-01-08 08:33:44 -08001098 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1099#if SK_SHOW_TEXT_BLIT_COVERAGE
1100 lum = SkTMax(lum, (U8CPU)0x30);
1101#endif
1102 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001103}
1104template<bool APPLY_PREBLEND>
1105static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1106 const SkGlyph& glyph, const uint8_t* table8) {
1107 const int width = glyph.fWidth;
1108 size_t dstRB = glyph.rowBytes();
1109 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1110
1111 for (int y = 0; y < glyph.fHeight; y++) {
1112 for (int i = 0; i < width; ++i) {
1113 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1114 }
bungemanc292b5f2016-12-20 12:04:29 -05001115 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1116 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001117 }
1118}
1119
1120template<bool APPLY_PREBLEND>
1121static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1122 const uint8_t* tableG,
1123 const uint8_t* tableB) {
bungemane280d062016-03-24 11:27:05 -07001124 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
1125 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
1126 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001127#if SK_SHOW_TEXT_BLIT_COVERAGE
1128 r = SkTMax(r, (U8CPU)0x30);
1129 g = SkTMax(g, (U8CPU)0x30);
1130 b = SkTMax(b, (U8CPU)0x30);
1131#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001132 return SkPack888ToRGB16(r, g, b);
1133}
1134template<bool APPLY_PREBLEND>
1135static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1136 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1137 const int width = glyph.fWidth;
1138 size_t dstRB = glyph.rowBytes();
1139 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1140
1141 for (int y = 0; y < glyph.fHeight; y++) {
1142 for (int i = 0; i < width; i++) {
1143 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1144 }
bungemanc292b5f2016-12-20 12:04:29 -05001145 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1146 dst = SkTAddOffset<uint16_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001147 }
1148}
1149
bungeman34902632014-12-10 21:43:27 -08001150static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1151 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001152 U8CPU r = (rgb >> 16) & 0xFF;
1153 U8CPU g = (rgb >> 8) & 0xFF;
1154 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001155#if SK_SHOW_TEXT_BLIT_COVERAGE
1156 a = SkTMax(a, (U8CPU)0x30);
1157#endif
bungeman34902632014-12-10 21:43:27 -08001158 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001159}
reed@google.comf77b35d2013-05-02 20:39:44 +00001160
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001161void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
bungemanc292b5f2016-12-20 12:04:29 -05001162 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001163
1164 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1165 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1166
1167 // Draw the glyph
1168 size_t cgRowBytes;
1169 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
halcanary96fcdcc2015-08-27 07:41:13 -07001170 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001171 return;
1172 }
1173
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001174 // Fix the glyph
bungemanc292b5f2016-12-20 12:04:29 -05001175 if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
1176 (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD))
1177 {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001178 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1179
1180 //Note that the following cannot really be integrated into the
1181 //pre-blend, since we may not be applying the pre-blend; when we aren't
1182 //applying the pre-blend it means that a filter wants linear anyway.
1183 //Other code may also be applying the pre-blend, so we'd need another
1184 //one with this and one without.
1185 CGRGBPixel* addr = cgPixels;
1186 for (int y = 0; y < glyph.fHeight; ++y) {
1187 for (int x = 0; x < glyph.fWidth; ++x) {
1188 int r = (addr[x] >> 16) & 0xFF;
1189 int g = (addr[x] >> 8) & 0xFF;
1190 int b = (addr[x] >> 0) & 0xFF;
1191 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1192 }
bungemane280d062016-03-24 11:27:05 -07001193 addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001194 }
1195 }
1196
1197 // Convert glyph to mask
1198 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001199 case SkMask::kLCD16_Format: {
1200 if (fPreBlend.isApplicable()) {
1201 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1202 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1203 } else {
1204 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1205 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1206 }
1207 } break;
1208 case SkMask::kA8_Format: {
1209 if (fPreBlend.isApplicable()) {
1210 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1211 } else {
1212 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1213 }
1214 } break;
1215 case SkMask::kBW_Format: {
1216 const int width = glyph.fWidth;
1217 size_t dstRB = glyph.rowBytes();
1218 uint8_t* dst = (uint8_t*)glyph.fImage;
1219 for (int y = 0; y < glyph.fHeight; y++) {
1220 cgpixels_to_bits(dst, cgPixels, width);
bungemanc292b5f2016-12-20 12:04:29 -05001221 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1222 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001223 }
1224 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001225 case SkMask::kARGB32_Format: {
1226 const int width = glyph.fWidth;
1227 size_t dstRB = glyph.rowBytes();
1228 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1229 for (int y = 0; y < glyph.fHeight; y++) {
1230 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001231 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001232 }
bungemanc292b5f2016-12-20 12:04:29 -05001233 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1234 dst = SkTAddOffset<SkPMColor>(dst, dstRB);
reed@google.comf77b35d2013-05-02 20:39:44 +00001235 }
1236 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001237 default:
1238 SkDEBUGFAIL("unexpected mask format");
1239 break;
1240 }
1241}
1242
1243/*
1244 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1245 * seems sufficient, and possibly even correct, to allow the hinted outline
1246 * to be subpixel positioned.
1247 */
1248#define kScaleForSubPixelPositionHinting (4.0f)
1249
Ben Wagner6e9ac122016-11-11 14:31:06 -05001250void SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001251 AUTO_CG_LOCK();
1252
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001253 SkScalar scaleX = SK_Scalar1;
1254 SkScalar scaleY = SK_Scalar1;
1255
bungemanef27ce32015-10-29 09:30:32 -07001256 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001257 /*
1258 * For subpixel positioning, we want to return an unhinted outline, so it
1259 * can be positioned nicely at fractional offsets. However, we special-case
1260 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1261 * we want to retain hinting in the direction orthogonal to the baseline.
1262 * e.g. for horizontal baseline, we want to retain hinting in Y.
1263 * The way we remove hinting is to scale the font by some value (4) in that
1264 * direction, ask for the path, and then scale the path back down.
1265 */
bungeman7cbeaae2015-09-22 09:54:56 -07001266 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001267 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001268 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001269 // now see if we need to restore hinting for axis-aligned baselines
bungeman27876bc2016-02-29 11:22:55 -08001270 switch (this->computeAxisAlignmentForHText()) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001271 case kX_SkAxisAlignment:
1272 scaleY = SK_Scalar1; // want hinting in the Y direction
1273 break;
1274 case kY_SkAxisAlignment:
1275 scaleX = SK_Scalar1; // want hinting in the X direction
1276 break;
1277 default:
1278 break;
1279 }
1280
bungemanef27ce32015-10-29 09:30:32 -07001281 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1282 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001283 }
1284
Ben Wagner6e9ac122016-11-11 14:31:06 -05001285 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
bungemanc292b5f2016-12-20 12:04:29 -05001286 UniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001287
1288 path->reset();
halcanary96fcdcc2015-08-27 07:41:13 -07001289 if (cgPath != nullptr) {
bungemanc292b5f2016-12-20 12:04:29 -05001290 CGPathApply(cgPath.get(), path, SkScalerContext_Mac::CTPathElement);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001291 }
1292
bungeman@google.comcefd9812013-05-15 15:07:32 +00001293 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001294 SkMatrix m;
1295 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1296 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001297 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001298 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001299 SkPoint offset;
1300 getVerticalOffset(cgGlyph, &offset);
1301 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001302 }
1303}
1304
bungeman41078062014-07-07 08:16:37 -07001305void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001306 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001307 return;
1308 }
1309
reedd0f41732015-07-10 12:08:38 -07001310 AUTO_CG_LOCK();
1311
bungemanc292b5f2016-12-20 12:04:29 -05001312 CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001313
bungeman41078062014-07-07 08:16:37 -07001314 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001315 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont.get()));
1316 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001317 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001318 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001319 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1320 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1321 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001322 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungemanc292b5f2016-12-20 12:04:29 -05001323 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont.get()));
1324 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont.get()));
1325 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont.get()));
1326 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont.get()));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001327
Ben Wagner3318da52017-03-23 14:01:22 -04001328 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag;
bungeman41078062014-07-07 08:16:37 -07001329 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman4ec46aa2017-02-15 17:49:12 -05001330
1331 // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
1332 // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
1333 // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
1334 // table are read, but then overwritten if the font is not a system font. As a result, if there
1335 // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
1336 struct OS2HeightMetrics {
1337 SK_OT_SHORT sxHeight;
1338 SK_OT_SHORT sCapHeight;
1339 } heights;
1340 size_t bytesRead = this->getTypeface()->getTableData(
1341 SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
1342 sizeof(heights), &heights);
1343 if (bytesRead == sizeof(heights)) {
1344 // 'fontSize' is correct because the entire resolved size is set by the constructor.
1345 CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
1346 unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
1347 unsigned maxSaneHeight = upem * 2;
1348 uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
1349 if (xHeight && xHeight < maxSaneHeight) {
1350 metrics->fXHeight = CGToScalar(xHeight * fontSize / upem);
1351 }
1352 uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
1353 if (capHeight && capHeight < maxSaneHeight) {
1354 metrics->fCapHeight = CGToScalar(capHeight * fontSize / upem);
1355 }
1356 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001357}
1358
1359void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1360 SkPath* skPath = (SkPath*)info;
1361
1362 // Process the path element
1363 switch (element->type) {
1364 case kCGPathElementMoveToPoint:
1365 skPath->moveTo(element->points[0].x, -element->points[0].y);
1366 break;
1367
1368 case kCGPathElementAddLineToPoint:
1369 skPath->lineTo(element->points[0].x, -element->points[0].y);
1370 break;
1371
1372 case kCGPathElementAddQuadCurveToPoint:
1373 skPath->quadTo(element->points[0].x, -element->points[0].y,
1374 element->points[1].x, -element->points[1].y);
1375 break;
1376
1377 case kCGPathElementAddCurveToPoint:
1378 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1379 element->points[1].x, -element->points[1].y,
1380 element->points[2].x, -element->points[2].y);
1381 break;
1382
1383 case kCGPathElementCloseSubpath:
1384 skPath->close();
1385 break;
1386
1387 default:
1388 SkDEBUGFAIL("Unknown path element!");
1389 break;
1390 }
1391}
1392
1393
1394///////////////////////////////////////////////////////////////////////////////
1395
halcanary96fcdcc2015-08-27 07:41:13 -07001396// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001397// Call must still manage its ownership of provider
Ben Wagnerfc497342017-02-24 11:15:26 -05001398static SkTypeface* create_from_dataProvider(UniqueCFRef<CGDataProviderRef> provider, int ttcIndex) {
1399 if (ttcIndex != 0) {
1400 return nullptr;
1401 }
bungemanc292b5f2016-12-20 12:04:29 -05001402 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
1403 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07001404 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001405 }
bungemanc292b5f2016-12-20 12:04:29 -05001406 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg.get(), 0, nullptr, nullptr));
1407 if (!ct) {
1408 return nullptr;
1409 }
1410 return create_from_CTFontRef(std::move(ct), nullptr, true);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001411}
1412
bungemanc292b5f2016-12-20 12:04:29 -05001413// Web fonts added to the CTFont registry do not return their character set.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001414// Iterate through the font in this case. The existing caller caches the result,
1415// so the performance impact isn't too bad.
1416static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1417 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001418 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001419 SkUnichar* out = glyphToUnicode->begin();
1420 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1421 UniChar unichar = 0;
1422 while (glyphCount > 0) {
1423 CGGlyph glyph;
1424 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
Hal Canary266dcb02017-04-07 11:49:20 -04001425 if (out[glyph] != 0) {
1426 out[glyph] = unichar;
1427 --glyphCount;
1428 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001429 }
1430 if (++unichar == 0) {
1431 break;
1432 }
1433 }
1434}
1435
1436// Construct Glyph to Unicode table.
1437// Unicode code points that require conjugate pairs in utf16 are not
1438// supported.
1439static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1440 SkTDArray<SkUnichar>* glyphToUnicode) {
bungemanc292b5f2016-12-20 12:04:29 -05001441 UniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001442 if (!charSet) {
1443 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1444 return;
1445 }
1446
bungemanc292b5f2016-12-20 12:04:29 -05001447 UniqueCFRef<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001448 if (!bitmap) {
1449 return;
1450 }
bungemanc292b5f2016-12-20 12:04:29 -05001451 CFIndex length = CFDataGetLength(bitmap.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001452 if (!length) {
1453 return;
1454 }
1455 if (length > 8192) {
1456 // TODO: Add support for Unicode above 0xFFFF
1457 // Consider only the BMP portion of the Unicode character points.
1458 // The bitmap may contain other planes, up to plane 16.
1459 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1460 length = 8192;
1461 }
bungemanc292b5f2016-12-20 12:04:29 -05001462 const UInt8* bits = CFDataGetBytePtr(bitmap.get());
reed@google.com7fa2a652014-01-27 13:42:58 +00001463 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001464 SkUnichar* out = glyphToUnicode->begin();
1465 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1466 for (int i = 0; i < length; i++) {
1467 int mask = bits[i];
1468 if (!mask) {
1469 continue;
1470 }
1471 for (int j = 0; j < 8; j++) {
1472 CGGlyph glyph;
1473 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1474 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1475 out[glyph] = unichar;
1476 }
1477 }
1478 }
1479}
1480
halcanary96fcdcc2015-08-27 07:41:13 -07001481/** Assumes src and dst are not nullptr. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001482static void CFStringToSkString(CFStringRef src, SkString* dst) {
1483 // Reserve enough room for the worst-case string,
1484 // plus 1 byte for the trailing null.
1485 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1486 kCFStringEncodingUTF8) + 1;
1487 dst->resize(length);
1488 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1489 // Resize to the actual UTF-8 length used, stripping the null character.
1490 dst->resize(strlen(dst->c_str()));
1491}
1492
Hal Canary209e4b12017-05-04 14:23:55 -04001493std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
reed@google.com2689f612013-03-20 20:01:47 +00001494
reedd0f41732015-07-10 12:08:38 -07001495 AUTO_CG_LOCK();
1496
bungemanc292b5f2016-12-20 12:04:29 -05001497 UniqueCFRef<CTFontRef> ctFont =
1498 ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()), nullptr);
bungeman7cbeaae2015-09-22 09:54:56 -07001499
Hal Canary209e4b12017-05-04 14:23:55 -04001500 std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001501
1502 {
bungemanc292b5f2016-12-20 12:04:29 -05001503 UniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
bungeman256b3512014-07-02 07:57:59 -07001504 if (fontName.get()) {
bungemanc292b5f2016-12-20 12:04:29 -05001505 CFStringToSkString(fontName.get(), &info->fFontName);
bungeman256b3512014-07-02 07:57:59 -07001506 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001507 }
1508
Ben Wagnerc6639052017-03-02 13:31:16 -05001509 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1510 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1511 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1512 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
1513 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont.get(), nullptr));
1514 if (cgFont) {
1515 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
1516 if (cgAxes && CFArrayGetCount(cgAxes.get()) > 0) {
1517 info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
1518 }
1519 }
1520
bungemanc292b5f2016-12-20 12:04:29 -05001521 CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001522
Hal Canary209e4b12017-05-04 14:23:55 -04001523 populate_glyph_to_unicode(ctFont.get(), glyphCount, &info->fGlyphToUnicode);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001524
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001525 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1526 // fonts always have both glyf and loca tables. At the least, this is what
1527 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1528 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001529 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001530 return info;
1531 }
1532
1533 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
bungemanc292b5f2016-12-20 12:04:29 -05001534 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001535 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1536 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1537 }
1538 if (symbolicTraits & kCTFontItalicTrait) {
1539 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1540 }
1541 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001542 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1543 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1544 } else if (stylisticClass & kCTFontScriptsClass) {
1545 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1546 }
bungemanc292b5f2016-12-20 12:04:29 -05001547 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
1548 info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
1549 info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
1550 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
1551 CGRect bbox = CTFontGetBoundingBox(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001552
1553 SkRect r;
1554 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1555 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1556 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1557 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1558
1559 r.roundOut(&(info->fBBox));
1560
1561 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1562 // This probably isn't very good with an italic font.
1563 int16_t min_width = SHRT_MAX;
1564 info->fStemV = 0;
1565 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1566 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1567 CGGlyph glyphs[count];
1568 CGRect boundingRects[count];
bungemanc292b5f2016-12-20 12:04:29 -05001569 if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
bungeman18ec7b92017-02-09 17:15:59 -05001570 CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001571 glyphs, boundingRects, count);
1572 for (size_t i = 0; i < count; i++) {
1573 int16_t width = (int16_t) boundingRects[i].size.width;
1574 if (width > 0 && width < min_width) {
1575 min_width = width;
1576 info->fStemV = min_width;
1577 }
1578 }
1579 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001580 return info;
1581}
1582
1583///////////////////////////////////////////////////////////////////////////////
1584
reed@google.comcc9aad52013-03-21 19:28:10 +00001585static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1586 CTFontRef ctFont = typeface->fFontRef.get();
bungemanc292b5f2016-12-20 12:04:29 -05001587 UniqueCFRef<CFNumberRef> fontFormatRef(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001588 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1589 if (!fontFormatRef) {
1590 return 0;
1591 }
1592
1593 SInt32 fontFormatValue;
bungemanc292b5f2016-12-20 12:04:29 -05001594 if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001595 return 0;
1596 }
1597
1598 switch (fontFormatValue) {
1599 case kCTFontFormatOpenTypePostScript:
1600 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1601 case kCTFontFormatOpenTypeTrueType:
1602 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1603 case kCTFontFormatTrueType:
1604 return SkSFNTHeader::fontType_MacTrueType::TAG;
1605 case kCTFontFormatPostScript:
1606 return SkSFNTHeader::fontType_PostScript::TAG;
1607 case kCTFontFormatBitmap:
1608 return SkSFNTHeader::fontType_MacTrueType::TAG;
1609 case kCTFontFormatUnrecognized:
1610 default:
1611 //CT seems to be unreliable in being able to obtain the type,
1612 //even if all we want is the first four bytes of the font resource.
1613 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1614 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1615 }
1616}
1617
bungeman5f213d92015-01-27 05:39:10 -08001618SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
reed@google.comcc9aad52013-03-21 19:28:10 +00001619 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001620 if (0 == fontType) {
halcanary96fcdcc2015-08-27 07:41:13 -07001621 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001622 }
1623
1624 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001625 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001626 SkTDArray<SkFontTableTag> tableTags;
1627 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001628 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001629
1630 // calc total size for font, save sizes
1631 SkTDArray<size_t> tableSizes;
1632 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1633 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001634 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001635 totalSize += (tableSize + 3) & ~3;
1636 *tableSizes.append() = tableSize;
1637 }
1638
1639 // reserve memory for stream, and zero it (tables must be zero padded)
1640 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1641 char* dataStart = (char*)stream->getMemoryBase();
1642 sk_bzero(dataStart, totalSize);
1643 char* dataPtr = dataStart;
1644
1645 // compute font header entries
1646 uint16_t entrySelector = 0;
1647 uint16_t searchRange = 1;
1648 while (searchRange < numTables >> 1) {
1649 entrySelector++;
1650 searchRange <<= 1;
1651 }
1652 searchRange <<= 4;
1653 uint16_t rangeShift = (numTables << 4) - searchRange;
1654
1655 // write font header
1656 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1657 header->fontType = fontType;
1658 header->numTables = SkEndian_SwapBE16(numTables);
1659 header->searchRange = SkEndian_SwapBE16(searchRange);
1660 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1661 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1662 dataPtr += sizeof(SkSFNTHeader);
1663
1664 // write tables
1665 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1666 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1667 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1668 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001669 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001670 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1671 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1672 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001673 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1674 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001675
1676 dataPtr += (tableSize + 3) & ~3;
1677 ++entry;
1678 }
1679
bungemanb3310c22015-03-02 09:05:36 -08001680 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001681 return stream;
1682}
1683
bungeman41868fe2015-05-20 09:21:04 -07001684struct NonDefaultAxesContext {
1685 SkFixed* axisValue;
1686 CFArrayRef cgAxes;
1687};
1688static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1689 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1690
1691 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1692 return;
1693 }
1694
1695 // The key is a CFString which is a string from the 'name' table.
1696 // Search the cgAxes for an axis with this name, and use its index to store the value.
1697 CFIndex keyIndex = -1;
1698 CFStringRef keyString = static_cast<CFStringRef>(key);
1699 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1700 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1701 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1702 continue;
1703 }
1704
1705 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1706 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1707 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1708 continue;
1709 }
1710 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1711 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1712 keyIndex = i;
1713 break;
1714 }
1715 }
1716 if (keyIndex == -1) {
1717 return;
1718 }
1719
1720 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1721 double valueDouble;
1722 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1723 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1724 {
1725 return;
1726 }
1727 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1728}
Ben Wagnerfc497342017-02-24 11:15:26 -05001729static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount,
bungeman41868fe2015-05-20 09:21:04 -07001730 SkAutoSTMalloc<4, SkFixed>* axisValues)
1731{
Ben Wagnerfc497342017-02-24 11:15:26 -05001732 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1733 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1734 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1735 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
1736 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05001737 if (!cgFont) {
bungeman41868fe2015-05-20 09:21:04 -07001738 return false;
1739 }
1740
bungemanc292b5f2016-12-20 12:04:29 -05001741 UniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
1742 // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
1743 if (!cgVariations) {
1744 return false;
1745 }
1746
1747 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
1748 if (!cgAxes) {
1749 return false;
1750 }
1751 *cgAxisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07001752 axisValues->reset(*cgAxisCount);
1753
1754 // Set all of the axes to their default values.
1755 // Fail if any default value cannot be determined.
1756 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001757 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07001758 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1759 return false;
1760 }
1761
1762 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1763 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1764 kCGFontVariationAxisDefaultValue);
1765 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1766 return false;
1767 }
1768 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
1769 double axisDefaultValueDouble;
1770 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
1771 {
1772 return false;
1773 }
1774 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
1775 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
1776 {
1777 return false;
1778 }
1779 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
1780 }
1781
1782 // Override the default values with the given font's stated axis values.
1783 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
bungemanc292b5f2016-12-20 12:04:29 -05001784 CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
bungeman41868fe2015-05-20 09:21:04 -07001785
1786 return true;
1787}
bungemanf93d7112016-09-16 06:24:20 -07001788std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
bungeman41868fe2015-05-20 09:21:04 -07001789 int index;
bungemanf93d7112016-09-16 06:24:20 -07001790 std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
bungeman41868fe2015-05-20 09:21:04 -07001791
1792 CFIndex cgAxisCount;
1793 SkAutoSTMalloc<4, SkFixed> axisValues;
bungemanc292b5f2016-12-20 12:04:29 -05001794 if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
bungemanf93d7112016-09-16 06:24:20 -07001795 return skstd::make_unique<SkFontData>(std::move(stream), index,
1796 axisValues.get(), cgAxisCount);
bungeman41868fe2015-05-20 09:21:04 -07001797 }
bungemanf93d7112016-09-16 06:24:20 -07001798 return skstd::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
bungeman41868fe2015-05-20 09:21:04 -07001799}
1800
Ben Wagnerfc497342017-02-24 11:15:26 -05001801/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */
1802static UniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
1803 CFArrayRef ctAxes) {
1804
1805 UniqueCFRef<CFMutableDictionaryRef> ctVariations(
1806 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1807 &kCFTypeDictionaryKeyCallBacks,
1808 &kCFTypeDictionaryValueCallBacks));
1809
1810 CFIndex axisCount = CFArrayGetCount(ctAxes);
1811 for (CFIndex i = 0; i < axisCount; ++i) {
1812 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i);
1813 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
1814 return nullptr;
1815 }
1816 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
1817
1818 // The assumption is that values produced by kCTFontVariationAxisNameKey and
1819 // kCGFontVariationAxisName will always be equal.
1820 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
1821 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
1822 return nullptr;
1823 }
1824
1825 CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName);
1826 if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) {
1827 return nullptr;
1828 }
1829
1830 CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1831 if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
1832 return nullptr;
1833 }
1834
1835 CFDictionaryAddValue(ctVariations.get(), axisTag, axisValue);
1836 }
1837 return std::move(ctVariations);
1838}
1839
1840int SkTypeface_Mac::onGetVariationDesignPosition(
1841 SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
1842{
1843 // The CGFont variation data does not contain the tag.
1844
bungemanbc096bf2017-04-25 13:32:50 -04001845 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
1846 // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
Ben Wagnerfc497342017-02-24 11:15:26 -05001847 UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
1848 if (!ctAxes) {
1849 return -1;
1850 }
1851 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
1852 if (!coordinates || coordinateCount < axisCount) {
1853 return axisCount;
1854 }
1855
1856 // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
1857 // When this happens, try converting the CG variation to a CT variation.
1858 // On 10.12 and later, this only returns non-default variations.
1859 UniqueCFRef<CFDictionaryRef> ctVariations(CTFontCopyVariation(fFontRef.get()));
1860 if (!ctVariations) {
1861 // When 10.11 and earlier are no longer supported, the following code can be replaced with
1862 // return -1 and ct_variation_from_cg_variation can be removed.
1863 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
1864 if (!cgFont) {
1865 return -1;
1866 }
1867 UniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
1868 if (!cgVariations) {
1869 return -1;
1870 }
1871 ctVariations = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
1872 if (!ctVariations) {
1873 return -1;
1874 }
1875 }
1876
1877 for (int i = 0; i < axisCount; ++i) {
1878 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
1879 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
1880 return -1;
1881 }
1882 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
1883
1884 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1885 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
1886 return -1;
1887 }
1888 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
1889 int64_t tagLong;
1890 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
1891 return -1;
1892 }
1893 coordinates[i].axis = tagLong;
1894
1895 CGFloat variationCGFloat;
1896 CFTypeRef variationValue = CFDictionaryGetValue(ctVariations.get(), tagNumber);
1897 if (variationValue) {
1898 if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
1899 return -1;
1900 }
1901 CFNumberRef variationNumber = static_cast<CFNumberRef>(variationValue);
1902 if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) {
1903 return -1;
1904 }
1905 } else {
1906 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
1907 if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) {
1908 return -1;
1909 }
1910 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
1911 if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) {
1912 return -1;
1913 }
1914 }
1915 coordinates[i].value = CGToScalar(variationCGFloat);
1916
1917 }
1918 return axisCount;
1919}
1920
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001921///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001922///////////////////////////////////////////////////////////////////////////////
1923
1924int SkTypeface_Mac::onGetUPEM() const {
bungemanc292b5f2016-12-20 12:04:29 -05001925 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
1926 return CGFontGetUnitsPerEm(cgFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001927}
1928
bungeman@google.com839702b2013-08-07 17:09:22 +00001929SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001930 SkTypeface::LocalizedStrings* nameIter =
bungemanc292b5f2016-12-20 12:04:29 -05001931 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07001932 if (nullptr == nameIter) {
bungemanc292b5f2016-12-20 12:04:29 -05001933 CFStringRef cfLanguageRaw;
1934 UniqueCFRef<CFStringRef> cfFamilyName(
1935 CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
1936 UniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
bungeman@google.coma9802692013-08-07 02:45:25 +00001937
1938 SkString skLanguage;
1939 SkString skFamilyName;
bungemanc292b5f2016-12-20 12:04:29 -05001940 if (cfLanguage) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001941 CFStringToSkString(cfLanguage.get(), &skLanguage);
1942 } else {
1943 skLanguage = "und"; //undetermined
1944 }
bungemanc292b5f2016-12-20 12:04:29 -05001945 if (cfFamilyName) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001946 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1947 }
1948
1949 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1950 }
1951 return nameIter;
1952}
1953
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001954int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
bungemanc292b5f2016-12-20 12:04:29 -05001955 UniqueCFRef<CFArrayRef> cfArray(
1956 CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
1957 if (!cfArray) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001958 return 0;
1959 }
bungemanc292b5f2016-12-20 12:04:29 -05001960 int count = SkToInt(CFArrayGetCount(cfArray.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001961 if (tags) {
1962 for (int i = 0; i < count; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001963 uintptr_t fontTag = reinterpret_cast<uintptr_t>(
1964 CFArrayGetValueAtIndex(cfArray.get(), i));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001965 tags[i] = static_cast<SkFontTableTag>(fontTag);
1966 }
1967 }
1968 return count;
1969}
1970
bungemanc292b5f2016-12-20 12:04:29 -05001971// If, as is the case with web fonts, the CTFont data isn't available,
1972// the CGFont data may work. While the CGFont may always provide the
1973// right result, leave the CTFont code path to minimize disruption.
1974static UniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
1975 UniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1976 kCTFontTableOptionNoOptions));
1977 if (!data) {
1978 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
1979 data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
1980 }
1981 return data;
1982}
1983
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001984size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1985 size_t length, void* dstData) const {
bungemanc292b5f2016-12-20 12:04:29 -05001986 UniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
1987 if (!srcData) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001988 return 0;
1989 }
1990
bungemanc292b5f2016-12-20 12:04:29 -05001991 size_t srcSize = CFDataGetLength(srcData.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001992 if (offset >= srcSize) {
1993 return 0;
1994 }
1995 if (length > srcSize - offset) {
1996 length = srcSize - offset;
1997 }
1998 if (dstData) {
bungemanc292b5f2016-12-20 12:04:29 -05001999 memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002000 }
2001 return length;
2002}
2003
reeda9322c22016-04-12 06:47:05 -07002004SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
2005 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04002006 return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002007}
2008
2009void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002010 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2011 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2012 {
2013 rec->fMaskFormat = SkMask::kA8_Format;
2014 // Render the glyphs as close as possible to what was requested.
2015 // The above turns off subpixel rendering, but the user requested it.
2016 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
2017 // See comments below for more details.
2018 rec->setHinting(SkPaint::kNormal_Hinting);
2019 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00002020
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002021 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00002022 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002023 SkScalerContext::kLCD_BGROrder_Flag |
2024 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002025
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002026 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002027
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002028 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002029
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002030 // Only two levels of hinting are supported.
2031 // kNo_Hinting means avoid CoreGraphics outline dilation.
2032 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
2033 // If there is no lcd support, hinting (dilation) cannot be supported.
2034 SkPaint::Hinting hinting = rec->getHinting();
2035 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
2036 hinting = SkPaint::kNo_Hinting;
2037 } else if (SkPaint::kFull_Hinting == hinting) {
2038 hinting = SkPaint::kNormal_Hinting;
2039 }
2040 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002041
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002042 // FIXME: lcd smoothed un-hinted rasterization unsupported.
2043 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
2044 // There is no current means to honor a request for unhinted lcd,
2045 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002046
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002047 // Hinting and smoothing should be orthogonal, but currently they are not.
2048 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
2049 // output is drawn from auto-dilated outlines (the amount of which is
2050 // determined by AppleFontSmoothing). Its regular anti-aliased output is
2051 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002052
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002053 // The behavior of Skia is as follows:
2054 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
2055 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
2056 // channel. This matches [LCD][yes-hint] in weight.
2057 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
2058 // Currenly side with LCD, effectively ignoring the hinting setting.
2059 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002060
bungemanc292b5f2016-12-20 12:04:29 -05002061 if (rec->fMaskFormat == SkMask::kLCD16_Format) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002062 if (lcdSupport) {
2063 //CoreGraphics creates 555 masks for smoothed text anyway.
2064 rec->fMaskFormat = SkMask::kLCD16_Format;
2065 rec->setHinting(SkPaint::kNormal_Hinting);
2066 } else {
2067 rec->fMaskFormat = SkMask::kA8_Format;
2068 }
2069 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002070
bungeman34902632014-12-10 21:43:27 -08002071 // CoreText provides no information as to whether a glyph will be color or not.
2072 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
2073 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08002074 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08002075 rec->fMaskFormat = SkMask::kARGB32_Format;
2076 }
2077
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002078 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2079 // All other masks can use regular gamma.
2080 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
2081#ifndef SK_GAMMA_APPLY_TO_A8
brianosmana1e8f8d2016-04-08 06:47:54 -07002082 // SRGBTODO: Is this correct? Do we want contrast boost?
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002083 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002084#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002085 } else {
2086 //CoreGraphics dialates smoothed text as needed.
2087 rec->setContrast(0);
2088 }
2089}
2090
bungemanc292b5f2016-12-20 12:04:29 -05002091/** Takes ownership of the CFStringRef. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002092static const char* get_str(CFStringRef ref, SkString* str) {
halcanary96fcdcc2015-08-27 07:41:13 -07002093 if (nullptr == ref) {
2094 return nullptr;
bungeman256b3512014-07-02 07:57:59 -07002095 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002096 CFStringToSkString(ref, str);
bungemanc292b5f2016-12-20 12:04:29 -05002097 CFRelease(ref);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002098 return str->c_str();
2099}
2100
bungemanb374d6a2014-09-17 07:48:59 -07002101void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
bungemanc292b5f2016-12-20 12:04:29 -05002102 get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
bungemanb374d6a2014-09-17 07:48:59 -07002103}
2104
reed@google.com5526ede2013-03-25 13:03:37 +00002105void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2106 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002107 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002108
bungemanc292b5f2016-12-20 12:04:29 -05002109 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
2110 desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
2111 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
bungemanb8113782016-07-25 16:54:59 -07002112 desc->setStyle(this->fontStyle());
caseq26337e92014-06-30 12:14:52 -07002113 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002114}
reed@google.com5526ede2013-03-25 13:03:37 +00002115
reed@google.combcb42ae2013-07-02 13:56:39 +00002116int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002117 uint16_t glyphs[], int glyphCount) const
2118{
2119 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
2120 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
2121 // 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 +00002122
reed@google.combcb42ae2013-07-02 13:56:39 +00002123 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002124 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
2125 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00002126 switch (encoding) {
2127 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002128 const char* utf8 = reinterpret_cast<const char*>(chars);
2129 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2130 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00002131 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002132 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
2133 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00002134 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002135 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002136 break;
2137 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002138 case kUTF16_Encoding: {
2139 src = reinterpret_cast<const UniChar*>(chars);
2140 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00002141 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002142 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2143 ++extra;
2144 }
reed@google.combcb42ae2013-07-02 13:56:39 +00002145 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002146 srcCount = glyphCount + extra;
2147 break;
2148 }
2149 case kUTF32_Encoding: {
2150 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
2151 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2152 src = utf16;
2153 for (int i = 0; i < glyphCount; ++i) {
2154 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
2155 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002156 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002157 break;
2158 }
2159 }
2160
halcanary96fcdcc2015-08-27 07:41:13 -07002161 // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002162 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002163 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2164 uint16_t* macGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002165 if (nullptr == macGlyphs || srcCount > glyphCount) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002166 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002167 }
2168
bungemanc292b5f2016-12-20 12:04:29 -05002169 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002170
2171 // If there were any non-bmp, then copy and compact.
halcanary96fcdcc2015-08-27 07:41:13 -07002172 // If 'glyphs' is nullptr, then compact glyphStorage in-place.
2173 // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
2174 // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002175 uint16_t* compactedGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002176 if (nullptr == compactedGlyphs) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002177 compactedGlyphs = macGlyphs;
2178 }
2179 if (srcCount > glyphCount) {
2180 int extra = 0;
2181 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002182 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002183 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2184 ++extra;
2185 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002186 }
2187 }
2188
2189 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002190 return glyphCount;
2191 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002192
2193 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002194 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002195 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002196 return i;
2197 }
2198 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002199 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002200 return glyphCount;
2201}
2202
2203int SkTypeface_Mac::onCountGlyphs() const {
bungemanc292b5f2016-12-20 12:04:29 -05002204 return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
reed@google.combcb42ae2013-07-02 13:56:39 +00002205}
2206
reed@google.com95625db2013-03-25 20:44:02 +00002207///////////////////////////////////////////////////////////////////////////////
2208///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002209
2210static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
bungemanc292b5f2016-12-20 12:04:29 -05002211 UniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
2212 if (!ref) {
reed@google.com83787c52013-03-26 17:19:15 +00002213 return false;
2214 }
bungemanc292b5f2016-12-20 12:04:29 -05002215 CFStringToSkString(ref.get(), value);
reed@google.com83787c52013-03-26 17:19:15 +00002216 return true;
2217}
2218
reed@google.com95625db2013-03-25 20:44:02 +00002219#include "SkFontMgr.h"
2220
reed@google.com964988f2013-03-29 14:57:22 +00002221static inline int sqr(int value) {
2222 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2223 return value * value;
2224}
2225
2226// We normalize each axis (weight, width, italic) to be base-900
2227static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2228 return sqr(a.weight() - b.weight()) +
2229 sqr((a.width() - b.width()) * 100) +
bungemanb4bb7d82016-04-27 10:21:04 -07002230 sqr((a.slant() != b.slant()) * 900);
reed@google.com964988f2013-03-29 14:57:22 +00002231}
2232
reed@google.com83787c52013-03-26 17:19:15 +00002233class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002234public:
bungeman53d5c6e2016-04-08 07:22:29 -07002235 SkFontStyleSet_Mac(CTFontDescriptorRef desc)
halcanary96fcdcc2015-08-27 07:41:13 -07002236 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
bungemanc292b5f2016-12-20 12:04:29 -05002237 , fCount(0)
2238 {
2239 if (!fArray) {
2240 fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
reed@google.comdea7ee02013-03-28 14:12:10 +00002241 }
bungemanc292b5f2016-12-20 12:04:29 -05002242 fCount = SkToInt(CFArrayGetCount(fArray.get()));
reed@google.com83787c52013-03-26 17:19:15 +00002243 }
2244
mtklein36352bf2015-03-25 18:17:31 -07002245 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002246 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002247 }
2248
mtklein36352bf2015-03-25 18:17:31 -07002249 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002250 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002251 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002252 if (style) {
bungeman2873c762017-01-13 11:40:21 -05002253 *style = fontstyle_from_descriptor(desc, false);
reed@google.com83787c52013-03-26 17:19:15 +00002254 }
2255 if (name) {
2256 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2257 name->reset();
2258 }
2259 }
2260 }
2261
mtklein36352bf2015-03-25 18:17:31 -07002262 SkTypeface* createTypeface(int index) override {
bungemanc292b5f2016-12-20 12:04:29 -05002263 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
2264 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002265
bungemanbea97482016-08-24 08:29:50 -07002266 return create_from_desc(desc);
reed@google.com83787c52013-03-26 17:19:15 +00002267 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002268
mtklein36352bf2015-03-25 18:17:31 -07002269 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002270 if (0 == fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002271 return nullptr;
reed@google.com964988f2013-03-29 14:57:22 +00002272 }
bungemanbea97482016-08-24 08:29:50 -07002273 return create_from_desc(findMatchingDesc(pattern));
reed@google.com964988f2013-03-29 14:57:22 +00002274 }
2275
reed@google.com83787c52013-03-26 17:19:15 +00002276private:
bungemanc292b5f2016-12-20 12:04:29 -05002277 UniqueCFRef<CFArrayRef> fArray;
2278 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002279
2280 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2281 int bestMetric = SK_MaxS32;
halcanary96fcdcc2015-08-27 07:41:13 -07002282 CTFontDescriptorRef bestDesc = nullptr;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002283
reed@google.com964988f2013-03-29 14:57:22 +00002284 for (int i = 0; i < fCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002285 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
bungeman2873c762017-01-13 11:40:21 -05002286 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc, false));
reed@google.com964988f2013-03-29 14:57:22 +00002287 if (0 == metric) {
2288 return desc;
2289 }
2290 if (metric < bestMetric) {
2291 bestMetric = metric;
2292 bestDesc = desc;
2293 }
2294 }
2295 SkASSERT(bestDesc);
2296 return bestDesc;
2297 }
reed@google.com83787c52013-03-26 17:19:15 +00002298};
2299
2300class SkFontMgr_Mac : public SkFontMgr {
bungemanc292b5f2016-12-20 12:04:29 -05002301 UniqueCFRef<CFArrayRef> fNames;
2302 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002303
bungemanc292b5f2016-12-20 12:04:29 -05002304 CFStringRef getFamilyNameAt(int index) const {
reed@google.com83787c52013-03-26 17:19:15 +00002305 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002306 return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002307 }
2308
reed@google.com964988f2013-03-29 14:57:22 +00002309 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
bungemanc292b5f2016-12-20 12:04:29 -05002310 UniqueCFRef<CFMutableDictionaryRef> cfAttr(
reed@google.com964988f2013-03-29 14:57:22 +00002311 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2312 &kCFTypeDictionaryKeyCallBacks,
2313 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002314
bungemanc292b5f2016-12-20 12:04:29 -05002315 CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002316
bungemanc292b5f2016-12-20 12:04:29 -05002317 UniqueCFRef<CTFontDescriptorRef> desc(
2318 CTFontDescriptorCreateWithAttributes(cfAttr.get()));
2319 return new SkFontStyleSet_Mac(desc.get());
2320 }
2321
2322 /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
2323 * provide a wrapper here that will return an empty array if need be.
2324 */
2325 static UniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
2326#ifdef SK_BUILD_FOR_IOS
2327 return UniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
2328#else
2329 return UniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
2330#endif
reed@google.com964988f2013-03-29 14:57:22 +00002331 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002332
reed@google.com83787c52013-03-26 17:19:15 +00002333public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002334 SkFontMgr_Mac()
bungemanc292b5f2016-12-20 12:04:29 -05002335 : fNames(CopyAvailableFontFamilyNames())
2336 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0) {}
reed@google.com95625db2013-03-25 20:44:02 +00002337
2338protected:
mtklein36352bf2015-03-25 18:17:31 -07002339 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002340 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002341 }
2342
mtklein36352bf2015-03-25 18:17:31 -07002343 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002344 if ((unsigned)index < (unsigned)fCount) {
bungemanc292b5f2016-12-20 12:04:29 -05002345 CFStringToSkString(this->getFamilyNameAt(index), familyName);
reed@google.com83787c52013-03-26 17:19:15 +00002346 } else {
2347 familyName->reset();
2348 }
reed@google.com95625db2013-03-25 20:44:02 +00002349 }
2350
mtklein36352bf2015-03-25 18:17:31 -07002351 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002352 if ((unsigned)index >= (unsigned)fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002353 return nullptr;
reed@google.com83787c52013-03-26 17:19:15 +00002354 }
bungemanc292b5f2016-12-20 12:04:29 -05002355 return CreateSet(this->getFamilyNameAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002356 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002357
mtklein36352bf2015-03-25 18:17:31 -07002358 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
bungeman7575bb12017-05-01 13:02:42 -04002359 if (!familyName) {
2360 return nullptr;
2361 }
bungemanc292b5f2016-12-20 12:04:29 -05002362 UniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
2363 return CreateSet(cfName.get());
reed@google.com964988f2013-03-29 14:57:22 +00002364 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002365
bungemanc292b5f2016-12-20 12:04:29 -05002366 SkTypeface* onMatchFamilyStyle(const char familyName[],
bungeman7575bb12017-05-01 13:02:42 -04002367 const SkFontStyle& style) const override {
2368 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
2369 return create_from_desc(desc.get());
reed@google.com95625db2013-03-25 20:44:02 +00002370 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002371
bungemanc292b5f2016-12-20 12:04:29 -05002372 SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
2373 const SkFontStyle& style,
2374 const char* bcp47[], int bcp47Count,
2375 SkUnichar character) const override {
2376 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
2377 UniqueCFRef<CTFontRef> currentFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -07002378
2379 // kCFStringEncodingUTF32 is BE unless there is a BOM.
2380 // Since there is no machine endian option, explicitly state machine endian.
2381#ifdef SK_CPU_LENDIAN
2382 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
2383#else
2384 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
2385#endif
bungemanc292b5f2016-12-20 12:04:29 -05002386 UniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
bungemanbea97482016-08-24 08:29:50 -07002387 kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
2388 encoding, false));
bungemanc292b5f2016-12-20 12:04:29 -05002389 CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units.
2390 UniqueCFRef<CTFontRef> fallbackFont(
2391 CTFontCreateForString(currentFont.get(), string.get(), range));
2392 return create_from_CTFontRef(std::move(fallbackFont), nullptr, false);
djsollen33068c12014-11-14 10:52:53 -08002393 }
2394
bungemanc292b5f2016-12-20 12:04:29 -05002395 SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2396 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002397 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002398 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002399
mtklein36352bf2015-03-25 18:17:31 -07002400 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002401 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromData(sk_ref_sp(data)));
2402 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002403 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002404 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002405 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002406 }
2407
bungemanf93d7112016-09-16 06:24:20 -07002408 SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
2409 std::unique_ptr<SkStreamAsset> stream(bareStream);
bungemanc292b5f2016-12-20 12:04:29 -05002410 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromStream(std::move(stream)));
2411 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002412 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002413 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002414 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002415 }
2416
Ben Wagnerfc497342017-02-24 11:15:26 -05002417 /** Creates a dictionary suitable for setting the axes on a CGFont. */
2418 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, const SkFontArguments& args) {
2419 // The CGFont variation data is keyed by name, but lacks the tag.
bungemanf6c71072016-01-21 14:17:47 -08002420 // The CTFont variation data is keyed by tag, and also has the name.
Ben Wagnerfc497342017-02-24 11:15:26 -05002421 // We would like to work with CTFont variations, but creating a CTFont font with
bungemanf6c71072016-01-21 14:17:47 -08002422 // CTFont variation dictionary runs into bugs. So use the CTFont variation data
2423 // to match names to tags to create the appropriate CGFont.
bungemanc292b5f2016-12-20 12:04:29 -05002424 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
bungemanbc096bf2017-04-25 13:32:50 -04002425 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
2426 // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
bungemanc292b5f2016-12-20 12:04:29 -05002427 UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct.get()));
Ben Wagnerfc497342017-02-24 11:15:26 -05002428 if (!ctAxes) {
bungemanf6c71072016-01-21 14:17:47 -08002429 return nullptr;
2430 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002431 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
bungemanf6c71072016-01-21 14:17:47 -08002432
Ben Wagnerfc497342017-02-24 11:15:26 -05002433 const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
bungemanf6c71072016-01-21 14:17:47 -08002434
bungemanc292b5f2016-12-20 12:04:29 -05002435 UniqueCFRef<CFMutableDictionaryRef> dict(
2436 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2437 &kCFTypeDictionaryKeyCallBacks,
2438 &kCFTypeDictionaryValueCallBacks));
2439
bungemanf6c71072016-01-21 14:17:47 -08002440 for (int i = 0; i < axisCount; ++i) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002441 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
bungemanf6c71072016-01-21 14:17:47 -08002442 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2443 return nullptr;
2444 }
2445 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2446
Ben Wagnerfc497342017-02-24 11:15:26 -05002447 // The assumption is that values produced by kCTFontVariationAxisNameKey and
2448 // kCGFontVariationAxisName will always be equal.
2449 // If they are ever not, seach the project history for "get_tag_for_name".
2450 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
bungemanf6c71072016-01-21 14:17:47 -08002451 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2452 return nullptr;
2453 }
2454
Ben Wagnerfc497342017-02-24 11:15:26 -05002455 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
2456 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2457 return nullptr;
bungemanf6c71072016-01-21 14:17:47 -08002458 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002459 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
bungemanf6c71072016-01-21 14:17:47 -08002460 int64_t tagLong;
2461 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2462 return nullptr;
2463 }
2464
2465 // The variation axes can be set to any value, but cg will effectively pin them.
2466 // Pin them here to normalize.
Ben Wagnerfc497342017-02-24 11:15:26 -05002467 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
2468 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
2469 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
bungemanf6c71072016-01-21 14:17:47 -08002470 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2471 !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
2472 !def || CFGetTypeID(def) != CFNumberGetTypeID())
2473 {
2474 return nullptr;
2475 }
2476 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2477 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2478 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2479 double minDouble;
2480 double maxDouble;
2481 double defDouble;
2482 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2483 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
2484 !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
2485 {
2486 return nullptr;
2487 }
2488
2489 double value = defDouble;
bungeman9aec8942017-03-29 13:38:53 -04002490 // The position may be over specified. If there are multiple values for a given axis,
2491 // use the last one since that's what css-fonts-4 requires.
2492 for (int j = position.coordinateCount; j --> 0;) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002493 if (position.coordinates[j].axis == tagLong) {
2494 value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
2495 minDouble, maxDouble);
bungemanf6c71072016-01-21 14:17:47 -08002496 break;
2497 }
2498 }
bungemanc292b5f2016-12-20 12:04:29 -05002499 UniqueCFRef<CFNumberRef> valueNumber(
2500 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2501 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungemanf6c71072016-01-21 14:17:47 -08002502 }
bungemanc292b5f2016-12-20 12:04:29 -05002503 return std::move(dict);
bungemanf6c71072016-01-21 14:17:47 -08002504 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002505 SkTypeface* onCreateFromStream(SkStreamAsset* bs, const SkFontArguments& args) const override {
bungemanf93d7112016-09-16 06:24:20 -07002506 std::unique_ptr<SkStreamAsset> s(bs);
Ben Wagnerfc497342017-02-24 11:15:26 -05002507 if (args.getCollectionIndex() != 0) {
2508 return nullptr;
2509 }
bungemanc292b5f2016-12-20 12:04:29 -05002510 UniqueCFRef<CGDataProviderRef> provider(SkCreateDataProviderFromStream(std::move(s)));
2511 if (!provider) {
bungemanf6c71072016-01-21 14:17:47 -08002512 return nullptr;
2513 }
bungemanc292b5f2016-12-20 12:04:29 -05002514 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2515 if (!cg) {
bungemanf6c71072016-01-21 14:17:47 -08002516 return nullptr;
2517 }
2518
Ben Wagnerfc497342017-02-24 11:15:26 -05002519 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), args);
bungemanf6c71072016-01-21 14:17:47 -08002520 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2521 // created from a data provider does not appear to have any ownership of the underlying
2522 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002523 UniqueCFRef<CGFontRef> cgVariant;
bungemanf6c71072016-01-21 14:17:47 -08002524 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002525 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungemanf6c71072016-01-21 14:17:47 -08002526 } else {
mtklein18300a32016-03-16 13:53:35 -07002527 cgVariant.reset(cg.release());
bungemanf6c71072016-01-21 14:17:47 -08002528 }
2529
bungemanc292b5f2016-12-20 12:04:29 -05002530 UniqueCFRef<CTFontRef> ct(
2531 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungemanf6c71072016-01-21 14:17:47 -08002532 if (!ct) {
2533 return nullptr;
2534 }
bungemanc292b5f2016-12-20 12:04:29 -05002535 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungemanf6c71072016-01-21 14:17:47 -08002536 }
2537
Ben Wagnerfc497342017-02-24 11:15:26 -05002538 /** Creates a dictionary suitable for setting the axes on a CGFont. */
bungemanc292b5f2016-12-20 12:04:29 -05002539 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, SkFontData* fontData) {
2540 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
bungeman41868fe2015-05-20 09:21:04 -07002541 if (!cgAxes) {
halcanary96fcdcc2015-08-27 07:41:13 -07002542 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002543 }
2544
bungemanc292b5f2016-12-20 12:04:29 -05002545 CFIndex axisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07002546 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002547 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002548 }
2549
bungemanc292b5f2016-12-20 12:04:29 -05002550 UniqueCFRef<CFMutableDictionaryRef> dict(
2551 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2552 &kCFTypeDictionaryKeyCallBacks,
2553 &kCFTypeDictionaryValueCallBacks));
2554
bungeman41868fe2015-05-20 09:21:04 -07002555 for (int i = 0; i < fontData->getAxisCount(); ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002556 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07002557 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002558 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002559 }
2560 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2561
2562 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2563 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002564 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002565 }
2566
2567 // The variation axes can be set to any value, but cg will effectively pin them.
2568 // Pin them here to normalize.
2569 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2570 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2571 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2572 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2573 {
halcanary96fcdcc2015-08-27 07:41:13 -07002574 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002575 }
2576 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2577 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2578 double minDouble;
2579 double maxDouble;
2580 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2581 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2582 {
halcanary96fcdcc2015-08-27 07:41:13 -07002583 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002584 }
2585 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
bungemanc292b5f2016-12-20 12:04:29 -05002586 UniqueCFRef<CFNumberRef> valueNumber(
2587 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2588 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungeman41868fe2015-05-20 09:21:04 -07002589 }
bungemanc292b5f2016-12-20 12:04:29 -05002590 return std::move(dict);
bungeman41868fe2015-05-20 09:21:04 -07002591 }
bungemanf93d7112016-09-16 06:24:20 -07002592 SkTypeface* onCreateFromFontData(std::unique_ptr<SkFontData> fontData) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002593 if (fontData->getIndex() != 0) {
2594 return nullptr;
2595 }
bungemanc292b5f2016-12-20 12:04:29 -05002596 UniqueCFRef<CGDataProviderRef> provider(
bungemanf93d7112016-09-16 06:24:20 -07002597 SkCreateDataProviderFromStream(fontData->detachStream()));
bungemanc292b5f2016-12-20 12:04:29 -05002598 if (!provider) {
halcanary96fcdcc2015-08-27 07:41:13 -07002599 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002600 }
bungemanc292b5f2016-12-20 12:04:29 -05002601 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2602 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07002603 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002604 }
2605
bungemanc292b5f2016-12-20 12:04:29 -05002606 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), fontData.get());
bungeman41868fe2015-05-20 09:21:04 -07002607 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2608 // created from a data provider does not appear to have any ownership of the underlying
2609 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002610 UniqueCFRef<CGFontRef> cgVariant;
bungeman41868fe2015-05-20 09:21:04 -07002611 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002612 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungeman41868fe2015-05-20 09:21:04 -07002613 } else {
mtklein18300a32016-03-16 13:53:35 -07002614 cgVariant.reset(cg.release());
bungeman41868fe2015-05-20 09:21:04 -07002615 }
2616
bungemanc292b5f2016-12-20 12:04:29 -05002617 UniqueCFRef<CTFontRef> ct(
2618 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07002619 if (!ct) {
halcanary96fcdcc2015-08-27 07:41:13 -07002620 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002621 }
bungemanc292b5f2016-12-20 12:04:29 -05002622 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungeman41868fe2015-05-20 09:21:04 -07002623 }
2624
mtklein36352bf2015-03-25 18:17:31 -07002625 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002626 UniqueCFRef<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2627 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002628 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002629 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002630 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002631 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002632
bungeman11a77c62016-04-12 13:45:06 -07002633 SkTypeface* onLegacyCreateTypeface(const char familyName[], SkFontStyle style) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002634 if (familyName) {
2635 familyName = map_css_names(familyName);
2636 }
2637
bungemanbea97482016-08-24 08:29:50 -07002638 SkTypeface* face = create_from_name(familyName, style);
bungeman53d5c6e2016-04-08 07:22:29 -07002639 if (face) {
2640 return face;
bungemana4c4a2d2014-10-20 13:33:19 -07002641 }
bungeman53d5c6e2016-04-08 07:22:29 -07002642
bungemanc292b5f2016-12-20 12:04:29 -05002643 static SkTypeface* gDefaultFace;
2644 static SkOnce lookupDefault;
bungeman83f1f442017-02-06 13:21:33 -05002645 static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
bungemanc292b5f2016-12-20 12:04:29 -05002646 lookupDefault([]{
2647 gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle());
2648 });
2649 return SkSafeRef(gDefaultFace);
reed@google.com7fdcd442013-07-30 21:25:49 +00002650 }
reed@google.com95625db2013-03-25 20:44:02 +00002651};
2652
reed@google.com7fdcd442013-07-30 21:25:49 +00002653///////////////////////////////////////////////////////////////////////////////
2654
Ben Wagner3546ff12017-01-03 13:32:36 -05002655sk_sp<SkFontMgr> SkFontMgr::Factory() { return sk_make_sp<SkFontMgr_Mac>(); }
mtklein1ee76512015-11-02 10:20:27 -08002656
2657#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)