blob: ae46664ff61111ee0050c55cfbd2e6fb0ff70352 [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
Mike Kleind94e00c2017-10-05 11:40:57 -04001038 // We're trying to pack left and top into int16_t,
1039 // and width and height into uint16_t, after outsetting by 1.
1040 if (!SkRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(skBounds)) {
1041 return;
1042 }
1043
bungeman@google.comcefd9812013-05-15 15:07:32 +00001044 SkIRect skIBounds;
1045 skBounds.roundOut(&skIBounds);
1046 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1047 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1048 // is not currently known, as CG dilates the outlines by some percentage.
1049 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1050 skIBounds.outset(1, 1);
1051 glyph->fLeft = SkToS16(skIBounds.fLeft);
1052 glyph->fTop = SkToS16(skIBounds.fTop);
1053 glyph->fWidth = SkToU16(skIBounds.width());
1054 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001055}
1056
Cary Clarka4083c92017-09-15 11:59:23 -04001057#include "SkColorData.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001058
Ben Wagner7a0248f2017-10-18 11:30:56 -04001059static constexpr uint8_t sk_pow2_table(size_t i) {
1060 return SkToU8(((i * i + 128) / 255));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001061}
1062
1063/**
1064 * This will invert the gamma applied by CoreGraphics, so we can get linear
1065 * values.
1066 *
1067 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1068 * The color space used does not appear to affect this choice.
1069 */
Ben Wagner7a0248f2017-10-18 11:30:56 -04001070static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001071
1072static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1073 while (count > 0) {
1074 uint8_t mask = 0;
1075 for (int i = 7; i >= 0; --i) {
bungemane280d062016-03-24 11:27:05 -07001076 mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001077 if (0 == --count) {
1078 break;
1079 }
1080 }
1081 *dst++ = mask;
1082 }
1083}
1084
1085template<bool APPLY_PREBLEND>
1086static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
bungemane280d062016-03-24 11:27:05 -07001087 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
1088 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
1089 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
bungeman3b4b66c2015-01-08 08:33:44 -08001090 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1091#if SK_SHOW_TEXT_BLIT_COVERAGE
1092 lum = SkTMax(lum, (U8CPU)0x30);
1093#endif
1094 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001095}
1096template<bool APPLY_PREBLEND>
1097static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1098 const SkGlyph& glyph, const uint8_t* table8) {
1099 const int width = glyph.fWidth;
1100 size_t dstRB = glyph.rowBytes();
1101 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1102
1103 for (int y = 0; y < glyph.fHeight; y++) {
1104 for (int i = 0; i < width; ++i) {
1105 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1106 }
bungemanc292b5f2016-12-20 12:04:29 -05001107 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1108 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001109 }
1110}
1111
1112template<bool APPLY_PREBLEND>
1113static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1114 const uint8_t* tableG,
1115 const uint8_t* tableB) {
bungemane280d062016-03-24 11:27:05 -07001116 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
1117 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
1118 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001119#if SK_SHOW_TEXT_BLIT_COVERAGE
1120 r = SkTMax(r, (U8CPU)0x30);
1121 g = SkTMax(g, (U8CPU)0x30);
1122 b = SkTMax(b, (U8CPU)0x30);
1123#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001124 return SkPack888ToRGB16(r, g, b);
1125}
1126template<bool APPLY_PREBLEND>
1127static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1128 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1129 const int width = glyph.fWidth;
1130 size_t dstRB = glyph.rowBytes();
1131 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1132
1133 for (int y = 0; y < glyph.fHeight; y++) {
1134 for (int i = 0; i < width; i++) {
1135 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1136 }
bungemanc292b5f2016-12-20 12:04:29 -05001137 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1138 dst = SkTAddOffset<uint16_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001139 }
1140}
1141
bungeman34902632014-12-10 21:43:27 -08001142static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1143 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001144 U8CPU r = (rgb >> 16) & 0xFF;
1145 U8CPU g = (rgb >> 8) & 0xFF;
1146 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001147#if SK_SHOW_TEXT_BLIT_COVERAGE
1148 a = SkTMax(a, (U8CPU)0x30);
1149#endif
bungeman34902632014-12-10 21:43:27 -08001150 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001151}
reed@google.comf77b35d2013-05-02 20:39:44 +00001152
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001153void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
bungemanc292b5f2016-12-20 12:04:29 -05001154 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001155
1156 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1157 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1158
1159 // Draw the glyph
1160 size_t cgRowBytes;
1161 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
halcanary96fcdcc2015-08-27 07:41:13 -07001162 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001163 return;
1164 }
1165
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001166 // Fix the glyph
bungemanc292b5f2016-12-20 12:04:29 -05001167 if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
1168 (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD))
1169 {
Ben Wagner7a0248f2017-10-18 11:30:56 -04001170 const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001171
1172 //Note that the following cannot really be integrated into the
1173 //pre-blend, since we may not be applying the pre-blend; when we aren't
1174 //applying the pre-blend it means that a filter wants linear anyway.
1175 //Other code may also be applying the pre-blend, so we'd need another
1176 //one with this and one without.
1177 CGRGBPixel* addr = cgPixels;
1178 for (int y = 0; y < glyph.fHeight; ++y) {
1179 for (int x = 0; x < glyph.fWidth; ++x) {
1180 int r = (addr[x] >> 16) & 0xFF;
1181 int g = (addr[x] >> 8) & 0xFF;
1182 int b = (addr[x] >> 0) & 0xFF;
Ben Wagner7a0248f2017-10-18 11:30:56 -04001183 addr[x] = (linear[r] << 16) | (linear[g] << 8) | linear[b];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001184 }
bungemane280d062016-03-24 11:27:05 -07001185 addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001186 }
1187 }
1188
1189 // Convert glyph to mask
1190 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001191 case SkMask::kLCD16_Format: {
1192 if (fPreBlend.isApplicable()) {
1193 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1194 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1195 } else {
1196 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1197 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1198 }
1199 } break;
1200 case SkMask::kA8_Format: {
1201 if (fPreBlend.isApplicable()) {
1202 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1203 } else {
1204 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1205 }
1206 } break;
1207 case SkMask::kBW_Format: {
1208 const int width = glyph.fWidth;
1209 size_t dstRB = glyph.rowBytes();
1210 uint8_t* dst = (uint8_t*)glyph.fImage;
1211 for (int y = 0; y < glyph.fHeight; y++) {
1212 cgpixels_to_bits(dst, cgPixels, width);
bungemanc292b5f2016-12-20 12:04:29 -05001213 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1214 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001215 }
1216 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001217 case SkMask::kARGB32_Format: {
1218 const int width = glyph.fWidth;
1219 size_t dstRB = glyph.rowBytes();
1220 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1221 for (int y = 0; y < glyph.fHeight; y++) {
1222 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001223 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001224 }
bungemanc292b5f2016-12-20 12:04:29 -05001225 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1226 dst = SkTAddOffset<SkPMColor>(dst, dstRB);
reed@google.comf77b35d2013-05-02 20:39:44 +00001227 }
1228 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001229 default:
1230 SkDEBUGFAIL("unexpected mask format");
1231 break;
1232 }
1233}
1234
1235/*
1236 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1237 * seems sufficient, and possibly even correct, to allow the hinted outline
1238 * to be subpixel positioned.
1239 */
1240#define kScaleForSubPixelPositionHinting (4.0f)
1241
Ben Wagner6e9ac122016-11-11 14:31:06 -05001242void SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001243 AUTO_CG_LOCK();
1244
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001245 SkScalar scaleX = SK_Scalar1;
1246 SkScalar scaleY = SK_Scalar1;
1247
bungemanef27ce32015-10-29 09:30:32 -07001248 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001249 /*
1250 * For subpixel positioning, we want to return an unhinted outline, so it
1251 * can be positioned nicely at fractional offsets. However, we special-case
1252 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1253 * we want to retain hinting in the direction orthogonal to the baseline.
1254 * e.g. for horizontal baseline, we want to retain hinting in Y.
1255 * The way we remove hinting is to scale the font by some value (4) in that
1256 * direction, ask for the path, and then scale the path back down.
1257 */
bungeman7cbeaae2015-09-22 09:54:56 -07001258 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001259 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001260 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001261 // now see if we need to restore hinting for axis-aligned baselines
bungeman27876bc2016-02-29 11:22:55 -08001262 switch (this->computeAxisAlignmentForHText()) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001263 case kX_SkAxisAlignment:
1264 scaleY = SK_Scalar1; // want hinting in the Y direction
1265 break;
1266 case kY_SkAxisAlignment:
1267 scaleX = SK_Scalar1; // want hinting in the X direction
1268 break;
1269 default:
1270 break;
1271 }
1272
bungemanef27ce32015-10-29 09:30:32 -07001273 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1274 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001275 }
1276
Ben Wagner6e9ac122016-11-11 14:31:06 -05001277 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
bungemanc292b5f2016-12-20 12:04:29 -05001278 UniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001279
1280 path->reset();
halcanary96fcdcc2015-08-27 07:41:13 -07001281 if (cgPath != nullptr) {
bungemanc292b5f2016-12-20 12:04:29 -05001282 CGPathApply(cgPath.get(), path, SkScalerContext_Mac::CTPathElement);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001283 }
1284
bungeman@google.comcefd9812013-05-15 15:07:32 +00001285 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001286 SkMatrix m;
1287 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1288 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001289 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001290 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001291 SkPoint offset;
1292 getVerticalOffset(cgGlyph, &offset);
1293 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001294 }
1295}
1296
bungeman41078062014-07-07 08:16:37 -07001297void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001298 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001299 return;
1300 }
1301
reedd0f41732015-07-10 12:08:38 -07001302 AUTO_CG_LOCK();
1303
bungemanc292b5f2016-12-20 12:04:29 -05001304 CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001305
bungeman41078062014-07-07 08:16:37 -07001306 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001307 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont.get()));
1308 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001309 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001310 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001311 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1312 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1313 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001314 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungemanc292b5f2016-12-20 12:04:29 -05001315 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont.get()));
1316 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont.get()));
1317 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont.get()));
1318 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont.get()));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001319
Ben Wagner219f3622017-07-17 15:32:25 -04001320 metrics->fFlags = 0;
Ben Wagner3318da52017-03-23 14:01:22 -04001321 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag;
bungeman41078062014-07-07 08:16:37 -07001322 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman4ec46aa2017-02-15 17:49:12 -05001323
1324 // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
1325 // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
1326 // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
1327 // table are read, but then overwritten if the font is not a system font. As a result, if there
1328 // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
1329 struct OS2HeightMetrics {
1330 SK_OT_SHORT sxHeight;
1331 SK_OT_SHORT sCapHeight;
1332 } heights;
1333 size_t bytesRead = this->getTypeface()->getTableData(
1334 SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
1335 sizeof(heights), &heights);
1336 if (bytesRead == sizeof(heights)) {
1337 // 'fontSize' is correct because the entire resolved size is set by the constructor.
1338 CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
1339 unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
1340 unsigned maxSaneHeight = upem * 2;
1341 uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
1342 if (xHeight && xHeight < maxSaneHeight) {
1343 metrics->fXHeight = CGToScalar(xHeight * fontSize / upem);
1344 }
1345 uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
1346 if (capHeight && capHeight < maxSaneHeight) {
1347 metrics->fCapHeight = CGToScalar(capHeight * fontSize / upem);
1348 }
1349 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001350}
1351
1352void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1353 SkPath* skPath = (SkPath*)info;
1354
1355 // Process the path element
1356 switch (element->type) {
1357 case kCGPathElementMoveToPoint:
1358 skPath->moveTo(element->points[0].x, -element->points[0].y);
1359 break;
1360
1361 case kCGPathElementAddLineToPoint:
1362 skPath->lineTo(element->points[0].x, -element->points[0].y);
1363 break;
1364
1365 case kCGPathElementAddQuadCurveToPoint:
1366 skPath->quadTo(element->points[0].x, -element->points[0].y,
1367 element->points[1].x, -element->points[1].y);
1368 break;
1369
1370 case kCGPathElementAddCurveToPoint:
1371 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1372 element->points[1].x, -element->points[1].y,
1373 element->points[2].x, -element->points[2].y);
1374 break;
1375
1376 case kCGPathElementCloseSubpath:
1377 skPath->close();
1378 break;
1379
1380 default:
1381 SkDEBUGFAIL("Unknown path element!");
1382 break;
1383 }
1384}
1385
1386
1387///////////////////////////////////////////////////////////////////////////////
1388
halcanary96fcdcc2015-08-27 07:41:13 -07001389// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001390// Call must still manage its ownership of provider
Mike Reed59227392017-09-26 09:46:08 -04001391static sk_sp<SkTypeface> create_from_dataProvider(UniqueCFRef<CGDataProviderRef> provider,
1392 int ttcIndex) {
Ben Wagnerfc497342017-02-24 11:15:26 -05001393 if (ttcIndex != 0) {
1394 return nullptr;
1395 }
bungemanc292b5f2016-12-20 12:04:29 -05001396 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
1397 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07001398 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001399 }
bungemanc292b5f2016-12-20 12:04:29 -05001400 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg.get(), 0, nullptr, nullptr));
1401 if (!ct) {
1402 return nullptr;
1403 }
1404 return create_from_CTFontRef(std::move(ct), nullptr, true);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001405}
1406
bungemanc292b5f2016-12-20 12:04:29 -05001407// Web fonts added to the CTFont registry do not return their character set.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001408// Iterate through the font in this case. The existing caller caches the result,
1409// so the performance impact isn't too bad.
1410static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1411 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001412 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001413 SkUnichar* out = glyphToUnicode->begin();
1414 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1415 UniChar unichar = 0;
1416 while (glyphCount > 0) {
1417 CGGlyph glyph;
1418 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
Hal Canary266dcb02017-04-07 11:49:20 -04001419 if (out[glyph] != 0) {
1420 out[glyph] = unichar;
1421 --glyphCount;
1422 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001423 }
1424 if (++unichar == 0) {
1425 break;
1426 }
1427 }
1428}
1429
1430// Construct Glyph to Unicode table.
1431// Unicode code points that require conjugate pairs in utf16 are not
1432// supported.
1433static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1434 SkTDArray<SkUnichar>* glyphToUnicode) {
bungemanc292b5f2016-12-20 12:04:29 -05001435 UniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001436 if (!charSet) {
1437 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1438 return;
1439 }
1440
bungemanc292b5f2016-12-20 12:04:29 -05001441 UniqueCFRef<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001442 if (!bitmap) {
1443 return;
1444 }
bungemanc292b5f2016-12-20 12:04:29 -05001445 CFIndex length = CFDataGetLength(bitmap.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001446 if (!length) {
1447 return;
1448 }
1449 if (length > 8192) {
1450 // TODO: Add support for Unicode above 0xFFFF
1451 // Consider only the BMP portion of the Unicode character points.
1452 // The bitmap may contain other planes, up to plane 16.
1453 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1454 length = 8192;
1455 }
bungemanc292b5f2016-12-20 12:04:29 -05001456 const UInt8* bits = CFDataGetBytePtr(bitmap.get());
reed@google.com7fa2a652014-01-27 13:42:58 +00001457 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001458 SkUnichar* out = glyphToUnicode->begin();
1459 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1460 for (int i = 0; i < length; i++) {
1461 int mask = bits[i];
1462 if (!mask) {
1463 continue;
1464 }
1465 for (int j = 0; j < 8; j++) {
1466 CGGlyph glyph;
1467 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1468 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1469 out[glyph] = unichar;
1470 }
1471 }
1472 }
1473}
1474
halcanary96fcdcc2015-08-27 07:41:13 -07001475/** Assumes src and dst are not nullptr. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001476static void CFStringToSkString(CFStringRef src, SkString* dst) {
1477 // Reserve enough room for the worst-case string,
1478 // plus 1 byte for the trailing null.
1479 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1480 kCFStringEncodingUTF8) + 1;
1481 dst->resize(length);
1482 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1483 // Resize to the actual UTF-8 length used, stripping the null character.
1484 dst->resize(strlen(dst->c_str()));
1485}
1486
Hal Canary209e4b12017-05-04 14:23:55 -04001487std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
reed@google.com2689f612013-03-20 20:01:47 +00001488
reedd0f41732015-07-10 12:08:38 -07001489 AUTO_CG_LOCK();
1490
bungemanc292b5f2016-12-20 12:04:29 -05001491 UniqueCFRef<CTFontRef> ctFont =
1492 ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()), nullptr);
bungeman7cbeaae2015-09-22 09:54:56 -07001493
Hal Canary209e4b12017-05-04 14:23:55 -04001494 std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001495
1496 {
bungemanc292b5f2016-12-20 12:04:29 -05001497 UniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
bungeman256b3512014-07-02 07:57:59 -07001498 if (fontName.get()) {
bungemanc292b5f2016-12-20 12:04:29 -05001499 CFStringToSkString(fontName.get(), &info->fFontName);
bungeman256b3512014-07-02 07:57:59 -07001500 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001501 }
1502
Ben Wagnerc6639052017-03-02 13:31:16 -05001503 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1504 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1505 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1506 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
1507 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont.get(), nullptr));
1508 if (cgFont) {
1509 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
1510 if (cgAxes && CFArrayGetCount(cgAxes.get()) > 0) {
1511 info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
1512 }
1513 }
1514
bungemanc292b5f2016-12-20 12:04:29 -05001515 CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001516
Hal Canary209e4b12017-05-04 14:23:55 -04001517 populate_glyph_to_unicode(ctFont.get(), glyphCount, &info->fGlyphToUnicode);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001518
Hal Canary4a851ca2017-11-09 11:09:34 -05001519 SkOTTableOS2_V4::Type fsType;
1520 if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
1521 offsetof(SkOTTableOS2_V4, fsType),
1522 sizeof(fsType),
1523 &fsType)) {
Hal Canarya865d252017-11-09 16:16:09 -05001524 SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
Hal Canary4a851ca2017-11-09 11:09:34 -05001525 }
1526
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001527 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1528 // fonts always have both glyf and loca tables. At the least, this is what
1529 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1530 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001531 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001532 return info;
1533 }
1534
1535 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
bungemanc292b5f2016-12-20 12:04:29 -05001536 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001537 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1538 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1539 }
1540 if (symbolicTraits & kCTFontItalicTrait) {
1541 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1542 }
1543 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001544 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1545 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1546 } else if (stylisticClass & kCTFontScriptsClass) {
1547 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1548 }
bungemanc292b5f2016-12-20 12:04:29 -05001549 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
1550 info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
1551 info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
1552 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
1553 CGRect bbox = CTFontGetBoundingBox(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001554
1555 SkRect r;
1556 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1557 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1558 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1559 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1560
1561 r.roundOut(&(info->fBBox));
1562
1563 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1564 // This probably isn't very good with an italic font.
1565 int16_t min_width = SHRT_MAX;
1566 info->fStemV = 0;
1567 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1568 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1569 CGGlyph glyphs[count];
1570 CGRect boundingRects[count];
bungemanc292b5f2016-12-20 12:04:29 -05001571 if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
bungeman18ec7b92017-02-09 17:15:59 -05001572 CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001573 glyphs, boundingRects, count);
1574 for (size_t i = 0; i < count; i++) {
1575 int16_t width = (int16_t) boundingRects[i].size.width;
1576 if (width > 0 && width < min_width) {
1577 min_width = width;
1578 info->fStemV = min_width;
1579 }
1580 }
1581 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001582 return info;
1583}
1584
1585///////////////////////////////////////////////////////////////////////////////
1586
reed@google.comcc9aad52013-03-21 19:28:10 +00001587static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1588 CTFontRef ctFont = typeface->fFontRef.get();
bungemanc292b5f2016-12-20 12:04:29 -05001589 UniqueCFRef<CFNumberRef> fontFormatRef(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001590 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1591 if (!fontFormatRef) {
1592 return 0;
1593 }
1594
1595 SInt32 fontFormatValue;
bungemanc292b5f2016-12-20 12:04:29 -05001596 if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001597 return 0;
1598 }
1599
1600 switch (fontFormatValue) {
1601 case kCTFontFormatOpenTypePostScript:
1602 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1603 case kCTFontFormatOpenTypeTrueType:
1604 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1605 case kCTFontFormatTrueType:
1606 return SkSFNTHeader::fontType_MacTrueType::TAG;
1607 case kCTFontFormatPostScript:
1608 return SkSFNTHeader::fontType_PostScript::TAG;
1609 case kCTFontFormatBitmap:
1610 return SkSFNTHeader::fontType_MacTrueType::TAG;
1611 case kCTFontFormatUnrecognized:
1612 default:
1613 //CT seems to be unreliable in being able to obtain the type,
1614 //even if all we want is the first four bytes of the font resource.
1615 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1616 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1617 }
1618}
1619
bungeman5f213d92015-01-27 05:39:10 -08001620SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
reed@google.comcc9aad52013-03-21 19:28:10 +00001621 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001622 if (0 == fontType) {
halcanary96fcdcc2015-08-27 07:41:13 -07001623 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001624 }
1625
1626 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001627 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001628 SkTDArray<SkFontTableTag> tableTags;
1629 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001630 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001631
1632 // calc total size for font, save sizes
1633 SkTDArray<size_t> tableSizes;
1634 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1635 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001636 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001637 totalSize += (tableSize + 3) & ~3;
1638 *tableSizes.append() = tableSize;
1639 }
1640
1641 // reserve memory for stream, and zero it (tables must be zero padded)
1642 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1643 char* dataStart = (char*)stream->getMemoryBase();
1644 sk_bzero(dataStart, totalSize);
1645 char* dataPtr = dataStart;
1646
1647 // compute font header entries
1648 uint16_t entrySelector = 0;
1649 uint16_t searchRange = 1;
1650 while (searchRange < numTables >> 1) {
1651 entrySelector++;
1652 searchRange <<= 1;
1653 }
1654 searchRange <<= 4;
1655 uint16_t rangeShift = (numTables << 4) - searchRange;
1656
1657 // write font header
1658 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1659 header->fontType = fontType;
1660 header->numTables = SkEndian_SwapBE16(numTables);
1661 header->searchRange = SkEndian_SwapBE16(searchRange);
1662 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1663 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1664 dataPtr += sizeof(SkSFNTHeader);
1665
1666 // write tables
1667 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1668 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1669 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1670 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001671 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001672 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1673 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1674 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001675 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1676 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001677
1678 dataPtr += (tableSize + 3) & ~3;
1679 ++entry;
1680 }
1681
bungemanb3310c22015-03-02 09:05:36 -08001682 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001683 return stream;
1684}
1685
bungeman41868fe2015-05-20 09:21:04 -07001686struct NonDefaultAxesContext {
1687 SkFixed* axisValue;
1688 CFArrayRef cgAxes;
1689};
1690static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1691 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1692
1693 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1694 return;
1695 }
1696
1697 // The key is a CFString which is a string from the 'name' table.
1698 // Search the cgAxes for an axis with this name, and use its index to store the value.
1699 CFIndex keyIndex = -1;
1700 CFStringRef keyString = static_cast<CFStringRef>(key);
1701 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1702 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1703 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1704 continue;
1705 }
1706
1707 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1708 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1709 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1710 continue;
1711 }
1712 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1713 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1714 keyIndex = i;
1715 break;
1716 }
1717 }
1718 if (keyIndex == -1) {
1719 return;
1720 }
1721
1722 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1723 double valueDouble;
1724 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1725 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1726 {
1727 return;
1728 }
1729 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1730}
Ben Wagnerfc497342017-02-24 11:15:26 -05001731static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount,
bungeman41868fe2015-05-20 09:21:04 -07001732 SkAutoSTMalloc<4, SkFixed>* axisValues)
1733{
Ben Wagnerfc497342017-02-24 11:15:26 -05001734 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1735 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1736 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1737 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
1738 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05001739 if (!cgFont) {
bungeman41868fe2015-05-20 09:21:04 -07001740 return false;
1741 }
1742
bungemanc292b5f2016-12-20 12:04:29 -05001743 UniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
1744 // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
1745 if (!cgVariations) {
1746 return false;
1747 }
1748
1749 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
1750 if (!cgAxes) {
1751 return false;
1752 }
1753 *cgAxisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07001754 axisValues->reset(*cgAxisCount);
1755
1756 // Set all of the axes to their default values.
1757 // Fail if any default value cannot be determined.
1758 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001759 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07001760 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1761 return false;
1762 }
1763
1764 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1765 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1766 kCGFontVariationAxisDefaultValue);
1767 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1768 return false;
1769 }
1770 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
1771 double axisDefaultValueDouble;
1772 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
1773 {
1774 return false;
1775 }
1776 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
1777 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
1778 {
1779 return false;
1780 }
1781 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
1782 }
1783
1784 // Override the default values with the given font's stated axis values.
1785 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
bungemanc292b5f2016-12-20 12:04:29 -05001786 CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
bungeman41868fe2015-05-20 09:21:04 -07001787
1788 return true;
1789}
bungemanf93d7112016-09-16 06:24:20 -07001790std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
bungeman41868fe2015-05-20 09:21:04 -07001791 int index;
bungemanf93d7112016-09-16 06:24:20 -07001792 std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
bungeman41868fe2015-05-20 09:21:04 -07001793
1794 CFIndex cgAxisCount;
1795 SkAutoSTMalloc<4, SkFixed> axisValues;
bungemanc292b5f2016-12-20 12:04:29 -05001796 if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
bungemanf93d7112016-09-16 06:24:20 -07001797 return skstd::make_unique<SkFontData>(std::move(stream), index,
1798 axisValues.get(), cgAxisCount);
bungeman41868fe2015-05-20 09:21:04 -07001799 }
bungemanf93d7112016-09-16 06:24:20 -07001800 return skstd::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
bungeman41868fe2015-05-20 09:21:04 -07001801}
1802
Ben Wagnerfc497342017-02-24 11:15:26 -05001803/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */
1804static UniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
1805 CFArrayRef ctAxes) {
1806
1807 UniqueCFRef<CFMutableDictionaryRef> ctVariations(
1808 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1809 &kCFTypeDictionaryKeyCallBacks,
1810 &kCFTypeDictionaryValueCallBacks));
1811
1812 CFIndex axisCount = CFArrayGetCount(ctAxes);
1813 for (CFIndex i = 0; i < axisCount; ++i) {
1814 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i);
1815 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
1816 return nullptr;
1817 }
1818 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
1819
1820 // The assumption is that values produced by kCTFontVariationAxisNameKey and
1821 // kCGFontVariationAxisName will always be equal.
1822 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
1823 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
1824 return nullptr;
1825 }
1826
1827 CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName);
1828 if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) {
1829 return nullptr;
1830 }
1831
1832 CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1833 if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
1834 return nullptr;
1835 }
1836
1837 CFDictionaryAddValue(ctVariations.get(), axisTag, axisValue);
1838 }
1839 return std::move(ctVariations);
1840}
1841
1842int SkTypeface_Mac::onGetVariationDesignPosition(
1843 SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
1844{
1845 // The CGFont variation data does not contain the tag.
1846
bungemanbc096bf2017-04-25 13:32:50 -04001847 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
1848 // 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 -05001849 UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
1850 if (!ctAxes) {
1851 return -1;
1852 }
1853 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
1854 if (!coordinates || coordinateCount < axisCount) {
1855 return axisCount;
1856 }
1857
1858 // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
1859 // When this happens, try converting the CG variation to a CT variation.
1860 // On 10.12 and later, this only returns non-default variations.
1861 UniqueCFRef<CFDictionaryRef> ctVariations(CTFontCopyVariation(fFontRef.get()));
1862 if (!ctVariations) {
1863 // When 10.11 and earlier are no longer supported, the following code can be replaced with
1864 // return -1 and ct_variation_from_cg_variation can be removed.
1865 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
1866 if (!cgFont) {
1867 return -1;
1868 }
1869 UniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
1870 if (!cgVariations) {
1871 return -1;
1872 }
1873 ctVariations = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
1874 if (!ctVariations) {
1875 return -1;
1876 }
1877 }
1878
1879 for (int i = 0; i < axisCount; ++i) {
1880 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
1881 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
1882 return -1;
1883 }
1884 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
1885
1886 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1887 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
1888 return -1;
1889 }
1890 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
1891 int64_t tagLong;
1892 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
1893 return -1;
1894 }
1895 coordinates[i].axis = tagLong;
1896
1897 CGFloat variationCGFloat;
1898 CFTypeRef variationValue = CFDictionaryGetValue(ctVariations.get(), tagNumber);
1899 if (variationValue) {
1900 if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
1901 return -1;
1902 }
1903 CFNumberRef variationNumber = static_cast<CFNumberRef>(variationValue);
1904 if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) {
1905 return -1;
1906 }
1907 } else {
1908 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
1909 if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) {
1910 return -1;
1911 }
1912 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
1913 if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) {
1914 return -1;
1915 }
1916 }
1917 coordinates[i].value = CGToScalar(variationCGFloat);
1918
1919 }
1920 return axisCount;
1921}
1922
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001923///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001924///////////////////////////////////////////////////////////////////////////////
1925
1926int SkTypeface_Mac::onGetUPEM() const {
bungemanc292b5f2016-12-20 12:04:29 -05001927 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
1928 return CGFontGetUnitsPerEm(cgFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001929}
1930
bungeman@google.com839702b2013-08-07 17:09:22 +00001931SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001932 SkTypeface::LocalizedStrings* nameIter =
bungemanc292b5f2016-12-20 12:04:29 -05001933 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07001934 if (nullptr == nameIter) {
bungemanc292b5f2016-12-20 12:04:29 -05001935 CFStringRef cfLanguageRaw;
1936 UniqueCFRef<CFStringRef> cfFamilyName(
1937 CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
1938 UniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
bungeman@google.coma9802692013-08-07 02:45:25 +00001939
1940 SkString skLanguage;
1941 SkString skFamilyName;
bungemanc292b5f2016-12-20 12:04:29 -05001942 if (cfLanguage) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001943 CFStringToSkString(cfLanguage.get(), &skLanguage);
1944 } else {
1945 skLanguage = "und"; //undetermined
1946 }
bungemanc292b5f2016-12-20 12:04:29 -05001947 if (cfFamilyName) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001948 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1949 }
1950
1951 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1952 }
1953 return nameIter;
1954}
1955
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001956int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
bungemanc292b5f2016-12-20 12:04:29 -05001957 UniqueCFRef<CFArrayRef> cfArray(
1958 CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
1959 if (!cfArray) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001960 return 0;
1961 }
bungemanc292b5f2016-12-20 12:04:29 -05001962 int count = SkToInt(CFArrayGetCount(cfArray.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001963 if (tags) {
1964 for (int i = 0; i < count; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001965 uintptr_t fontTag = reinterpret_cast<uintptr_t>(
1966 CFArrayGetValueAtIndex(cfArray.get(), i));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001967 tags[i] = static_cast<SkFontTableTag>(fontTag);
1968 }
1969 }
1970 return count;
1971}
1972
bungemanc292b5f2016-12-20 12:04:29 -05001973// If, as is the case with web fonts, the CTFont data isn't available,
1974// the CGFont data may work. While the CGFont may always provide the
1975// right result, leave the CTFont code path to minimize disruption.
1976static UniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
1977 UniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1978 kCTFontTableOptionNoOptions));
1979 if (!data) {
1980 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
1981 data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
1982 }
1983 return data;
1984}
1985
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001986size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1987 size_t length, void* dstData) const {
bungemanc292b5f2016-12-20 12:04:29 -05001988 UniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
1989 if (!srcData) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001990 return 0;
1991 }
1992
bungemanc292b5f2016-12-20 12:04:29 -05001993 size_t srcSize = CFDataGetLength(srcData.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001994 if (offset >= srcSize) {
1995 return 0;
1996 }
1997 if (length > srcSize - offset) {
1998 length = srcSize - offset;
1999 }
2000 if (dstData) {
bungemanc292b5f2016-12-20 12:04:29 -05002001 memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002002 }
2003 return length;
2004}
2005
reeda9322c22016-04-12 06:47:05 -07002006SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
2007 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04002008 return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002009}
2010
2011void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002012 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2013 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2014 {
2015 rec->fMaskFormat = SkMask::kA8_Format;
2016 // Render the glyphs as close as possible to what was requested.
2017 // The above turns off subpixel rendering, but the user requested it.
2018 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
2019 // See comments below for more details.
2020 rec->setHinting(SkPaint::kNormal_Hinting);
2021 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00002022
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002023 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00002024 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002025 SkScalerContext::kLCD_BGROrder_Flag |
2026 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002027
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002028 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002029
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002030 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002031
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002032 // Only two levels of hinting are supported.
2033 // kNo_Hinting means avoid CoreGraphics outline dilation.
2034 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
2035 // If there is no lcd support, hinting (dilation) cannot be supported.
2036 SkPaint::Hinting hinting = rec->getHinting();
2037 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
2038 hinting = SkPaint::kNo_Hinting;
2039 } else if (SkPaint::kFull_Hinting == hinting) {
2040 hinting = SkPaint::kNormal_Hinting;
2041 }
2042 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002043
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002044 // FIXME: lcd smoothed un-hinted rasterization unsupported.
2045 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
2046 // There is no current means to honor a request for unhinted lcd,
2047 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002048
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002049 // Hinting and smoothing should be orthogonal, but currently they are not.
2050 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
2051 // output is drawn from auto-dilated outlines (the amount of which is
2052 // determined by AppleFontSmoothing). Its regular anti-aliased output is
2053 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002054
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002055 // The behavior of Skia is as follows:
2056 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
2057 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
2058 // channel. This matches [LCD][yes-hint] in weight.
2059 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
2060 // Currenly side with LCD, effectively ignoring the hinting setting.
2061 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002062
bungemanc292b5f2016-12-20 12:04:29 -05002063 if (rec->fMaskFormat == SkMask::kLCD16_Format) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002064 if (lcdSupport) {
2065 //CoreGraphics creates 555 masks for smoothed text anyway.
2066 rec->fMaskFormat = SkMask::kLCD16_Format;
2067 rec->setHinting(SkPaint::kNormal_Hinting);
2068 } else {
2069 rec->fMaskFormat = SkMask::kA8_Format;
2070 }
2071 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002072
bungeman34902632014-12-10 21:43:27 -08002073 // CoreText provides no information as to whether a glyph will be color or not.
2074 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
2075 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08002076 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08002077 rec->fMaskFormat = SkMask::kARGB32_Format;
2078 }
2079
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002080 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2081 // All other masks can use regular gamma.
2082 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
2083#ifndef SK_GAMMA_APPLY_TO_A8
brianosmana1e8f8d2016-04-08 06:47:54 -07002084 // SRGBTODO: Is this correct? Do we want contrast boost?
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002085 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002086#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002087 } else {
2088 //CoreGraphics dialates smoothed text as needed.
2089 rec->setContrast(0);
2090 }
2091}
2092
bungemanc292b5f2016-12-20 12:04:29 -05002093/** Takes ownership of the CFStringRef. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002094static const char* get_str(CFStringRef ref, SkString* str) {
halcanary96fcdcc2015-08-27 07:41:13 -07002095 if (nullptr == ref) {
2096 return nullptr;
bungeman256b3512014-07-02 07:57:59 -07002097 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002098 CFStringToSkString(ref, str);
bungemanc292b5f2016-12-20 12:04:29 -05002099 CFRelease(ref);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002100 return str->c_str();
2101}
2102
bungemanb374d6a2014-09-17 07:48:59 -07002103void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
bungemanc292b5f2016-12-20 12:04:29 -05002104 get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
bungemanb374d6a2014-09-17 07:48:59 -07002105}
2106
reed@google.com5526ede2013-03-25 13:03:37 +00002107void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2108 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002109 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002110
bungemanc292b5f2016-12-20 12:04:29 -05002111 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
2112 desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
2113 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
bungemanb8113782016-07-25 16:54:59 -07002114 desc->setStyle(this->fontStyle());
caseq26337e92014-06-30 12:14:52 -07002115 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002116}
reed@google.com5526ede2013-03-25 13:03:37 +00002117
reed@google.combcb42ae2013-07-02 13:56:39 +00002118int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002119 uint16_t glyphs[], int glyphCount) const
2120{
2121 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
2122 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
2123 // 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 +00002124
reed@google.combcb42ae2013-07-02 13:56:39 +00002125 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002126 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
2127 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00002128 switch (encoding) {
2129 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002130 const char* utf8 = reinterpret_cast<const char*>(chars);
2131 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2132 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00002133 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002134 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
2135 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00002136 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002137 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002138 break;
2139 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002140 case kUTF16_Encoding: {
2141 src = reinterpret_cast<const UniChar*>(chars);
2142 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00002143 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002144 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2145 ++extra;
2146 }
reed@google.combcb42ae2013-07-02 13:56:39 +00002147 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002148 srcCount = glyphCount + extra;
2149 break;
2150 }
2151 case kUTF32_Encoding: {
2152 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
2153 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2154 src = utf16;
2155 for (int i = 0; i < glyphCount; ++i) {
2156 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
2157 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002158 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002159 break;
2160 }
2161 }
2162
halcanary96fcdcc2015-08-27 07:41:13 -07002163 // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002164 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002165 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2166 uint16_t* macGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002167 if (nullptr == macGlyphs || srcCount > glyphCount) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002168 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002169 }
2170
bungemanc292b5f2016-12-20 12:04:29 -05002171 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002172
2173 // If there were any non-bmp, then copy and compact.
halcanary96fcdcc2015-08-27 07:41:13 -07002174 // If 'glyphs' is nullptr, then compact glyphStorage in-place.
2175 // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
2176 // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002177 uint16_t* compactedGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002178 if (nullptr == compactedGlyphs) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002179 compactedGlyphs = macGlyphs;
2180 }
2181 if (srcCount > glyphCount) {
2182 int extra = 0;
2183 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002184 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002185 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2186 ++extra;
2187 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002188 }
2189 }
2190
2191 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002192 return glyphCount;
2193 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002194
2195 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002196 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002197 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002198 return i;
2199 }
2200 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002201 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002202 return glyphCount;
2203}
2204
2205int SkTypeface_Mac::onCountGlyphs() const {
bungemanc292b5f2016-12-20 12:04:29 -05002206 return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
reed@google.combcb42ae2013-07-02 13:56:39 +00002207}
2208
reed@google.com95625db2013-03-25 20:44:02 +00002209///////////////////////////////////////////////////////////////////////////////
2210///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002211
2212static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
bungemanc292b5f2016-12-20 12:04:29 -05002213 UniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
2214 if (!ref) {
reed@google.com83787c52013-03-26 17:19:15 +00002215 return false;
2216 }
bungemanc292b5f2016-12-20 12:04:29 -05002217 CFStringToSkString(ref.get(), value);
reed@google.com83787c52013-03-26 17:19:15 +00002218 return true;
2219}
2220
reed@google.com95625db2013-03-25 20:44:02 +00002221#include "SkFontMgr.h"
2222
reed@google.com964988f2013-03-29 14:57:22 +00002223static inline int sqr(int value) {
2224 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2225 return value * value;
2226}
2227
2228// We normalize each axis (weight, width, italic) to be base-900
2229static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2230 return sqr(a.weight() - b.weight()) +
2231 sqr((a.width() - b.width()) * 100) +
bungemanb4bb7d82016-04-27 10:21:04 -07002232 sqr((a.slant() != b.slant()) * 900);
reed@google.com964988f2013-03-29 14:57:22 +00002233}
2234
reed@google.com83787c52013-03-26 17:19:15 +00002235class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002236public:
bungeman53d5c6e2016-04-08 07:22:29 -07002237 SkFontStyleSet_Mac(CTFontDescriptorRef desc)
halcanary96fcdcc2015-08-27 07:41:13 -07002238 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
bungemanc292b5f2016-12-20 12:04:29 -05002239 , fCount(0)
2240 {
2241 if (!fArray) {
2242 fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
reed@google.comdea7ee02013-03-28 14:12:10 +00002243 }
bungemanc292b5f2016-12-20 12:04:29 -05002244 fCount = SkToInt(CFArrayGetCount(fArray.get()));
reed@google.com83787c52013-03-26 17:19:15 +00002245 }
2246
mtklein36352bf2015-03-25 18:17:31 -07002247 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002248 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002249 }
2250
mtklein36352bf2015-03-25 18:17:31 -07002251 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002252 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002253 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002254 if (style) {
bungeman2873c762017-01-13 11:40:21 -05002255 *style = fontstyle_from_descriptor(desc, false);
reed@google.com83787c52013-03-26 17:19:15 +00002256 }
2257 if (name) {
2258 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2259 name->reset();
2260 }
2261 }
2262 }
2263
mtklein36352bf2015-03-25 18:17:31 -07002264 SkTypeface* createTypeface(int index) override {
bungemanc292b5f2016-12-20 12:04:29 -05002265 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
2266 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002267
Mike Reed59227392017-09-26 09:46:08 -04002268 return create_from_desc(desc).release();
reed@google.com83787c52013-03-26 17:19:15 +00002269 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002270
mtklein36352bf2015-03-25 18:17:31 -07002271 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002272 if (0 == fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002273 return nullptr;
reed@google.com964988f2013-03-29 14:57:22 +00002274 }
Mike Reed59227392017-09-26 09:46:08 -04002275 return create_from_desc(findMatchingDesc(pattern)).release();
reed@google.com964988f2013-03-29 14:57:22 +00002276 }
2277
reed@google.com83787c52013-03-26 17:19:15 +00002278private:
bungemanc292b5f2016-12-20 12:04:29 -05002279 UniqueCFRef<CFArrayRef> fArray;
2280 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002281
2282 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2283 int bestMetric = SK_MaxS32;
halcanary96fcdcc2015-08-27 07:41:13 -07002284 CTFontDescriptorRef bestDesc = nullptr;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002285
reed@google.com964988f2013-03-29 14:57:22 +00002286 for (int i = 0; i < fCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002287 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
bungeman2873c762017-01-13 11:40:21 -05002288 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc, false));
reed@google.com964988f2013-03-29 14:57:22 +00002289 if (0 == metric) {
2290 return desc;
2291 }
2292 if (metric < bestMetric) {
2293 bestMetric = metric;
2294 bestDesc = desc;
2295 }
2296 }
2297 SkASSERT(bestDesc);
2298 return bestDesc;
2299 }
reed@google.com83787c52013-03-26 17:19:15 +00002300};
2301
2302class SkFontMgr_Mac : public SkFontMgr {
bungemanc292b5f2016-12-20 12:04:29 -05002303 UniqueCFRef<CFArrayRef> fNames;
2304 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002305
bungemanc292b5f2016-12-20 12:04:29 -05002306 CFStringRef getFamilyNameAt(int index) const {
reed@google.com83787c52013-03-26 17:19:15 +00002307 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002308 return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002309 }
2310
reed@google.com964988f2013-03-29 14:57:22 +00002311 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
bungemanc292b5f2016-12-20 12:04:29 -05002312 UniqueCFRef<CFMutableDictionaryRef> cfAttr(
reed@google.com964988f2013-03-29 14:57:22 +00002313 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2314 &kCFTypeDictionaryKeyCallBacks,
2315 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002316
bungemanc292b5f2016-12-20 12:04:29 -05002317 CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002318
bungemanc292b5f2016-12-20 12:04:29 -05002319 UniqueCFRef<CTFontDescriptorRef> desc(
2320 CTFontDescriptorCreateWithAttributes(cfAttr.get()));
2321 return new SkFontStyleSet_Mac(desc.get());
2322 }
2323
2324 /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
2325 * provide a wrapper here that will return an empty array if need be.
2326 */
2327 static UniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
2328#ifdef SK_BUILD_FOR_IOS
2329 return UniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
2330#else
2331 return UniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
2332#endif
reed@google.com964988f2013-03-29 14:57:22 +00002333 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002334
reed@google.com83787c52013-03-26 17:19:15 +00002335public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002336 SkFontMgr_Mac()
bungemanc292b5f2016-12-20 12:04:29 -05002337 : fNames(CopyAvailableFontFamilyNames())
2338 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0) {}
reed@google.com95625db2013-03-25 20:44:02 +00002339
2340protected:
mtklein36352bf2015-03-25 18:17:31 -07002341 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002342 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002343 }
2344
mtklein36352bf2015-03-25 18:17:31 -07002345 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002346 if ((unsigned)index < (unsigned)fCount) {
bungemanc292b5f2016-12-20 12:04:29 -05002347 CFStringToSkString(this->getFamilyNameAt(index), familyName);
reed@google.com83787c52013-03-26 17:19:15 +00002348 } else {
2349 familyName->reset();
2350 }
reed@google.com95625db2013-03-25 20:44:02 +00002351 }
2352
mtklein36352bf2015-03-25 18:17:31 -07002353 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002354 if ((unsigned)index >= (unsigned)fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002355 return nullptr;
reed@google.com83787c52013-03-26 17:19:15 +00002356 }
bungemanc292b5f2016-12-20 12:04:29 -05002357 return CreateSet(this->getFamilyNameAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002358 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002359
mtklein36352bf2015-03-25 18:17:31 -07002360 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
bungeman7575bb12017-05-01 13:02:42 -04002361 if (!familyName) {
2362 return nullptr;
2363 }
bungemanc292b5f2016-12-20 12:04:29 -05002364 UniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
2365 return CreateSet(cfName.get());
reed@google.com964988f2013-03-29 14:57:22 +00002366 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002367
bungemanc292b5f2016-12-20 12:04:29 -05002368 SkTypeface* onMatchFamilyStyle(const char familyName[],
bungeman7575bb12017-05-01 13:02:42 -04002369 const SkFontStyle& style) const override {
2370 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
Mike Reed59227392017-09-26 09:46:08 -04002371 return create_from_desc(desc.get()).release();
reed@google.com95625db2013-03-25 20:44:02 +00002372 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002373
bungemanc292b5f2016-12-20 12:04:29 -05002374 SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
2375 const SkFontStyle& style,
2376 const char* bcp47[], int bcp47Count,
2377 SkUnichar character) const override {
2378 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
2379 UniqueCFRef<CTFontRef> currentFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -07002380
2381 // kCFStringEncodingUTF32 is BE unless there is a BOM.
2382 // Since there is no machine endian option, explicitly state machine endian.
2383#ifdef SK_CPU_LENDIAN
2384 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
2385#else
2386 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
2387#endif
bungemanc292b5f2016-12-20 12:04:29 -05002388 UniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
bungemanbea97482016-08-24 08:29:50 -07002389 kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
2390 encoding, false));
bungemanc292b5f2016-12-20 12:04:29 -05002391 CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units.
2392 UniqueCFRef<CTFontRef> fallbackFont(
2393 CTFontCreateForString(currentFont.get(), string.get(), range));
Mike Reed59227392017-09-26 09:46:08 -04002394 return create_from_CTFontRef(std::move(fallbackFont), nullptr, false).release();
djsollen33068c12014-11-14 10:52:53 -08002395 }
2396
bungemanc292b5f2016-12-20 12:04:29 -05002397 SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2398 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002399 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002400 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002401
Mike Reed59227392017-09-26 09:46:08 -04002402 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
2403 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromData(std::move(data)));
bungemanc292b5f2016-12-20 12:04:29 -05002404 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002405 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002406 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002407 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002408 }
2409
Mike Reed59227392017-09-26 09:46:08 -04002410 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2411 int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002412 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromStream(std::move(stream)));
2413 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002414 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002415 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002416 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002417 }
2418
Ben Wagnerfc497342017-02-24 11:15:26 -05002419 /** Creates a dictionary suitable for setting the axes on a CGFont. */
2420 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, const SkFontArguments& args) {
2421 // The CGFont variation data is keyed by name, but lacks the tag.
bungemanf6c71072016-01-21 14:17:47 -08002422 // The CTFont variation data is keyed by tag, and also has the name.
Ben Wagnerfc497342017-02-24 11:15:26 -05002423 // We would like to work with CTFont variations, but creating a CTFont font with
bungemanf6c71072016-01-21 14:17:47 -08002424 // CTFont variation dictionary runs into bugs. So use the CTFont variation data
2425 // to match names to tags to create the appropriate CGFont.
bungemanc292b5f2016-12-20 12:04:29 -05002426 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
bungemanbc096bf2017-04-25 13:32:50 -04002427 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
2428 // 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 -05002429 UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct.get()));
Ben Wagnerfc497342017-02-24 11:15:26 -05002430 if (!ctAxes) {
bungemanf6c71072016-01-21 14:17:47 -08002431 return nullptr;
2432 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002433 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
bungemanf6c71072016-01-21 14:17:47 -08002434
Ben Wagnerfc497342017-02-24 11:15:26 -05002435 const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
bungemanf6c71072016-01-21 14:17:47 -08002436
bungemanc292b5f2016-12-20 12:04:29 -05002437 UniqueCFRef<CFMutableDictionaryRef> dict(
2438 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2439 &kCFTypeDictionaryKeyCallBacks,
2440 &kCFTypeDictionaryValueCallBacks));
2441
bungemanf6c71072016-01-21 14:17:47 -08002442 for (int i = 0; i < axisCount; ++i) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002443 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
bungemanf6c71072016-01-21 14:17:47 -08002444 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2445 return nullptr;
2446 }
2447 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2448
Ben Wagnerfc497342017-02-24 11:15:26 -05002449 // The assumption is that values produced by kCTFontVariationAxisNameKey and
2450 // kCGFontVariationAxisName will always be equal.
2451 // If they are ever not, seach the project history for "get_tag_for_name".
2452 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
bungemanf6c71072016-01-21 14:17:47 -08002453 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2454 return nullptr;
2455 }
2456
Ben Wagnerfc497342017-02-24 11:15:26 -05002457 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
2458 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2459 return nullptr;
bungemanf6c71072016-01-21 14:17:47 -08002460 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002461 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
bungemanf6c71072016-01-21 14:17:47 -08002462 int64_t tagLong;
2463 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2464 return nullptr;
2465 }
2466
2467 // The variation axes can be set to any value, but cg will effectively pin them.
2468 // Pin them here to normalize.
Ben Wagnerfc497342017-02-24 11:15:26 -05002469 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
2470 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
2471 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
bungemanf6c71072016-01-21 14:17:47 -08002472 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2473 !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
2474 !def || CFGetTypeID(def) != CFNumberGetTypeID())
2475 {
2476 return nullptr;
2477 }
2478 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2479 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2480 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2481 double minDouble;
2482 double maxDouble;
2483 double defDouble;
2484 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2485 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
2486 !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
2487 {
2488 return nullptr;
2489 }
2490
2491 double value = defDouble;
bungeman9aec8942017-03-29 13:38:53 -04002492 // The position may be over specified. If there are multiple values for a given axis,
2493 // use the last one since that's what css-fonts-4 requires.
2494 for (int j = position.coordinateCount; j --> 0;) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002495 if (position.coordinates[j].axis == tagLong) {
2496 value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
2497 minDouble, maxDouble);
bungemanf6c71072016-01-21 14:17:47 -08002498 break;
2499 }
2500 }
bungemanc292b5f2016-12-20 12:04:29 -05002501 UniqueCFRef<CFNumberRef> valueNumber(
2502 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2503 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungemanf6c71072016-01-21 14:17:47 -08002504 }
bungemanc292b5f2016-12-20 12:04:29 -05002505 return std::move(dict);
bungemanf6c71072016-01-21 14:17:47 -08002506 }
Mike Reed59227392017-09-26 09:46:08 -04002507 sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> s,
2508 const SkFontArguments& args) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002509 if (args.getCollectionIndex() != 0) {
2510 return nullptr;
2511 }
bungemanc292b5f2016-12-20 12:04:29 -05002512 UniqueCFRef<CGDataProviderRef> provider(SkCreateDataProviderFromStream(std::move(s)));
2513 if (!provider) {
bungemanf6c71072016-01-21 14:17:47 -08002514 return nullptr;
2515 }
bungemanc292b5f2016-12-20 12:04:29 -05002516 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2517 if (!cg) {
bungemanf6c71072016-01-21 14:17:47 -08002518 return nullptr;
2519 }
2520
Ben Wagnerfc497342017-02-24 11:15:26 -05002521 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), args);
bungemanf6c71072016-01-21 14:17:47 -08002522 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2523 // created from a data provider does not appear to have any ownership of the underlying
2524 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002525 UniqueCFRef<CGFontRef> cgVariant;
bungemanf6c71072016-01-21 14:17:47 -08002526 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002527 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungemanf6c71072016-01-21 14:17:47 -08002528 } else {
mtklein18300a32016-03-16 13:53:35 -07002529 cgVariant.reset(cg.release());
bungemanf6c71072016-01-21 14:17:47 -08002530 }
2531
bungemanc292b5f2016-12-20 12:04:29 -05002532 UniqueCFRef<CTFontRef> ct(
2533 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungemanf6c71072016-01-21 14:17:47 -08002534 if (!ct) {
2535 return nullptr;
2536 }
bungemanc292b5f2016-12-20 12:04:29 -05002537 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungemanf6c71072016-01-21 14:17:47 -08002538 }
2539
Ben Wagnerfc497342017-02-24 11:15:26 -05002540 /** Creates a dictionary suitable for setting the axes on a CGFont. */
bungemanc292b5f2016-12-20 12:04:29 -05002541 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, SkFontData* fontData) {
2542 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
bungeman41868fe2015-05-20 09:21:04 -07002543 if (!cgAxes) {
halcanary96fcdcc2015-08-27 07:41:13 -07002544 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002545 }
2546
bungemanc292b5f2016-12-20 12:04:29 -05002547 CFIndex axisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07002548 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002549 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002550 }
2551
bungemanc292b5f2016-12-20 12:04:29 -05002552 UniqueCFRef<CFMutableDictionaryRef> dict(
2553 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2554 &kCFTypeDictionaryKeyCallBacks,
2555 &kCFTypeDictionaryValueCallBacks));
2556
bungeman41868fe2015-05-20 09:21:04 -07002557 for (int i = 0; i < fontData->getAxisCount(); ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002558 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07002559 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002560 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002561 }
2562 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2563
2564 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2565 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002566 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002567 }
2568
2569 // The variation axes can be set to any value, but cg will effectively pin them.
2570 // Pin them here to normalize.
2571 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2572 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2573 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2574 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2575 {
halcanary96fcdcc2015-08-27 07:41:13 -07002576 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002577 }
2578 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2579 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2580 double minDouble;
2581 double maxDouble;
2582 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2583 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2584 {
halcanary96fcdcc2015-08-27 07:41:13 -07002585 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002586 }
2587 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
bungemanc292b5f2016-12-20 12:04:29 -05002588 UniqueCFRef<CFNumberRef> valueNumber(
2589 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2590 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungeman41868fe2015-05-20 09:21:04 -07002591 }
bungemanc292b5f2016-12-20 12:04:29 -05002592 return std::move(dict);
bungeman41868fe2015-05-20 09:21:04 -07002593 }
Mike Reed59227392017-09-26 09:46:08 -04002594 sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData> fontData) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002595 if (fontData->getIndex() != 0) {
2596 return nullptr;
2597 }
bungemanc292b5f2016-12-20 12:04:29 -05002598 UniqueCFRef<CGDataProviderRef> provider(
bungemanf93d7112016-09-16 06:24:20 -07002599 SkCreateDataProviderFromStream(fontData->detachStream()));
bungemanc292b5f2016-12-20 12:04:29 -05002600 if (!provider) {
halcanary96fcdcc2015-08-27 07:41:13 -07002601 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002602 }
bungemanc292b5f2016-12-20 12:04:29 -05002603 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2604 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07002605 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002606 }
2607
bungemanc292b5f2016-12-20 12:04:29 -05002608 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), fontData.get());
bungeman41868fe2015-05-20 09:21:04 -07002609 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2610 // created from a data provider does not appear to have any ownership of the underlying
2611 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002612 UniqueCFRef<CGFontRef> cgVariant;
bungeman41868fe2015-05-20 09:21:04 -07002613 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002614 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungeman41868fe2015-05-20 09:21:04 -07002615 } else {
mtklein18300a32016-03-16 13:53:35 -07002616 cgVariant.reset(cg.release());
bungeman41868fe2015-05-20 09:21:04 -07002617 }
2618
bungemanc292b5f2016-12-20 12:04:29 -05002619 UniqueCFRef<CTFontRef> ct(
2620 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07002621 if (!ct) {
halcanary96fcdcc2015-08-27 07:41:13 -07002622 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002623 }
bungemanc292b5f2016-12-20 12:04:29 -05002624 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungeman41868fe2015-05-20 09:21:04 -07002625 }
2626
Mike Reed59227392017-09-26 09:46:08 -04002627 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002628 UniqueCFRef<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2629 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002630 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002631 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002632 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002633 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002634
Mike Reed59227392017-09-26 09:46:08 -04002635 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002636 if (familyName) {
2637 familyName = map_css_names(familyName);
2638 }
2639
Mike Reed59227392017-09-26 09:46:08 -04002640 sk_sp<SkTypeface> face = create_from_name(familyName, style);
bungeman53d5c6e2016-04-08 07:22:29 -07002641 if (face) {
2642 return face;
bungemana4c4a2d2014-10-20 13:33:19 -07002643 }
bungeman53d5c6e2016-04-08 07:22:29 -07002644
bungemanc292b5f2016-12-20 12:04:29 -05002645 static SkTypeface* gDefaultFace;
2646 static SkOnce lookupDefault;
bungeman83f1f442017-02-06 13:21:33 -05002647 static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
bungemanc292b5f2016-12-20 12:04:29 -05002648 lookupDefault([]{
Mike Reed59227392017-09-26 09:46:08 -04002649 gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
bungemanc292b5f2016-12-20 12:04:29 -05002650 });
Mike Reed59227392017-09-26 09:46:08 -04002651 return sk_ref_sp(gDefaultFace);
reed@google.com7fdcd442013-07-30 21:25:49 +00002652 }
reed@google.com95625db2013-03-25 20:44:02 +00002653};
2654
reed@google.com7fdcd442013-07-30 21:25:49 +00002655///////////////////////////////////////////////////////////////////////////////
2656
Ben Wagner3546ff12017-01-03 13:32:36 -05002657sk_sp<SkFontMgr> SkFontMgr::Factory() { return sk_make_sp<SkFontMgr_Mac>(); }
mtklein1ee76512015-11-02 10:20:27 -08002658
2659#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)