blob: d19ac5b6b4d59d98144f2c840e034962f8b4b70e [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"
Cary Clarka4083c92017-09-15 11:59:23 -040025#include "SkColorData.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000026#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() {
Mike Klein70284432017-09-19 13:07:27 -0400209 static bool gSupportsLCD = []{
210 uint32_t rgb = 0;
211 UniqueCFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
212 UniqueCFRef<CGContextRef> cgContext(
213 CGBitmapContextCreate(&rgb, 1, 1, 8, 4, colorspace.get(), BITMAP_INFO_RGB));
214 UniqueCFRef<CTFontRef> ctFont(CTFontCreateWithName(CFSTR("Helvetica"), 16, nullptr));
215 CGContextSetShouldSmoothFonts(cgContext.get(), true);
216 CGContextSetShouldAntialias(cgContext.get(), true);
217 CGContextSetTextDrawingMode(cgContext.get(), kCGTextFill);
218 CGContextSetGrayFillColor(cgContext.get(), 1, 1);
219 CGPoint point = CGPointMake(-1, 0);
220 static const UniChar pipeChar = '|';
221 CGGlyph pipeGlyph;
222 CTFontGetGlyphsForCharacters(ctFont.get(), &pipeChar, &pipeGlyph, 1);
223 CTFontDrawGlyphs(ctFont.get(), &pipeGlyph, &point, 1, cgContext.get());
ccameronf8ee5b42016-10-04 15:02:02 -0700224
Mike Klein70284432017-09-19 13:07:27 -0400225 uint32_t r = (rgb >> 16) & 0xFF;
226 uint32_t g = (rgb >> 8) & 0xFF;
227 uint32_t b = (rgb >> 0) & 0xFF;
228 return (r != g || r != b);
229 }();
230 return gSupportsLCD;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000231}
232
233class Offscreen {
234public:
bungeman34902632014-12-10 21:43:27 -0800235 Offscreen()
halcanary96fcdcc2015-08-27 07:41:13 -0700236 : fRGBSpace(nullptr)
237 , fCG(nullptr)
bungeman34902632014-12-10 21:43:27 -0800238 , fDoAA(false)
239 , fDoLCD(false)
240 {
241 fSize.set(0, 0);
242 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000243
244 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
bungemanc292b5f2016-12-20 12:04:29 -0500245 CGGlyph glyphID, size_t* rowBytesPtr, bool generateA8FromLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000246
247private:
248 enum {
249 kSize = 32 * 32 * sizeof(CGRGBPixel)
250 };
251 SkAutoSMalloc<kSize> fImageStorage;
bungemanc292b5f2016-12-20 12:04:29 -0500252 UniqueCFRef<CGColorSpaceRef> fRGBSpace;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000253
254 // cached state
bungemanc292b5f2016-12-20 12:04:29 -0500255 UniqueCFRef<CGContextRef> fCG;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000256 SkISize fSize;
257 bool fDoAA;
258 bool fDoLCD;
259
260 static int RoundSize(int dimension) {
261 return SkNextPow2(dimension);
262 }
263};
264
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000265///////////////////////////////////////////////////////////////////////////////
266
bungeman6e45bda2016-07-25 15:11:49 -0700267static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
bungemana4c4a2d2014-10-20 13:33:19 -0700268 CFNumberRef num;
269 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
bungemanc292b5f2016-12-20 12:04:29 -0500270 && CFNumberIsFloatType(num)
271 && CFNumberGetValue(num, kCFNumberCGFloatType, value);
bungemana4c4a2d2014-10-20 13:33:19 -0700272}
273
bungeman6e45bda2016-07-25 15:11:49 -0700274template <typename S, typename D, typename C> struct LinearInterpolater {
275 struct Mapping {
276 S src_val;
277 D dst_val;
278 };
279 constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
280 : fMapping(mapping), fMappingCount(mappingCount) {}
281
282 static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
283 SkASSERT(src_min < src_max);
284 SkASSERT(dst_min <= dst_max);
285 return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000286 }
bungeman6e45bda2016-07-25 15:11:49 -0700287
bungemanf1ef1fa2017-02-09 14:23:46 -0500288 D map(S val) const {
bungeman6e45bda2016-07-25 15:11:49 -0700289 // -Inf to [0]
290 if (val < fMapping[0].src_val) {
291 return fMapping[0].dst_val;
292 }
293
294 // Linear from [i] to [i+1]
295 for (int i = 0; i < fMappingCount - 1; ++i) {
296 if (val < fMapping[i+1].src_val) {
297 return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
298 fMapping[i].dst_val, fMapping[i+1].dst_val);
299 }
300 }
301
302 // From [n] to +Inf
303 // if (fcweight < Inf)
304 return fMapping[fMappingCount - 1].dst_val;
305 }
306
307 Mapping const * fMapping;
308 int fMappingCount;
309};
310
311struct RoundCGFloatToInt {
312 int operator()(CGFloat s) { return s + 0.5; }
313};
bungemanf1ef1fa2017-02-09 14:23:46 -0500314struct CGFloatIdentity {
315 CGFloat operator()(CGFloat s) { return s; }
316};
317
bungeman3e306f62017-03-29 11:32:02 -0400318/** Returns the [-1, 1] CTFontDescriptor weights for the
319 * <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
320 *
321 * It is assumed that the values will be interpolated linearly between these points.
322 * NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
323 * The actual values appear to be stable, but they may change in the future without notice.
324 */
325static CGFloat(&get_NSFontWeight_mapping())[11] {
326
327 // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
328#ifdef SK_BUILD_FOR_MAC
329# define SK_KIT_FONT_WEIGHT_PREFIX "NS"
330#endif
331#ifdef SK_BUILD_FOR_IOS
332# define SK_KIT_FONT_WEIGHT_PREFIX "UI"
333#endif
334 static constexpr struct {
335 CGFloat defaultValue;
336 const char* name;
337 } nsFontWeightLoaderInfos[] = {
338 { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
339 { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
340 { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
341 { 0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
342 { 0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
343 { 0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
344 { 0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
345 { 0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
346 { 0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
347 };
348
349 static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
350 static CGFloat nsFontWeights[11];
351 static SkOnce once;
352 once([&] {
353 size_t i = 0;
354 nsFontWeights[i++] = -1.00;
355 for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
356 void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
357 if (nsFontWeightValuePtr) {
358 nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
359 } else {
360 nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
361 }
362 }
363 nsFontWeights[i++] = 1.00;
364 });
365 return nsFontWeights;
366}
367
bungemanf1ef1fa2017-02-09 14:23:46 -0500368/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
369 *
370 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
371 * CTFont is native or created from a CGDataProvider.
372 */
373static CGFloat fontstyle_to_ct_weight(int fontstyleWeight) {
374 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
375
376 // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
377 // However, on this end we can't tell, so this is ignored.
378
bungeman3e306f62017-03-29 11:32:02 -0400379 static Interpolator::Mapping nativeWeightMappings[11];
380 static SkOnce once;
381 once([&] {
382 CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
383 for (int i = 0; i < 11; ++i) {
384 nativeWeightMappings[i].src_val = i * 100;
385 nativeWeightMappings[i].dst_val = nsFontWeights[i];
386 }
387 });
bungemanf1ef1fa2017-02-09 14:23:46 -0500388 static constexpr Interpolator nativeInterpolator(
389 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
390
391 return nativeInterpolator.map(fontstyleWeight);
392}
393
bungeman6e45bda2016-07-25 15:11:49 -0700394
bungeman2873c762017-01-13 11:40:21 -0500395/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
396 *
397 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
398 * CTFont is native or created from a CGDataProvider.
399 */
400static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
bungeman6e45bda2016-07-25 15:11:49 -0700401 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
402
bungeman6e45bda2016-07-25 15:11:49 -0700403 // 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 -0500404 // However, on this end we can't tell, so this is ignored.
405
406 /** This mapping for CGDataProvider created fonts is determined by creating font data with every
407 * weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
408 * in tests/TypefaceTest.cpp for the code used to determine these values.
409 */
410 static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
bungeman6e45bda2016-07-25 15:11:49 -0700411 { -1.00, 0 },
412 { -0.70, 100 },
413 { -0.50, 200 },
414 { -0.23, 300 },
415 { 0.00, 400 },
416 { 0.20, 500 },
417 { 0.30, 600 },
418 { 0.40, 700 },
419 { 0.60, 800 },
420 { 0.80, 900 },
421 { 1.00, 1000 },
422 };
bungeman2873c762017-01-13 11:40:21 -0500423 static constexpr Interpolator dataProviderInterpolator(
424 dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
425
bungeman3e306f62017-03-29 11:32:02 -0400426 static Interpolator::Mapping nativeWeightMappings[11];
427 static SkOnce once;
428 once([&] {
429 CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
430 for (int i = 0; i < 11; ++i) {
431 nativeWeightMappings[i].src_val = nsFontWeights[i];
432 nativeWeightMappings[i].dst_val = i * 100;
433 }
434 });
bungeman2873c762017-01-13 11:40:21 -0500435 static constexpr Interpolator nativeInterpolator(
436 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
437
438 return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
439 : nativeInterpolator.map(cgWeight);
bungemana4c4a2d2014-10-20 13:33:19 -0700440}
441
bungemanf1ef1fa2017-02-09 14:23:46 -0500442/** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
443static int fontstyle_to_ct_width(int fontstyleWidth) {
444 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
445
446 // Values determined by creating font data with every width, creating a CTFont,
447 // and asking the CTFont for its width. See TypefaceStyle test for basics.
448 static constexpr Interpolator::Mapping widthMappings[] = {
449 { 0, -0.5 },
450 { 10, 0.5 },
451 };
452 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
453 return interpolator.map(fontstyleWidth);
454}
455
456/** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
bungeman6e45bda2016-07-25 15:11:49 -0700457static int ct_width_to_fontstyle(CGFloat cgWidth) {
458 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
459
460 // Values determined by creating font data with every width, creating a CTFont,
461 // and asking the CTFont for its width. See TypefaceStyle test for basics.
462 static constexpr Interpolator::Mapping widthMappings[] = {
463 { -0.5, 0 },
464 { 0.5, 10 },
465 };
bungeman2873c762017-01-13 11:40:21 -0500466 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
467 return interpolator.map(cgWidth);
bungemana4c4a2d2014-10-20 13:33:19 -0700468}
469
bungeman2873c762017-01-13 11:40:21 -0500470static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc, bool fromDataProvider) {
bungemanc292b5f2016-12-20 12:04:29 -0500471 UniqueCFRef<CFTypeRef> fontTraits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
472 if (!fontTraits || CFDictionaryGetTypeID() != CFGetTypeID(fontTraits.get())) {
bungemana4c4a2d2014-10-20 13:33:19 -0700473 return SkFontStyle();
474 }
bungemanc292b5f2016-12-20 12:04:29 -0500475 UniqueCFRef<CFDictionaryRef> fontTraitsDict(static_cast<CFDictionaryRef>(fontTraits.release()));
bungemana4c4a2d2014-10-20 13:33:19 -0700476
bungeman6e45bda2016-07-25 15:11:49 -0700477 CGFloat weight, width, slant;
bungemanc292b5f2016-12-20 12:04:29 -0500478 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800479 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700480 }
bungemanc292b5f2016-12-20 12:04:29 -0500481 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWidthTrait, &width)) {
bungemana4c4a2d2014-10-20 13:33:19 -0700482 width = 0;
483 }
bungemanc292b5f2016-12-20 12:04:29 -0500484 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800485 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700486 }
487
bungeman2873c762017-01-13 11:40:21 -0500488 return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
bungeman6e45bda2016-07-25 15:11:49 -0700489 ct_width_to_fontstyle(width),
bungemana4c4a2d2014-10-20 13:33:19 -0700490 slant ? SkFontStyle::kItalic_Slant
bungemanb4bb7d82016-04-27 10:21:04 -0700491 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000492}
493
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000494class SkTypeface_Mac : public SkTypeface {
495public:
bungemanc292b5f2016-12-20 12:04:29 -0500496 SkTypeface_Mac(UniqueCFRef<CTFontRef> fontRef, UniqueCFRef<CFTypeRef> resourceRef,
bungeman78884012015-06-08 13:39:12 -0700497 const SkFontStyle& fs, bool isFixedPitch,
bungeman53d5c6e2016-04-08 07:22:29 -0700498 bool isLocalStream)
bungemane3aea102016-07-13 05:16:58 -0700499 : SkTypeface(fs, isFixedPitch)
bungemanc292b5f2016-12-20 12:04:29 -0500500 , fFontRef(std::move(fontRef))
501 , fOriginatingCFTypeRef(std::move(resourceRef))
502 , fHasColorGlyphs(
503 SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
caseq26337e92014-06-30 12:14:52 -0700504 , fIsLocalStream(isLocalStream)
reed@google.comce8b3de2013-03-26 19:30:16 +0000505 {
bungemanc292b5f2016-12-20 12:04:29 -0500506 SkASSERT(fFontRef);
reed@google.comce8b3de2013-03-26 19:30:16 +0000507 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000508
bungemanc292b5f2016-12-20 12:04:29 -0500509 UniqueCFRef<CTFontRef> fFontRef;
510 UniqueCFRef<CFTypeRef> fOriginatingCFTypeRef;
bungemane3bea5c2015-04-07 07:34:36 -0700511 const bool fHasColorGlyphs;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000512
513protected:
mtklein36352bf2015-03-25 18:17:31 -0700514 int onGetUPEM() const override;
515 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
bungemanf93d7112016-09-16 06:24:20 -0700516 std::unique_ptr<SkFontData> onMakeFontData() const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500517 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
518 int coordinateCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700519 void onGetFamilyName(SkString* familyName) const override;
520 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
521 int onGetTableTags(SkFontTableTag tags[]) const override;
bungemanc292b5f2016-12-20 12:04:29 -0500522 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
reeda9322c22016-04-12 06:47:05 -0700523 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
524 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700525 void onFilterRec(SkScalerContextRec*) const override;
526 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
Hal Canary209e4b12017-05-04 14:23:55 -0400527 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
bungemanc292b5f2016-12-20 12:04:29 -0500528 int onCharsToGlyphs(const void* chars, Encoding,
529 uint16_t glyphs[], int glyphCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700530 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000531
Mike Reed342a9fa2017-05-03 15:48:22 -0400532 void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
533
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000534private:
caseq26337e92014-06-30 12:14:52 -0700535 bool fIsLocalStream;
reed@google.comce8b3de2013-03-26 19:30:16 +0000536
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000537 typedef SkTypeface INHERITED;
538};
539
bungeman82a455f2016-04-14 08:04:45 -0700540static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
bungeman64fb51d2015-05-04 12:03:50 -0700541 CTFontRef self = (CTFontRef)context;
Mike Reed342a9fa2017-05-03 15:48:22 -0400542 CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000543
bungeman64fb51d2015-05-04 12:03:50 -0700544 return CFEqual(self, other);
545}
546
bungemanc292b5f2016-12-20 12:04:29 -0500547/** Creates a typeface, searching the cache if isLocalStream is false. */
Mike Reed59227392017-09-26 09:46:08 -0400548static sk_sp<SkTypeface> create_from_CTFontRef(UniqueCFRef<CTFontRef> font,
549 UniqueCFRef<CFTypeRef> resource,
550 bool isLocalStream) {
bungemanc292b5f2016-12-20 12:04:29 -0500551 SkASSERT(font);
bungemanbea97482016-08-24 08:29:50 -0700552
553 if (!isLocalStream) {
554 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)font.get());
555 if (face) {
Mike Reed59227392017-09-26 09:46:08 -0400556 return sk_sp<SkTypeface>(face);
bungemanbea97482016-08-24 08:29:50 -0700557 }
558 }
559
bungemanc292b5f2016-12-20 12:04:29 -0500560 UniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
bungeman2873c762017-01-13 11:40:21 -0500561 SkFontStyle style = fontstyle_from_descriptor(desc.get(), isLocalStream);
bungemanc292b5f2016-12-20 12:04:29 -0500562 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
bungemanbea97482016-08-24 08:29:50 -0700563 bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
564
bungemanc292b5f2016-12-20 12:04:29 -0500565 SkTypeface* face = new SkTypeface_Mac(std::move(font), std::move(resource),
bungemanbea97482016-08-24 08:29:50 -0700566 style, isFixedPitch, isLocalStream);
567 if (!isLocalStream) {
568 SkTypefaceCache::Add(face);
569 }
Mike Reed59227392017-09-26 09:46:08 -0400570 return sk_sp<SkTypeface>(face);
bungemanbea97482016-08-24 08:29:50 -0700571}
572
573/** Creates a typeface from a descriptor, searching the cache. */
Mike Reed59227392017-09-26 09:46:08 -0400574static sk_sp<SkTypeface> create_from_desc(CTFontDescriptorRef desc) {
bungemanc292b5f2016-12-20 12:04:29 -0500575 UniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -0700576 if (!ctFont) {
577 return nullptr;
578 }
579
bungemanc292b5f2016-12-20 12:04:29 -0500580 return create_from_CTFontRef(std::move(ctFont), nullptr, false);
bungemanbea97482016-08-24 08:29:50 -0700581}
582
bungemanc292b5f2016-12-20 12:04:29 -0500583static UniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
584 const SkFontStyle& style) {
bungemanc292b5f2016-12-20 12:04:29 -0500585 UniqueCFRef<CFMutableDictionaryRef> cfAttributes(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000586 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
587 &kCFTypeDictionaryKeyCallBacks,
588 &kCFTypeDictionaryValueCallBacks));
589
bungemanc292b5f2016-12-20 12:04:29 -0500590 UniqueCFRef<CFMutableDictionaryRef> cfTraits(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000591 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
592 &kCFTypeDictionaryKeyCallBacks,
593 &kCFTypeDictionaryValueCallBacks));
594
bungeman83f1f442017-02-06 13:21:33 -0500595 if (!cfAttributes || !cfTraits) {
halcanary96fcdcc2015-08-27 07:41:13 -0700596 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000597 }
598
bungemanf1ef1fa2017-02-09 14:23:46 -0500599 // CTFontTraits (symbolic)
bungeman83f1f442017-02-06 13:21:33 -0500600 CTFontSymbolicTraits ctFontTraits = 0;
601 if (style.weight() >= SkFontStyle::kBold_Weight) {
602 ctFontTraits |= kCTFontBoldTrait;
603 }
604 if (style.slant() != SkFontStyle::kUpright_Slant) {
605 ctFontTraits |= kCTFontItalicTrait;
606 }
607 UniqueCFRef<CFNumberRef> cfFontTraits(
608 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
609 if (cfFontTraits) {
610 CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get());
bungeman83f1f442017-02-06 13:21:33 -0500611 }
bungemanf1ef1fa2017-02-09 14:23:46 -0500612 // CTFontTraits (weight)
613 CGFloat ctWeight = fontstyle_to_ct_weight(style.weight());
614 UniqueCFRef<CFNumberRef> cfFontWeight(
615 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
616 if (cfFontWeight) {
617 CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
618 }
619 // CTFontTraits (width)
620 CGFloat ctWidth = fontstyle_to_ct_width(style.weight());
621 UniqueCFRef<CFNumberRef> cfFontWidth(
622 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
623 if (cfFontWidth) {
624 CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
625 }
626 // CTFontTraits (slant)
627 CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1;
628 UniqueCFRef<CFNumberRef> cfFontSlant(
629 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
630 if (cfFontSlant) {
631 CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
632 }
633 // CTFontTraits
634 CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
bungeman83f1f442017-02-06 13:21:33 -0500635
636 // CTFontFamilyName
637 if (familyName) {
638 UniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
639 if (cfFontName) {
640 CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
641 }
642 }
bungeman64fb51d2015-05-04 12:03:50 -0700643
bungemanc292b5f2016-12-20 12:04:29 -0500644 return UniqueCFRef<CTFontDescriptorRef>(
645 CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
bungemanbea97482016-08-24 08:29:50 -0700646}
647
648/** Creates a typeface from a name, searching the cache. */
Mike Reed59227392017-09-26 09:46:08 -0400649static sk_sp<SkTypeface> create_from_name(const char familyName[], const SkFontStyle& style) {
bungemanc292b5f2016-12-20 12:04:29 -0500650 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
bungemanbea97482016-08-24 08:29:50 -0700651 if (!desc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700652 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700653 }
bungemanc292b5f2016-12-20 12:04:29 -0500654 return create_from_desc(desc.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000655}
656
657///////////////////////////////////////////////////////////////////////////////
658
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000659/* This function is visible on the outside. It first searches the cache, and if
660 * not found, returns a new entry (after adding it to the cache).
661 */
bungemanc292b5f2016-12-20 12:04:29 -0500662SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef font, CFTypeRef resource) {
663 CFRetain(font);
664 if (resource) {
665 CFRetain(resource);
bungeman53d5c6e2016-04-08 07:22:29 -0700666 }
bungemanc292b5f2016-12-20 12:04:29 -0500667 return create_from_CTFontRef(UniqueCFRef<CTFontRef>(font),
668 UniqueCFRef<CFTypeRef>(resource),
Mike Reed59227392017-09-26 09:46:08 -0400669 false).release();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000670}
671
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000672static const char* map_css_names(const char* name) {
673 static const struct {
674 const char* fFrom; // name the caller specified
675 const char* fTo; // "canonical" name we map to
676 } gPairs[] = {
677 { "sans-serif", "Helvetica" },
678 { "serif", "Times" },
679 { "monospace", "Courier" }
680 };
681
682 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
683 if (strcmp(name, gPairs[i].fFrom) == 0) {
684 return gPairs[i].fTo;
685 }
686 }
687 return name; // no change
688}
689
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000690///////////////////////////////////////////////////////////////////////////////
691
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000692class SkScalerContext_Mac : public SkScalerContext {
693public:
bungeman7cfd46a2016-10-20 16:06:52 -0400694 SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000695
696protected:
mtklein36352bf2015-03-25 18:17:31 -0700697 unsigned generateGlyphCount(void) override;
698 uint16_t generateCharToGlyph(SkUnichar uni) override;
699 void generateAdvance(SkGlyph* glyph) override;
700 void generateMetrics(SkGlyph* glyph) override;
701 void generateImage(const SkGlyph& glyph) override;
Ben Wagner6e9ac122016-11-11 14:31:06 -0500702 void generatePath(SkGlyphID glyph, SkPath* path) override;
mtklein36352bf2015-03-25 18:17:31 -0700703 void generateFontMetrics(SkPaint::FontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000704
705private:
706 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000707
bungeman@google.comcefd9812013-05-15 15:07:32 +0000708 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
709 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000710
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000711 Offscreen fOffscreen;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000712
bungeman3b4b66c2015-01-08 08:33:44 -0800713 /** Unrotated variant of fCTFont.
714 *
715 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
716 * advances, but always sets the height to 0. This font is used to get the advances of the
717 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000718 *
719 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
bungeman18ec7b92017-02-09 17:15:59 -0500720 * This makes kCTFontOrientationDefault dangerous, because the metrics from
721 * kCTFontOrientationHorizontal are in a different space from kCTFontOrientationVertical.
722 * With kCTFontOrientationVertical the advances must be unrotated.
bungemanef27ce32015-10-29 09:30:32 -0700723 *
724 * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
725 * different underlying font data. As a result, avoid ever creating more than one CTFont per
726 * SkScalerContext to ensure that only one CTFont is used.
727 *
728 * As a result of the above (and other constraints) this font contains the size, but not the
729 * transform. The transform must always be applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000730 */
bungemanc292b5f2016-12-20 12:04:29 -0500731 UniqueCFRef<CTFontRef> fCTFont;
bungemanef27ce32015-10-29 09:30:32 -0700732
733 /** The transform without the font size. */
734 CGAffineTransform fTransform;
735 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000736
bungemanc292b5f2016-12-20 12:04:29 -0500737 UniqueCFRef<CGFontRef> fCGFont;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000738 uint16_t fGlyphCount;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000739 const bool fDoSubPosition;
740 const bool fVertical;
741
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000742 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000743
744 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000745};
746
bungeman7cbeaae2015-09-22 09:54:56 -0700747// CTFontCreateCopyWithAttributes or CTFontCreateCopyWithSymbolicTraits cannot be used on 10.10
bungeman05846312015-09-23 12:51:28 -0700748// and later, as they will return different underlying fonts depending on the size requested.
749// It is not possible to use descriptors with CTFontCreateWithFontDescriptor, since that does not
750// work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
751// CGFont.
bungemanc292b5f2016-12-20 12:04:29 -0500752static UniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
753 const CGAffineTransform* transform)
bungeman7cbeaae2015-09-22 09:54:56 -0700754{
bungemanc292b5f2016-12-20 12:04:29 -0500755 UniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700756
bungeman05846312015-09-23 12:51:28 -0700757 // The last parameter (CTFontDescriptorRef attributes) *must* be nullptr.
758 // If non-nullptr then with fonts with variation axes, the copy will fail in
759 // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
760 // which it quite obviously is not.
bungeman7cbeaae2015-09-22 09:54:56 -0700761
bungeman05846312015-09-23 12:51:28 -0700762 // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
763 // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
764 // the scaler context, since they aren't 'normal'.
bungemanc292b5f2016-12-20 12:04:29 -0500765 return UniqueCFRef<CTFontRef>(
766 CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, transform, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700767}
768
bungeman7cfd46a2016-10-20 16:06:52 -0400769SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
reeda9322c22016-04-12 06:47:05 -0700770 const SkScalerContextEffects& effects,
reed@google.com0da48612013-03-19 16:06:52 +0000771 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400772 : INHERITED(std::move(typeface), effects, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000773 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
774 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
775
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000776{
reedd0f41732015-07-10 12:08:38 -0700777 AUTO_CG_LOCK();
778
Mike Reed342a9fa2017-05-03 15:48:22 -0400779 CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000780 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000781 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
782 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000783
bungeman34902632014-12-10 21:43:27 -0800784 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
785 // As a result, it is necessary to know the actual device size and request that.
786 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800787 SkMatrix skTransform;
bungemane55131c2016-08-24 12:01:31 -0700788 bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
bungeman4ec46aa2017-02-15 17:49:12 -0500789 &scale, &skTransform, nullptr, nullptr, nullptr);
bungemanaae30912015-03-02 13:43:26 -0800790 fTransform = MatrixToCGAffineTransform(skTransform);
bungeman1f0e78d2016-08-23 13:19:01 -0700791 // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
792 // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
bungemane55131c2016-08-24 12:01:31 -0700793 if (invertible) {
bungeman1f0e78d2016-08-23 13:19:01 -0700794 fInvTransform = CGAffineTransformInvert(fTransform);
795 } else {
796 fInvTransform = fTransform;
797 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000798
bungemanbe2284d2014-11-25 08:08:09 -0800799 // The transform contains everything except the requested text size.
800 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -0800801 CGFloat textSize = ScalarToCG(scale.y());
bungemanc292b5f2016-12-20 12:04:29 -0500802 fCTFont = ctfont_create_exact_copy(ctFont, textSize, nullptr);
803 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000804}
805
806CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
807 CGGlyph glyphID, size_t* rowBytesPtr,
mtkleinbbd40182015-09-08 08:19:33 -0700808 bool generateA8FromLCD) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000809 if (!fRGBSpace) {
810 //It doesn't appear to matter what color space is specified.
811 //Regular blends and antialiased text are always (s*a + d*(1-a))
812 //and smoothed text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -0700813 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000814 }
815
816 // default to kBW_Format
817 bool doAA = false;
818 bool doLCD = false;
819
820 if (SkMask::kBW_Format != glyph.fMaskFormat) {
821 doLCD = true;
822 doAA = true;
823 }
824
825 // FIXME: lcd smoothed un-hinted rasterization unsupported.
826 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
827 doLCD = false;
828 doAA = true;
829 }
830
bungeman34902632014-12-10 21:43:27 -0800831 // If this font might have color glyphs, disable LCD as there's no way to support it.
832 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
bungemane280d062016-03-24 11:27:05 -0700833 // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
bungeman34902632014-12-10 21:43:27 -0800834 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
835 doLCD = false;
836 }
837
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000838 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
839 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
840 if (fSize.fWidth < glyph.fWidth) {
841 fSize.fWidth = RoundSize(glyph.fWidth);
842 }
843 if (fSize.fHeight < glyph.fHeight) {
844 fSize.fHeight = RoundSize(glyph.fHeight);
845 }
846
847 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
848 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -0800849 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
850 ? kCGImageAlphaPremultipliedFirst
851 : kCGImageAlphaNoneSkipFirst;
852 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -0700853 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungemanc292b5f2016-12-20 12:04:29 -0500854 rowBytes, fRGBSpace.get(), bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000855
bungeman34902632014-12-10 21:43:27 -0800856 // Skia handles quantization and subpixel positioning,
857 // so disable quantization and enabe subpixel positioning in CG.
bungemanc292b5f2016-12-20 12:04:29 -0500858 CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
859 CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000860
bungeman@google.comcefd9812013-05-15 15:07:32 +0000861 // Because CG always draws from the horizontal baseline,
862 // if there is a non-integral translation from the horizontal origin to the vertical origin,
863 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungemanc292b5f2016-12-20 12:04:29 -0500864 CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
865 CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
bungeman34902632014-12-10 21:43:27 -0800866
bungemanc292b5f2016-12-20 12:04:29 -0500867 CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000868
bungemane280d062016-03-24 11:27:05 -0700869 // Draw black on white to create mask. (Special path exists to speed this up in CG.)
bungemanc292b5f2016-12-20 12:04:29 -0500870 CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000871
872 // force our checks below to happen
873 fDoAA = !doAA;
874 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -0800875
bungemanc292b5f2016-12-20 12:04:29 -0500876 CGContextSetTextMatrix(fCG.get(), context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000877 }
878
879 if (fDoAA != doAA) {
bungemanc292b5f2016-12-20 12:04:29 -0500880 CGContextSetShouldAntialias(fCG.get(), doAA);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000881 fDoAA = doAA;
882 }
883 if (fDoLCD != doLCD) {
bungemanc292b5f2016-12-20 12:04:29 -0500884 CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000885 fDoLCD = doLCD;
886 }
887
888 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
889 // skip rows based on the glyph's height
890 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
891
bungemane280d062016-03-24 11:27:05 -0700892 // Erase to white (or transparent black if it's a color glyph, to not composite against white).
893 uint32_t bgColor = (SkMask::kARGB32_Format != glyph.fMaskFormat) ? 0xFFFFFFFF : 0x00000000;
894 sk_memset_rect32(image, bgColor, glyph.fWidth, glyph.fHeight, rowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000895
896 float subX = 0;
897 float subY = 0;
898 if (context.fDoSubPosition) {
899 subX = SkFixedToFloat(glyph.getSubXFixed());
900 subY = SkFixedToFloat(glyph.getSubYFixed());
901 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000902
bungeman34902632014-12-10 21:43:27 -0800903 // CoreText and CoreGraphics always draw using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000904 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000905 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000906 context.getVerticalOffset(glyphID, &offset);
907 subX += offset.fX;
908 subY += offset.fY;
909 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000910
bungeman34902632014-12-10 21:43:27 -0800911 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -0800912 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
913 // 'positions' which are in text space. The glyph location (in device space) must be
914 // mapped into text space, so that CG can convert it back into device space.
Hal Canary55325b72017-01-03 10:36:17 -0500915 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
bungeman1b5f25c2015-06-08 14:32:24 -0700916 //
bungemanaae30912015-03-02 13:43:26 -0800917 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
918 // So always make the font transform identity and place the transform on the context.
919 point = CGPointApplyAffineTransform(point, context.fInvTransform);
920
bungemanc292b5f2016-12-20 12:04:29 -0500921 CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000922
923 SkASSERT(rowBytesPtr);
924 *rowBytesPtr = rowBytes;
925 return image;
926}
927
bungeman@google.comcefd9812013-05-15 15:07:32 +0000928void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
bungemanc2ba75b2016-12-14 16:53:00 -0500929 // CTFontGetVerticalTranslationsForGlyphs produces cgVertOffset in CG units (pixels, y up).
bungeman@google.comcefd9812013-05-15 15:07:32 +0000930 CGSize cgVertOffset;
bungemanc292b5f2016-12-20 12:04:29 -0500931 CTFontGetVerticalTranslationsForGlyphs(fCTFont.get(), &glyphID, &cgVertOffset, 1);
bungemanef27ce32015-10-29 09:30:32 -0700932 cgVertOffset = CGSizeApplyAffineTransform(cgVertOffset, fTransform);
933 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
934 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
935 skVertOffset.fY = -skVertOffset.fY;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000936 *offset = skVertOffset;
937}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000938
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000939unsigned SkScalerContext_Mac::generateGlyphCount(void) {
940 return fGlyphCount;
941}
942
943uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
reedd0f41732015-07-10 12:08:38 -0700944 AUTO_CG_LOCK();
945
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000946 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000947 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000948
949 // Get the glyph
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000950 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar);
951 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000952
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000953 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
954 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
955 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
bungemanc292b5f2016-12-20 12:04:29 -0500956 CTFontGetGlyphsForCharacters(fCTFont.get(), theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000957 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000958}
959
960void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
961 this->generateMetrics(glyph);
962}
963
bungeman@google.comcefd9812013-05-15 15:07:32 +0000964void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
reedd0f41732015-07-10 12:08:38 -0700965 AUTO_CG_LOCK();
966
djsollen1b277042014-08-06 06:58:06 -0700967 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +0000968 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000969
bungeman@google.comcefd9812013-05-15 15:07:32 +0000970 // The following block produces cgAdvance in CG units (pixels, y up).
971 CGSize cgAdvance;
972 if (fVertical) {
bungeman18ec7b92017-02-09 17:15:59 -0500973 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationVertical,
bungeman@google.comcefd9812013-05-15 15:07:32 +0000974 &cgGlyph, &cgAdvance, 1);
bungeman3b4b66c2015-01-08 08:33:44 -0800975 // Vertical advances are returned as widths instead of heights.
976 SkTSwap(cgAdvance.height, cgAdvance.width);
977 cgAdvance.height = -cgAdvance.height;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000978 } else {
bungeman18ec7b92017-02-09 17:15:59 -0500979 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
bungeman@google.comcefd9812013-05-15 15:07:32 +0000980 &cgGlyph, &cgAdvance, 1);
981 }
bungemanef27ce32015-10-29 09:30:32 -0700982 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700983 glyph->fAdvanceX = CGToFloat(cgAdvance.width);
984 glyph->fAdvanceY = -CGToFloat(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000985
bungeman@google.comcefd9812013-05-15 15:07:32 +0000986 // The following produces skBounds in SkGlyph units (pixels, y down),
987 // or returns early if skBounds would be empty.
988 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000989
bungemanc2ba75b2016-12-14 16:53:00 -0500990 // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
991 // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
992 // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
993 // glyph is vertical. This avoids any diagreement between the various means of retrieving
994 // vertical metrics.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000995 {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000996 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
997 CGRect cgBounds;
bungeman18ec7b92017-02-09 17:15:59 -0500998 CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
bungeman@google.comcefd9812013-05-15 15:07:32 +0000999 &cgGlyph, &cgBounds, 1);
bungemanef27ce32015-10-29 09:30:32 -07001000 cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001001
bungeman@google.comcefd9812013-05-15 15:07:32 +00001002 // BUG?
1003 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1004 // it should be empty. So, if we see a zero-advance, we check if it has an
1005 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1006 // is rare, so we won't incur a big performance cost for this extra check.
1007 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
bungemanc292b5f2016-12-20 12:04:29 -05001008 UniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, nullptr));
1009 if (!path || CGPathIsEmpty(path.get())) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001010 return;
1011 }
1012 }
1013
1014 if (CGRectIsEmpty_inline(cgBounds)) {
1015 return;
1016 }
1017
1018 // Convert cgBounds to SkGlyph units (pixels, y down).
1019 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1020 cgBounds.size.width, cgBounds.size.height);
1021 }
1022
1023 if (fVertical) {
bungemanc2ba75b2016-12-14 16:53:00 -05001024 // Due to possible vertical bounds bugs and simplicity, skBounds is the horizontal bounds.
bungeman@google.comcefd9812013-05-15 15:07:32 +00001025 // Convert these horizontal bounds into vertical bounds.
1026 SkPoint offset;
bungemanc2ba75b2016-12-14 16:53:00 -05001027 this->getVerticalOffset(cgGlyph, &offset);
bungeman@google.comcefd9812013-05-15 15:07:32 +00001028 skBounds.offset(offset);
1029 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001030
bungeman@google.comcefd9812013-05-15 15:07:32 +00001031 // Currently the bounds are based on being rendered at (0,0).
1032 // The top left must not move, since that is the base from which subpixel positioning is offset.
1033 if (fDoSubPosition) {
1034 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1035 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1036 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001037
bungeman@google.comcefd9812013-05-15 15:07:32 +00001038 SkIRect skIBounds;
1039 skBounds.roundOut(&skIBounds);
1040 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1041 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1042 // is not currently known, as CG dilates the outlines by some percentage.
1043 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1044 skIBounds.outset(1, 1);
1045 glyph->fLeft = SkToS16(skIBounds.fLeft);
1046 glyph->fTop = SkToS16(skIBounds.fTop);
1047 glyph->fWidth = SkToU16(skIBounds.width());
1048 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001049}
1050
Cary Clarka4083c92017-09-15 11:59:23 -04001051#include "SkColorData.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001052
bungemane280d062016-03-24 11:27:05 -07001053static void build_power_table(uint8_t table[]) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001054 for (int i = 0; i < 256; i++) {
1055 float x = i / 255.f;
bungemane280d062016-03-24 11:27:05 -07001056 int xx = SkScalarRoundToInt(x * x * 255);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001057 table[i] = SkToU8(xx);
1058 }
1059}
1060
1061/**
1062 * This will invert the gamma applied by CoreGraphics, so we can get linear
1063 * values.
1064 *
1065 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1066 * The color space used does not appear to affect this choice.
1067 */
1068static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1069 static bool gInited;
1070 static uint8_t gTableCoreGraphicsSmoothing[256];
1071 if (!gInited) {
bungemane280d062016-03-24 11:27:05 -07001072 build_power_table(gTableCoreGraphicsSmoothing);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001073 gInited = true;
1074 }
1075 return gTableCoreGraphicsSmoothing;
1076}
1077
1078static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1079 while (count > 0) {
1080 uint8_t mask = 0;
1081 for (int i = 7; i >= 0; --i) {
bungemane280d062016-03-24 11:27:05 -07001082 mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001083 if (0 == --count) {
1084 break;
1085 }
1086 }
1087 *dst++ = mask;
1088 }
1089}
1090
1091template<bool APPLY_PREBLEND>
1092static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
bungemane280d062016-03-24 11:27:05 -07001093 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
1094 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
1095 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
bungeman3b4b66c2015-01-08 08:33:44 -08001096 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1097#if SK_SHOW_TEXT_BLIT_COVERAGE
1098 lum = SkTMax(lum, (U8CPU)0x30);
1099#endif
1100 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001101}
1102template<bool APPLY_PREBLEND>
1103static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1104 const SkGlyph& glyph, const uint8_t* table8) {
1105 const int width = glyph.fWidth;
1106 size_t dstRB = glyph.rowBytes();
1107 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1108
1109 for (int y = 0; y < glyph.fHeight; y++) {
1110 for (int i = 0; i < width; ++i) {
1111 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1112 }
bungemanc292b5f2016-12-20 12:04:29 -05001113 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1114 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001115 }
1116}
1117
1118template<bool APPLY_PREBLEND>
1119static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1120 const uint8_t* tableG,
1121 const uint8_t* tableB) {
bungemane280d062016-03-24 11:27:05 -07001122 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
1123 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
1124 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001125#if SK_SHOW_TEXT_BLIT_COVERAGE
1126 r = SkTMax(r, (U8CPU)0x30);
1127 g = SkTMax(g, (U8CPU)0x30);
1128 b = SkTMax(b, (U8CPU)0x30);
1129#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001130 return SkPack888ToRGB16(r, g, b);
1131}
1132template<bool APPLY_PREBLEND>
1133static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1134 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1135 const int width = glyph.fWidth;
1136 size_t dstRB = glyph.rowBytes();
1137 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1138
1139 for (int y = 0; y < glyph.fHeight; y++) {
1140 for (int i = 0; i < width; i++) {
1141 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1142 }
bungemanc292b5f2016-12-20 12:04:29 -05001143 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1144 dst = SkTAddOffset<uint16_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001145 }
1146}
1147
bungeman34902632014-12-10 21:43:27 -08001148static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1149 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001150 U8CPU r = (rgb >> 16) & 0xFF;
1151 U8CPU g = (rgb >> 8) & 0xFF;
1152 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001153#if SK_SHOW_TEXT_BLIT_COVERAGE
1154 a = SkTMax(a, (U8CPU)0x30);
1155#endif
bungeman34902632014-12-10 21:43:27 -08001156 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001157}
reed@google.comf77b35d2013-05-02 20:39:44 +00001158
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001159void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
bungemanc292b5f2016-12-20 12:04:29 -05001160 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001161
1162 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1163 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1164
1165 // Draw the glyph
1166 size_t cgRowBytes;
1167 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
halcanary96fcdcc2015-08-27 07:41:13 -07001168 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001169 return;
1170 }
1171
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001172 // Fix the glyph
bungemanc292b5f2016-12-20 12:04:29 -05001173 if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
1174 (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD))
1175 {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001176 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1177
1178 //Note that the following cannot really be integrated into the
1179 //pre-blend, since we may not be applying the pre-blend; when we aren't
1180 //applying the pre-blend it means that a filter wants linear anyway.
1181 //Other code may also be applying the pre-blend, so we'd need another
1182 //one with this and one without.
1183 CGRGBPixel* addr = cgPixels;
1184 for (int y = 0; y < glyph.fHeight; ++y) {
1185 for (int x = 0; x < glyph.fWidth; ++x) {
1186 int r = (addr[x] >> 16) & 0xFF;
1187 int g = (addr[x] >> 8) & 0xFF;
1188 int b = (addr[x] >> 0) & 0xFF;
1189 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1190 }
bungemane280d062016-03-24 11:27:05 -07001191 addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001192 }
1193 }
1194
1195 // Convert glyph to mask
1196 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001197 case SkMask::kLCD16_Format: {
1198 if (fPreBlend.isApplicable()) {
1199 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1200 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1201 } else {
1202 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1203 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1204 }
1205 } break;
1206 case SkMask::kA8_Format: {
1207 if (fPreBlend.isApplicable()) {
1208 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1209 } else {
1210 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1211 }
1212 } break;
1213 case SkMask::kBW_Format: {
1214 const int width = glyph.fWidth;
1215 size_t dstRB = glyph.rowBytes();
1216 uint8_t* dst = (uint8_t*)glyph.fImage;
1217 for (int y = 0; y < glyph.fHeight; y++) {
1218 cgpixels_to_bits(dst, cgPixels, width);
bungemanc292b5f2016-12-20 12:04:29 -05001219 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1220 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001221 }
1222 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001223 case SkMask::kARGB32_Format: {
1224 const int width = glyph.fWidth;
1225 size_t dstRB = glyph.rowBytes();
1226 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1227 for (int y = 0; y < glyph.fHeight; y++) {
1228 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001229 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001230 }
bungemanc292b5f2016-12-20 12:04:29 -05001231 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1232 dst = SkTAddOffset<SkPMColor>(dst, dstRB);
reed@google.comf77b35d2013-05-02 20:39:44 +00001233 }
1234 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001235 default:
1236 SkDEBUGFAIL("unexpected mask format");
1237 break;
1238 }
1239}
1240
1241/*
1242 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1243 * seems sufficient, and possibly even correct, to allow the hinted outline
1244 * to be subpixel positioned.
1245 */
1246#define kScaleForSubPixelPositionHinting (4.0f)
1247
Ben Wagner6e9ac122016-11-11 14:31:06 -05001248void SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001249 AUTO_CG_LOCK();
1250
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001251 SkScalar scaleX = SK_Scalar1;
1252 SkScalar scaleY = SK_Scalar1;
1253
bungemanef27ce32015-10-29 09:30:32 -07001254 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001255 /*
1256 * For subpixel positioning, we want to return an unhinted outline, so it
1257 * can be positioned nicely at fractional offsets. However, we special-case
1258 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1259 * we want to retain hinting in the direction orthogonal to the baseline.
1260 * e.g. for horizontal baseline, we want to retain hinting in Y.
1261 * The way we remove hinting is to scale the font by some value (4) in that
1262 * direction, ask for the path, and then scale the path back down.
1263 */
bungeman7cbeaae2015-09-22 09:54:56 -07001264 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001265 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001266 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001267 // now see if we need to restore hinting for axis-aligned baselines
bungeman27876bc2016-02-29 11:22:55 -08001268 switch (this->computeAxisAlignmentForHText()) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001269 case kX_SkAxisAlignment:
1270 scaleY = SK_Scalar1; // want hinting in the Y direction
1271 break;
1272 case kY_SkAxisAlignment:
1273 scaleX = SK_Scalar1; // want hinting in the X direction
1274 break;
1275 default:
1276 break;
1277 }
1278
bungemanef27ce32015-10-29 09:30:32 -07001279 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1280 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001281 }
1282
Ben Wagner6e9ac122016-11-11 14:31:06 -05001283 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
bungemanc292b5f2016-12-20 12:04:29 -05001284 UniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001285
1286 path->reset();
halcanary96fcdcc2015-08-27 07:41:13 -07001287 if (cgPath != nullptr) {
bungemanc292b5f2016-12-20 12:04:29 -05001288 CGPathApply(cgPath.get(), path, SkScalerContext_Mac::CTPathElement);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001289 }
1290
bungeman@google.comcefd9812013-05-15 15:07:32 +00001291 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001292 SkMatrix m;
1293 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1294 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001295 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001296 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001297 SkPoint offset;
1298 getVerticalOffset(cgGlyph, &offset);
1299 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001300 }
1301}
1302
bungeman41078062014-07-07 08:16:37 -07001303void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001304 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001305 return;
1306 }
1307
reedd0f41732015-07-10 12:08:38 -07001308 AUTO_CG_LOCK();
1309
bungemanc292b5f2016-12-20 12:04:29 -05001310 CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001311
bungeman41078062014-07-07 08:16:37 -07001312 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001313 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont.get()));
1314 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001315 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001316 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001317 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1318 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1319 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001320 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungemanc292b5f2016-12-20 12:04:29 -05001321 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont.get()));
1322 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont.get()));
1323 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont.get()));
1324 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont.get()));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001325
Ben Wagner219f3622017-07-17 15:32:25 -04001326 metrics->fFlags = 0;
Ben Wagner3318da52017-03-23 14:01:22 -04001327 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag;
bungeman41078062014-07-07 08:16:37 -07001328 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman4ec46aa2017-02-15 17:49:12 -05001329
1330 // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
1331 // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
1332 // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
1333 // table are read, but then overwritten if the font is not a system font. As a result, if there
1334 // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
1335 struct OS2HeightMetrics {
1336 SK_OT_SHORT sxHeight;
1337 SK_OT_SHORT sCapHeight;
1338 } heights;
1339 size_t bytesRead = this->getTypeface()->getTableData(
1340 SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
1341 sizeof(heights), &heights);
1342 if (bytesRead == sizeof(heights)) {
1343 // 'fontSize' is correct because the entire resolved size is set by the constructor.
1344 CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
1345 unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
1346 unsigned maxSaneHeight = upem * 2;
1347 uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
1348 if (xHeight && xHeight < maxSaneHeight) {
1349 metrics->fXHeight = CGToScalar(xHeight * fontSize / upem);
1350 }
1351 uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
1352 if (capHeight && capHeight < maxSaneHeight) {
1353 metrics->fCapHeight = CGToScalar(capHeight * fontSize / upem);
1354 }
1355 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001356}
1357
1358void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1359 SkPath* skPath = (SkPath*)info;
1360
1361 // Process the path element
1362 switch (element->type) {
1363 case kCGPathElementMoveToPoint:
1364 skPath->moveTo(element->points[0].x, -element->points[0].y);
1365 break;
1366
1367 case kCGPathElementAddLineToPoint:
1368 skPath->lineTo(element->points[0].x, -element->points[0].y);
1369 break;
1370
1371 case kCGPathElementAddQuadCurveToPoint:
1372 skPath->quadTo(element->points[0].x, -element->points[0].y,
1373 element->points[1].x, -element->points[1].y);
1374 break;
1375
1376 case kCGPathElementAddCurveToPoint:
1377 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1378 element->points[1].x, -element->points[1].y,
1379 element->points[2].x, -element->points[2].y);
1380 break;
1381
1382 case kCGPathElementCloseSubpath:
1383 skPath->close();
1384 break;
1385
1386 default:
1387 SkDEBUGFAIL("Unknown path element!");
1388 break;
1389 }
1390}
1391
1392
1393///////////////////////////////////////////////////////////////////////////////
1394
halcanary96fcdcc2015-08-27 07:41:13 -07001395// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001396// Call must still manage its ownership of provider
Mike Reed59227392017-09-26 09:46:08 -04001397static sk_sp<SkTypeface> create_from_dataProvider(UniqueCFRef<CGDataProviderRef> provider,
1398 int ttcIndex) {
Ben Wagnerfc497342017-02-24 11:15:26 -05001399 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
Mike Reed59227392017-09-26 09:46:08 -04002266 return create_from_desc(desc).release();
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 }
Mike Reed59227392017-09-26 09:46:08 -04002273 return create_from_desc(findMatchingDesc(pattern)).release();
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);
Mike Reed59227392017-09-26 09:46:08 -04002369 return create_from_desc(desc.get()).release();
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));
Mike Reed59227392017-09-26 09:46:08 -04002392 return create_from_CTFontRef(std::move(fallbackFont), nullptr, false).release();
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
Mike Reed59227392017-09-26 09:46:08 -04002400 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
2401 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromData(std::move(data)));
bungemanc292b5f2016-12-20 12:04:29 -05002402 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
Mike Reed59227392017-09-26 09:46:08 -04002408 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2409 int ttcIndex) const override {
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 }
Mike Reed59227392017-09-26 09:46:08 -04002505 sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> s,
2506 const SkFontArguments& args) const override {
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 }
Mike Reed59227392017-09-26 09:46:08 -04002592 sk_sp<SkTypeface> onMakeFromFontData(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
Mike Reed59227392017-09-26 09:46:08 -04002625 sk_sp<SkTypeface> onMakeFromFile(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
Mike Reed59227392017-09-26 09:46:08 -04002633 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002634 if (familyName) {
2635 familyName = map_css_names(familyName);
2636 }
2637
Mike Reed59227392017-09-26 09:46:08 -04002638 sk_sp<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([]{
Mike Reed59227392017-09-26 09:46:08 -04002647 gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
bungemanc292b5f2016-12-20 12:04:29 -05002648 });
Mike Reed59227392017-09-26 09:46:08 -04002649 return sk_ref_sp(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)