blob: 8fadb805a26a2fb98b5405a07faa639d4b9eac72 [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
bungemane280d062016-03-24 11:27:05 -07001059static void build_power_table(uint8_t table[]) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001060 for (int i = 0; i < 256; i++) {
1061 float x = i / 255.f;
bungemane280d062016-03-24 11:27:05 -07001062 int xx = SkScalarRoundToInt(x * x * 255);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001063 table[i] = SkToU8(xx);
1064 }
1065}
1066
1067/**
1068 * This will invert the gamma applied by CoreGraphics, so we can get linear
1069 * values.
1070 *
1071 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1072 * The color space used does not appear to affect this choice.
1073 */
1074static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1075 static bool gInited;
1076 static uint8_t gTableCoreGraphicsSmoothing[256];
1077 if (!gInited) {
bungemane280d062016-03-24 11:27:05 -07001078 build_power_table(gTableCoreGraphicsSmoothing);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001079 gInited = true;
1080 }
1081 return gTableCoreGraphicsSmoothing;
1082}
1083
1084static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1085 while (count > 0) {
1086 uint8_t mask = 0;
1087 for (int i = 7; i >= 0; --i) {
bungemane280d062016-03-24 11:27:05 -07001088 mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001089 if (0 == --count) {
1090 break;
1091 }
1092 }
1093 *dst++ = mask;
1094 }
1095}
1096
1097template<bool APPLY_PREBLEND>
1098static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
bungemane280d062016-03-24 11:27:05 -07001099 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
1100 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
1101 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
bungeman3b4b66c2015-01-08 08:33:44 -08001102 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1103#if SK_SHOW_TEXT_BLIT_COVERAGE
1104 lum = SkTMax(lum, (U8CPU)0x30);
1105#endif
1106 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001107}
1108template<bool APPLY_PREBLEND>
1109static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1110 const SkGlyph& glyph, const uint8_t* table8) {
1111 const int width = glyph.fWidth;
1112 size_t dstRB = glyph.rowBytes();
1113 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1114
1115 for (int y = 0; y < glyph.fHeight; y++) {
1116 for (int i = 0; i < width; ++i) {
1117 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1118 }
bungemanc292b5f2016-12-20 12:04:29 -05001119 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1120 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001121 }
1122}
1123
1124template<bool APPLY_PREBLEND>
1125static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1126 const uint8_t* tableG,
1127 const uint8_t* tableB) {
bungemane280d062016-03-24 11:27:05 -07001128 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
1129 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
1130 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001131#if SK_SHOW_TEXT_BLIT_COVERAGE
1132 r = SkTMax(r, (U8CPU)0x30);
1133 g = SkTMax(g, (U8CPU)0x30);
1134 b = SkTMax(b, (U8CPU)0x30);
1135#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001136 return SkPack888ToRGB16(r, g, b);
1137}
1138template<bool APPLY_PREBLEND>
1139static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1140 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1141 const int width = glyph.fWidth;
1142 size_t dstRB = glyph.rowBytes();
1143 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1144
1145 for (int y = 0; y < glyph.fHeight; y++) {
1146 for (int i = 0; i < width; i++) {
1147 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1148 }
bungemanc292b5f2016-12-20 12:04:29 -05001149 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1150 dst = SkTAddOffset<uint16_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001151 }
1152}
1153
bungeman34902632014-12-10 21:43:27 -08001154static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1155 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001156 U8CPU r = (rgb >> 16) & 0xFF;
1157 U8CPU g = (rgb >> 8) & 0xFF;
1158 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001159#if SK_SHOW_TEXT_BLIT_COVERAGE
1160 a = SkTMax(a, (U8CPU)0x30);
1161#endif
bungeman34902632014-12-10 21:43:27 -08001162 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001163}
reed@google.comf77b35d2013-05-02 20:39:44 +00001164
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001165void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
bungemanc292b5f2016-12-20 12:04:29 -05001166 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001167
1168 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1169 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1170
1171 // Draw the glyph
1172 size_t cgRowBytes;
1173 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
halcanary96fcdcc2015-08-27 07:41:13 -07001174 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001175 return;
1176 }
1177
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001178 // Fix the glyph
bungemanc292b5f2016-12-20 12:04:29 -05001179 if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
1180 (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD))
1181 {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001182 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1183
1184 //Note that the following cannot really be integrated into the
1185 //pre-blend, since we may not be applying the pre-blend; when we aren't
1186 //applying the pre-blend it means that a filter wants linear anyway.
1187 //Other code may also be applying the pre-blend, so we'd need another
1188 //one with this and one without.
1189 CGRGBPixel* addr = cgPixels;
1190 for (int y = 0; y < glyph.fHeight; ++y) {
1191 for (int x = 0; x < glyph.fWidth; ++x) {
1192 int r = (addr[x] >> 16) & 0xFF;
1193 int g = (addr[x] >> 8) & 0xFF;
1194 int b = (addr[x] >> 0) & 0xFF;
1195 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1196 }
bungemane280d062016-03-24 11:27:05 -07001197 addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001198 }
1199 }
1200
1201 // Convert glyph to mask
1202 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001203 case SkMask::kLCD16_Format: {
1204 if (fPreBlend.isApplicable()) {
1205 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1206 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1207 } else {
1208 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1209 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1210 }
1211 } break;
1212 case SkMask::kA8_Format: {
1213 if (fPreBlend.isApplicable()) {
1214 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1215 } else {
1216 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1217 }
1218 } break;
1219 case SkMask::kBW_Format: {
1220 const int width = glyph.fWidth;
1221 size_t dstRB = glyph.rowBytes();
1222 uint8_t* dst = (uint8_t*)glyph.fImage;
1223 for (int y = 0; y < glyph.fHeight; y++) {
1224 cgpixels_to_bits(dst, cgPixels, width);
bungemanc292b5f2016-12-20 12:04:29 -05001225 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1226 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001227 }
1228 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001229 case SkMask::kARGB32_Format: {
1230 const int width = glyph.fWidth;
1231 size_t dstRB = glyph.rowBytes();
1232 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1233 for (int y = 0; y < glyph.fHeight; y++) {
1234 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001235 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001236 }
bungemanc292b5f2016-12-20 12:04:29 -05001237 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1238 dst = SkTAddOffset<SkPMColor>(dst, dstRB);
reed@google.comf77b35d2013-05-02 20:39:44 +00001239 }
1240 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001241 default:
1242 SkDEBUGFAIL("unexpected mask format");
1243 break;
1244 }
1245}
1246
1247/*
1248 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1249 * seems sufficient, and possibly even correct, to allow the hinted outline
1250 * to be subpixel positioned.
1251 */
1252#define kScaleForSubPixelPositionHinting (4.0f)
1253
Ben Wagner6e9ac122016-11-11 14:31:06 -05001254void SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001255 AUTO_CG_LOCK();
1256
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001257 SkScalar scaleX = SK_Scalar1;
1258 SkScalar scaleY = SK_Scalar1;
1259
bungemanef27ce32015-10-29 09:30:32 -07001260 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001261 /*
1262 * For subpixel positioning, we want to return an unhinted outline, so it
1263 * can be positioned nicely at fractional offsets. However, we special-case
1264 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1265 * we want to retain hinting in the direction orthogonal to the baseline.
1266 * e.g. for horizontal baseline, we want to retain hinting in Y.
1267 * The way we remove hinting is to scale the font by some value (4) in that
1268 * direction, ask for the path, and then scale the path back down.
1269 */
bungeman7cbeaae2015-09-22 09:54:56 -07001270 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001271 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001272 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001273 // now see if we need to restore hinting for axis-aligned baselines
bungeman27876bc2016-02-29 11:22:55 -08001274 switch (this->computeAxisAlignmentForHText()) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001275 case kX_SkAxisAlignment:
1276 scaleY = SK_Scalar1; // want hinting in the Y direction
1277 break;
1278 case kY_SkAxisAlignment:
1279 scaleX = SK_Scalar1; // want hinting in the X direction
1280 break;
1281 default:
1282 break;
1283 }
1284
bungemanef27ce32015-10-29 09:30:32 -07001285 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1286 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001287 }
1288
Ben Wagner6e9ac122016-11-11 14:31:06 -05001289 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
bungemanc292b5f2016-12-20 12:04:29 -05001290 UniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001291
1292 path->reset();
halcanary96fcdcc2015-08-27 07:41:13 -07001293 if (cgPath != nullptr) {
bungemanc292b5f2016-12-20 12:04:29 -05001294 CGPathApply(cgPath.get(), path, SkScalerContext_Mac::CTPathElement);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001295 }
1296
bungeman@google.comcefd9812013-05-15 15:07:32 +00001297 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001298 SkMatrix m;
1299 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1300 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001301 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001302 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001303 SkPoint offset;
1304 getVerticalOffset(cgGlyph, &offset);
1305 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001306 }
1307}
1308
bungeman41078062014-07-07 08:16:37 -07001309void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001310 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001311 return;
1312 }
1313
reedd0f41732015-07-10 12:08:38 -07001314 AUTO_CG_LOCK();
1315
bungemanc292b5f2016-12-20 12:04:29 -05001316 CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001317
bungeman41078062014-07-07 08:16:37 -07001318 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001319 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont.get()));
1320 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001321 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001322 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001323 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1324 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1325 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001326 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungemanc292b5f2016-12-20 12:04:29 -05001327 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont.get()));
1328 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont.get()));
1329 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont.get()));
1330 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont.get()));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001331
Ben Wagner219f3622017-07-17 15:32:25 -04001332 metrics->fFlags = 0;
Ben Wagner3318da52017-03-23 14:01:22 -04001333 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag;
bungeman41078062014-07-07 08:16:37 -07001334 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman4ec46aa2017-02-15 17:49:12 -05001335
1336 // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
1337 // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
1338 // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
1339 // table are read, but then overwritten if the font is not a system font. As a result, if there
1340 // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
1341 struct OS2HeightMetrics {
1342 SK_OT_SHORT sxHeight;
1343 SK_OT_SHORT sCapHeight;
1344 } heights;
1345 size_t bytesRead = this->getTypeface()->getTableData(
1346 SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
1347 sizeof(heights), &heights);
1348 if (bytesRead == sizeof(heights)) {
1349 // 'fontSize' is correct because the entire resolved size is set by the constructor.
1350 CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
1351 unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
1352 unsigned maxSaneHeight = upem * 2;
1353 uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
1354 if (xHeight && xHeight < maxSaneHeight) {
1355 metrics->fXHeight = CGToScalar(xHeight * fontSize / upem);
1356 }
1357 uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
1358 if (capHeight && capHeight < maxSaneHeight) {
1359 metrics->fCapHeight = CGToScalar(capHeight * fontSize / upem);
1360 }
1361 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001362}
1363
1364void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1365 SkPath* skPath = (SkPath*)info;
1366
1367 // Process the path element
1368 switch (element->type) {
1369 case kCGPathElementMoveToPoint:
1370 skPath->moveTo(element->points[0].x, -element->points[0].y);
1371 break;
1372
1373 case kCGPathElementAddLineToPoint:
1374 skPath->lineTo(element->points[0].x, -element->points[0].y);
1375 break;
1376
1377 case kCGPathElementAddQuadCurveToPoint:
1378 skPath->quadTo(element->points[0].x, -element->points[0].y,
1379 element->points[1].x, -element->points[1].y);
1380 break;
1381
1382 case kCGPathElementAddCurveToPoint:
1383 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1384 element->points[1].x, -element->points[1].y,
1385 element->points[2].x, -element->points[2].y);
1386 break;
1387
1388 case kCGPathElementCloseSubpath:
1389 skPath->close();
1390 break;
1391
1392 default:
1393 SkDEBUGFAIL("Unknown path element!");
1394 break;
1395 }
1396}
1397
1398
1399///////////////////////////////////////////////////////////////////////////////
1400
halcanary96fcdcc2015-08-27 07:41:13 -07001401// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001402// Call must still manage its ownership of provider
Mike Reed59227392017-09-26 09:46:08 -04001403static sk_sp<SkTypeface> create_from_dataProvider(UniqueCFRef<CGDataProviderRef> provider,
1404 int ttcIndex) {
Ben Wagnerfc497342017-02-24 11:15:26 -05001405 if (ttcIndex != 0) {
1406 return nullptr;
1407 }
bungemanc292b5f2016-12-20 12:04:29 -05001408 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
1409 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07001410 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001411 }
bungemanc292b5f2016-12-20 12:04:29 -05001412 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg.get(), 0, nullptr, nullptr));
1413 if (!ct) {
1414 return nullptr;
1415 }
1416 return create_from_CTFontRef(std::move(ct), nullptr, true);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001417}
1418
bungemanc292b5f2016-12-20 12:04:29 -05001419// Web fonts added to the CTFont registry do not return their character set.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001420// Iterate through the font in this case. The existing caller caches the result,
1421// so the performance impact isn't too bad.
1422static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1423 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001424 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001425 SkUnichar* out = glyphToUnicode->begin();
1426 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1427 UniChar unichar = 0;
1428 while (glyphCount > 0) {
1429 CGGlyph glyph;
1430 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
Hal Canary266dcb02017-04-07 11:49:20 -04001431 if (out[glyph] != 0) {
1432 out[glyph] = unichar;
1433 --glyphCount;
1434 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001435 }
1436 if (++unichar == 0) {
1437 break;
1438 }
1439 }
1440}
1441
1442// Construct Glyph to Unicode table.
1443// Unicode code points that require conjugate pairs in utf16 are not
1444// supported.
1445static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1446 SkTDArray<SkUnichar>* glyphToUnicode) {
bungemanc292b5f2016-12-20 12:04:29 -05001447 UniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001448 if (!charSet) {
1449 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1450 return;
1451 }
1452
bungemanc292b5f2016-12-20 12:04:29 -05001453 UniqueCFRef<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001454 if (!bitmap) {
1455 return;
1456 }
bungemanc292b5f2016-12-20 12:04:29 -05001457 CFIndex length = CFDataGetLength(bitmap.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001458 if (!length) {
1459 return;
1460 }
1461 if (length > 8192) {
1462 // TODO: Add support for Unicode above 0xFFFF
1463 // Consider only the BMP portion of the Unicode character points.
1464 // The bitmap may contain other planes, up to plane 16.
1465 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1466 length = 8192;
1467 }
bungemanc292b5f2016-12-20 12:04:29 -05001468 const UInt8* bits = CFDataGetBytePtr(bitmap.get());
reed@google.com7fa2a652014-01-27 13:42:58 +00001469 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001470 SkUnichar* out = glyphToUnicode->begin();
1471 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1472 for (int i = 0; i < length; i++) {
1473 int mask = bits[i];
1474 if (!mask) {
1475 continue;
1476 }
1477 for (int j = 0; j < 8; j++) {
1478 CGGlyph glyph;
1479 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1480 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1481 out[glyph] = unichar;
1482 }
1483 }
1484 }
1485}
1486
halcanary96fcdcc2015-08-27 07:41:13 -07001487/** Assumes src and dst are not nullptr. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001488static void CFStringToSkString(CFStringRef src, SkString* dst) {
1489 // Reserve enough room for the worst-case string,
1490 // plus 1 byte for the trailing null.
1491 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1492 kCFStringEncodingUTF8) + 1;
1493 dst->resize(length);
1494 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1495 // Resize to the actual UTF-8 length used, stripping the null character.
1496 dst->resize(strlen(dst->c_str()));
1497}
1498
Hal Canary209e4b12017-05-04 14:23:55 -04001499std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
reed@google.com2689f612013-03-20 20:01:47 +00001500
reedd0f41732015-07-10 12:08:38 -07001501 AUTO_CG_LOCK();
1502
bungemanc292b5f2016-12-20 12:04:29 -05001503 UniqueCFRef<CTFontRef> ctFont =
1504 ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()), nullptr);
bungeman7cbeaae2015-09-22 09:54:56 -07001505
Hal Canary209e4b12017-05-04 14:23:55 -04001506 std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001507
1508 {
bungemanc292b5f2016-12-20 12:04:29 -05001509 UniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
bungeman256b3512014-07-02 07:57:59 -07001510 if (fontName.get()) {
bungemanc292b5f2016-12-20 12:04:29 -05001511 CFStringToSkString(fontName.get(), &info->fFontName);
bungeman256b3512014-07-02 07:57:59 -07001512 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001513 }
1514
Ben Wagnerc6639052017-03-02 13:31:16 -05001515 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1516 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1517 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1518 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
1519 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont.get(), nullptr));
1520 if (cgFont) {
1521 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
1522 if (cgAxes && CFArrayGetCount(cgAxes.get()) > 0) {
1523 info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
1524 }
1525 }
1526
bungemanc292b5f2016-12-20 12:04:29 -05001527 CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001528
Hal Canary209e4b12017-05-04 14:23:55 -04001529 populate_glyph_to_unicode(ctFont.get(), glyphCount, &info->fGlyphToUnicode);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001530
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001531 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1532 // fonts always have both glyf and loca tables. At the least, this is what
1533 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1534 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001535 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001536 return info;
1537 }
1538
1539 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
bungemanc292b5f2016-12-20 12:04:29 -05001540 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001541 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1542 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1543 }
1544 if (symbolicTraits & kCTFontItalicTrait) {
1545 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1546 }
1547 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001548 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1549 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1550 } else if (stylisticClass & kCTFontScriptsClass) {
1551 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1552 }
bungemanc292b5f2016-12-20 12:04:29 -05001553 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
1554 info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
1555 info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
1556 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
1557 CGRect bbox = CTFontGetBoundingBox(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001558
1559 SkRect r;
1560 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1561 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1562 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1563 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1564
1565 r.roundOut(&(info->fBBox));
1566
1567 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1568 // This probably isn't very good with an italic font.
1569 int16_t min_width = SHRT_MAX;
1570 info->fStemV = 0;
1571 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1572 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1573 CGGlyph glyphs[count];
1574 CGRect boundingRects[count];
bungemanc292b5f2016-12-20 12:04:29 -05001575 if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
bungeman18ec7b92017-02-09 17:15:59 -05001576 CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001577 glyphs, boundingRects, count);
1578 for (size_t i = 0; i < count; i++) {
1579 int16_t width = (int16_t) boundingRects[i].size.width;
1580 if (width > 0 && width < min_width) {
1581 min_width = width;
1582 info->fStemV = min_width;
1583 }
1584 }
1585 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001586 return info;
1587}
1588
1589///////////////////////////////////////////////////////////////////////////////
1590
reed@google.comcc9aad52013-03-21 19:28:10 +00001591static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1592 CTFontRef ctFont = typeface->fFontRef.get();
bungemanc292b5f2016-12-20 12:04:29 -05001593 UniqueCFRef<CFNumberRef> fontFormatRef(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001594 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1595 if (!fontFormatRef) {
1596 return 0;
1597 }
1598
1599 SInt32 fontFormatValue;
bungemanc292b5f2016-12-20 12:04:29 -05001600 if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001601 return 0;
1602 }
1603
1604 switch (fontFormatValue) {
1605 case kCTFontFormatOpenTypePostScript:
1606 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1607 case kCTFontFormatOpenTypeTrueType:
1608 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1609 case kCTFontFormatTrueType:
1610 return SkSFNTHeader::fontType_MacTrueType::TAG;
1611 case kCTFontFormatPostScript:
1612 return SkSFNTHeader::fontType_PostScript::TAG;
1613 case kCTFontFormatBitmap:
1614 return SkSFNTHeader::fontType_MacTrueType::TAG;
1615 case kCTFontFormatUnrecognized:
1616 default:
1617 //CT seems to be unreliable in being able to obtain the type,
1618 //even if all we want is the first four bytes of the font resource.
1619 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1620 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1621 }
1622}
1623
bungeman5f213d92015-01-27 05:39:10 -08001624SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
reed@google.comcc9aad52013-03-21 19:28:10 +00001625 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001626 if (0 == fontType) {
halcanary96fcdcc2015-08-27 07:41:13 -07001627 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001628 }
1629
1630 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001631 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001632 SkTDArray<SkFontTableTag> tableTags;
1633 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001634 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001635
1636 // calc total size for font, save sizes
1637 SkTDArray<size_t> tableSizes;
1638 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1639 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001640 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001641 totalSize += (tableSize + 3) & ~3;
1642 *tableSizes.append() = tableSize;
1643 }
1644
1645 // reserve memory for stream, and zero it (tables must be zero padded)
1646 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1647 char* dataStart = (char*)stream->getMemoryBase();
1648 sk_bzero(dataStart, totalSize);
1649 char* dataPtr = dataStart;
1650
1651 // compute font header entries
1652 uint16_t entrySelector = 0;
1653 uint16_t searchRange = 1;
1654 while (searchRange < numTables >> 1) {
1655 entrySelector++;
1656 searchRange <<= 1;
1657 }
1658 searchRange <<= 4;
1659 uint16_t rangeShift = (numTables << 4) - searchRange;
1660
1661 // write font header
1662 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1663 header->fontType = fontType;
1664 header->numTables = SkEndian_SwapBE16(numTables);
1665 header->searchRange = SkEndian_SwapBE16(searchRange);
1666 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1667 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1668 dataPtr += sizeof(SkSFNTHeader);
1669
1670 // write tables
1671 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1672 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1673 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1674 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001675 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001676 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1677 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1678 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001679 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1680 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001681
1682 dataPtr += (tableSize + 3) & ~3;
1683 ++entry;
1684 }
1685
bungemanb3310c22015-03-02 09:05:36 -08001686 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001687 return stream;
1688}
1689
bungeman41868fe2015-05-20 09:21:04 -07001690struct NonDefaultAxesContext {
1691 SkFixed* axisValue;
1692 CFArrayRef cgAxes;
1693};
1694static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1695 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1696
1697 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1698 return;
1699 }
1700
1701 // The key is a CFString which is a string from the 'name' table.
1702 // Search the cgAxes for an axis with this name, and use its index to store the value.
1703 CFIndex keyIndex = -1;
1704 CFStringRef keyString = static_cast<CFStringRef>(key);
1705 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1706 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1707 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1708 continue;
1709 }
1710
1711 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1712 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1713 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1714 continue;
1715 }
1716 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1717 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1718 keyIndex = i;
1719 break;
1720 }
1721 }
1722 if (keyIndex == -1) {
1723 return;
1724 }
1725
1726 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1727 double valueDouble;
1728 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1729 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1730 {
1731 return;
1732 }
1733 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1734}
Ben Wagnerfc497342017-02-24 11:15:26 -05001735static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount,
bungeman41868fe2015-05-20 09:21:04 -07001736 SkAutoSTMalloc<4, SkFixed>* axisValues)
1737{
Ben Wagnerfc497342017-02-24 11:15:26 -05001738 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1739 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1740 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1741 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
1742 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05001743 if (!cgFont) {
bungeman41868fe2015-05-20 09:21:04 -07001744 return false;
1745 }
1746
bungemanc292b5f2016-12-20 12:04:29 -05001747 UniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
1748 // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
1749 if (!cgVariations) {
1750 return false;
1751 }
1752
1753 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
1754 if (!cgAxes) {
1755 return false;
1756 }
1757 *cgAxisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07001758 axisValues->reset(*cgAxisCount);
1759
1760 // Set all of the axes to their default values.
1761 // Fail if any default value cannot be determined.
1762 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001763 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07001764 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1765 return false;
1766 }
1767
1768 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1769 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1770 kCGFontVariationAxisDefaultValue);
1771 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1772 return false;
1773 }
1774 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
1775 double axisDefaultValueDouble;
1776 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
1777 {
1778 return false;
1779 }
1780 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
1781 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
1782 {
1783 return false;
1784 }
1785 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
1786 }
1787
1788 // Override the default values with the given font's stated axis values.
1789 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
bungemanc292b5f2016-12-20 12:04:29 -05001790 CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
bungeman41868fe2015-05-20 09:21:04 -07001791
1792 return true;
1793}
bungemanf93d7112016-09-16 06:24:20 -07001794std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
bungeman41868fe2015-05-20 09:21:04 -07001795 int index;
bungemanf93d7112016-09-16 06:24:20 -07001796 std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
bungeman41868fe2015-05-20 09:21:04 -07001797
1798 CFIndex cgAxisCount;
1799 SkAutoSTMalloc<4, SkFixed> axisValues;
bungemanc292b5f2016-12-20 12:04:29 -05001800 if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
bungemanf93d7112016-09-16 06:24:20 -07001801 return skstd::make_unique<SkFontData>(std::move(stream), index,
1802 axisValues.get(), cgAxisCount);
bungeman41868fe2015-05-20 09:21:04 -07001803 }
bungemanf93d7112016-09-16 06:24:20 -07001804 return skstd::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
bungeman41868fe2015-05-20 09:21:04 -07001805}
1806
Ben Wagnerfc497342017-02-24 11:15:26 -05001807/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */
1808static UniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
1809 CFArrayRef ctAxes) {
1810
1811 UniqueCFRef<CFMutableDictionaryRef> ctVariations(
1812 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1813 &kCFTypeDictionaryKeyCallBacks,
1814 &kCFTypeDictionaryValueCallBacks));
1815
1816 CFIndex axisCount = CFArrayGetCount(ctAxes);
1817 for (CFIndex i = 0; i < axisCount; ++i) {
1818 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i);
1819 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
1820 return nullptr;
1821 }
1822 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
1823
1824 // The assumption is that values produced by kCTFontVariationAxisNameKey and
1825 // kCGFontVariationAxisName will always be equal.
1826 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
1827 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
1828 return nullptr;
1829 }
1830
1831 CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName);
1832 if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) {
1833 return nullptr;
1834 }
1835
1836 CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1837 if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
1838 return nullptr;
1839 }
1840
1841 CFDictionaryAddValue(ctVariations.get(), axisTag, axisValue);
1842 }
1843 return std::move(ctVariations);
1844}
1845
1846int SkTypeface_Mac::onGetVariationDesignPosition(
1847 SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
1848{
1849 // The CGFont variation data does not contain the tag.
1850
bungemanbc096bf2017-04-25 13:32:50 -04001851 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
1852 // 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 -05001853 UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
1854 if (!ctAxes) {
1855 return -1;
1856 }
1857 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
1858 if (!coordinates || coordinateCount < axisCount) {
1859 return axisCount;
1860 }
1861
1862 // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
1863 // When this happens, try converting the CG variation to a CT variation.
1864 // On 10.12 and later, this only returns non-default variations.
1865 UniqueCFRef<CFDictionaryRef> ctVariations(CTFontCopyVariation(fFontRef.get()));
1866 if (!ctVariations) {
1867 // When 10.11 and earlier are no longer supported, the following code can be replaced with
1868 // return -1 and ct_variation_from_cg_variation can be removed.
1869 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
1870 if (!cgFont) {
1871 return -1;
1872 }
1873 UniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
1874 if (!cgVariations) {
1875 return -1;
1876 }
1877 ctVariations = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
1878 if (!ctVariations) {
1879 return -1;
1880 }
1881 }
1882
1883 for (int i = 0; i < axisCount; ++i) {
1884 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
1885 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
1886 return -1;
1887 }
1888 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
1889
1890 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
1891 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
1892 return -1;
1893 }
1894 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
1895 int64_t tagLong;
1896 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
1897 return -1;
1898 }
1899 coordinates[i].axis = tagLong;
1900
1901 CGFloat variationCGFloat;
1902 CFTypeRef variationValue = CFDictionaryGetValue(ctVariations.get(), tagNumber);
1903 if (variationValue) {
1904 if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
1905 return -1;
1906 }
1907 CFNumberRef variationNumber = static_cast<CFNumberRef>(variationValue);
1908 if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) {
1909 return -1;
1910 }
1911 } else {
1912 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
1913 if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) {
1914 return -1;
1915 }
1916 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
1917 if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) {
1918 return -1;
1919 }
1920 }
1921 coordinates[i].value = CGToScalar(variationCGFloat);
1922
1923 }
1924 return axisCount;
1925}
1926
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001927///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001928///////////////////////////////////////////////////////////////////////////////
1929
1930int SkTypeface_Mac::onGetUPEM() const {
bungemanc292b5f2016-12-20 12:04:29 -05001931 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
1932 return CGFontGetUnitsPerEm(cgFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001933}
1934
bungeman@google.com839702b2013-08-07 17:09:22 +00001935SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001936 SkTypeface::LocalizedStrings* nameIter =
bungemanc292b5f2016-12-20 12:04:29 -05001937 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07001938 if (nullptr == nameIter) {
bungemanc292b5f2016-12-20 12:04:29 -05001939 CFStringRef cfLanguageRaw;
1940 UniqueCFRef<CFStringRef> cfFamilyName(
1941 CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
1942 UniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
bungeman@google.coma9802692013-08-07 02:45:25 +00001943
1944 SkString skLanguage;
1945 SkString skFamilyName;
bungemanc292b5f2016-12-20 12:04:29 -05001946 if (cfLanguage) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001947 CFStringToSkString(cfLanguage.get(), &skLanguage);
1948 } else {
1949 skLanguage = "und"; //undetermined
1950 }
bungemanc292b5f2016-12-20 12:04:29 -05001951 if (cfFamilyName) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001952 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1953 }
1954
1955 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1956 }
1957 return nameIter;
1958}
1959
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001960int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
bungemanc292b5f2016-12-20 12:04:29 -05001961 UniqueCFRef<CFArrayRef> cfArray(
1962 CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
1963 if (!cfArray) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001964 return 0;
1965 }
bungemanc292b5f2016-12-20 12:04:29 -05001966 int count = SkToInt(CFArrayGetCount(cfArray.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001967 if (tags) {
1968 for (int i = 0; i < count; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001969 uintptr_t fontTag = reinterpret_cast<uintptr_t>(
1970 CFArrayGetValueAtIndex(cfArray.get(), i));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001971 tags[i] = static_cast<SkFontTableTag>(fontTag);
1972 }
1973 }
1974 return count;
1975}
1976
bungemanc292b5f2016-12-20 12:04:29 -05001977// If, as is the case with web fonts, the CTFont data isn't available,
1978// the CGFont data may work. While the CGFont may always provide the
1979// right result, leave the CTFont code path to minimize disruption.
1980static UniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
1981 UniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1982 kCTFontTableOptionNoOptions));
1983 if (!data) {
1984 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
1985 data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
1986 }
1987 return data;
1988}
1989
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001990size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1991 size_t length, void* dstData) const {
bungemanc292b5f2016-12-20 12:04:29 -05001992 UniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
1993 if (!srcData) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001994 return 0;
1995 }
1996
bungemanc292b5f2016-12-20 12:04:29 -05001997 size_t srcSize = CFDataGetLength(srcData.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001998 if (offset >= srcSize) {
1999 return 0;
2000 }
2001 if (length > srcSize - offset) {
2002 length = srcSize - offset;
2003 }
2004 if (dstData) {
bungemanc292b5f2016-12-20 12:04:29 -05002005 memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002006 }
2007 return length;
2008}
2009
reeda9322c22016-04-12 06:47:05 -07002010SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
2011 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04002012 return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002013}
2014
2015void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002016 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2017 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2018 {
2019 rec->fMaskFormat = SkMask::kA8_Format;
2020 // Render the glyphs as close as possible to what was requested.
2021 // The above turns off subpixel rendering, but the user requested it.
2022 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
2023 // See comments below for more details.
2024 rec->setHinting(SkPaint::kNormal_Hinting);
2025 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00002026
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002027 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00002028 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002029 SkScalerContext::kLCD_BGROrder_Flag |
2030 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002031
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002032 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002033
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002034 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002035
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002036 // Only two levels of hinting are supported.
2037 // kNo_Hinting means avoid CoreGraphics outline dilation.
2038 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
2039 // If there is no lcd support, hinting (dilation) cannot be supported.
2040 SkPaint::Hinting hinting = rec->getHinting();
2041 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
2042 hinting = SkPaint::kNo_Hinting;
2043 } else if (SkPaint::kFull_Hinting == hinting) {
2044 hinting = SkPaint::kNormal_Hinting;
2045 }
2046 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002047
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002048 // FIXME: lcd smoothed un-hinted rasterization unsupported.
2049 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
2050 // There is no current means to honor a request for unhinted lcd,
2051 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002052
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002053 // Hinting and smoothing should be orthogonal, but currently they are not.
2054 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
2055 // output is drawn from auto-dilated outlines (the amount of which is
2056 // determined by AppleFontSmoothing). Its regular anti-aliased output is
2057 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002058
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002059 // The behavior of Skia is as follows:
2060 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
2061 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
2062 // channel. This matches [LCD][yes-hint] in weight.
2063 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
2064 // Currenly side with LCD, effectively ignoring the hinting setting.
2065 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002066
bungemanc292b5f2016-12-20 12:04:29 -05002067 if (rec->fMaskFormat == SkMask::kLCD16_Format) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002068 if (lcdSupport) {
2069 //CoreGraphics creates 555 masks for smoothed text anyway.
2070 rec->fMaskFormat = SkMask::kLCD16_Format;
2071 rec->setHinting(SkPaint::kNormal_Hinting);
2072 } else {
2073 rec->fMaskFormat = SkMask::kA8_Format;
2074 }
2075 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002076
bungeman34902632014-12-10 21:43:27 -08002077 // CoreText provides no information as to whether a glyph will be color or not.
2078 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
2079 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08002080 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08002081 rec->fMaskFormat = SkMask::kARGB32_Format;
2082 }
2083
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002084 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2085 // All other masks can use regular gamma.
2086 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
2087#ifndef SK_GAMMA_APPLY_TO_A8
brianosmana1e8f8d2016-04-08 06:47:54 -07002088 // SRGBTODO: Is this correct? Do we want contrast boost?
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002089 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002090#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002091 } else {
2092 //CoreGraphics dialates smoothed text as needed.
2093 rec->setContrast(0);
2094 }
2095}
2096
bungemanc292b5f2016-12-20 12:04:29 -05002097/** Takes ownership of the CFStringRef. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002098static const char* get_str(CFStringRef ref, SkString* str) {
halcanary96fcdcc2015-08-27 07:41:13 -07002099 if (nullptr == ref) {
2100 return nullptr;
bungeman256b3512014-07-02 07:57:59 -07002101 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002102 CFStringToSkString(ref, str);
bungemanc292b5f2016-12-20 12:04:29 -05002103 CFRelease(ref);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002104 return str->c_str();
2105}
2106
bungemanb374d6a2014-09-17 07:48:59 -07002107void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
bungemanc292b5f2016-12-20 12:04:29 -05002108 get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
bungemanb374d6a2014-09-17 07:48:59 -07002109}
2110
reed@google.com5526ede2013-03-25 13:03:37 +00002111void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2112 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002113 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002114
bungemanc292b5f2016-12-20 12:04:29 -05002115 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
2116 desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
2117 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
bungemanb8113782016-07-25 16:54:59 -07002118 desc->setStyle(this->fontStyle());
caseq26337e92014-06-30 12:14:52 -07002119 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002120}
reed@google.com5526ede2013-03-25 13:03:37 +00002121
reed@google.combcb42ae2013-07-02 13:56:39 +00002122int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002123 uint16_t glyphs[], int glyphCount) const
2124{
2125 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
2126 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
2127 // 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 +00002128
reed@google.combcb42ae2013-07-02 13:56:39 +00002129 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002130 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
2131 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00002132 switch (encoding) {
2133 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002134 const char* utf8 = reinterpret_cast<const char*>(chars);
2135 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2136 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00002137 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002138 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
2139 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00002140 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002141 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002142 break;
2143 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002144 case kUTF16_Encoding: {
2145 src = reinterpret_cast<const UniChar*>(chars);
2146 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00002147 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002148 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2149 ++extra;
2150 }
reed@google.combcb42ae2013-07-02 13:56:39 +00002151 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002152 srcCount = glyphCount + extra;
2153 break;
2154 }
2155 case kUTF32_Encoding: {
2156 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
2157 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2158 src = utf16;
2159 for (int i = 0; i < glyphCount; ++i) {
2160 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
2161 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002162 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002163 break;
2164 }
2165 }
2166
halcanary96fcdcc2015-08-27 07:41:13 -07002167 // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002168 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002169 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2170 uint16_t* macGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002171 if (nullptr == macGlyphs || srcCount > glyphCount) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002172 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002173 }
2174
bungemanc292b5f2016-12-20 12:04:29 -05002175 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002176
2177 // If there were any non-bmp, then copy and compact.
halcanary96fcdcc2015-08-27 07:41:13 -07002178 // If 'glyphs' is nullptr, then compact glyphStorage in-place.
2179 // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
2180 // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002181 uint16_t* compactedGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002182 if (nullptr == compactedGlyphs) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002183 compactedGlyphs = macGlyphs;
2184 }
2185 if (srcCount > glyphCount) {
2186 int extra = 0;
2187 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002188 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002189 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2190 ++extra;
2191 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002192 }
2193 }
2194
2195 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002196 return glyphCount;
2197 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002198
2199 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002200 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002201 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002202 return i;
2203 }
2204 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002205 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002206 return glyphCount;
2207}
2208
2209int SkTypeface_Mac::onCountGlyphs() const {
bungemanc292b5f2016-12-20 12:04:29 -05002210 return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
reed@google.combcb42ae2013-07-02 13:56:39 +00002211}
2212
reed@google.com95625db2013-03-25 20:44:02 +00002213///////////////////////////////////////////////////////////////////////////////
2214///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002215
2216static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
bungemanc292b5f2016-12-20 12:04:29 -05002217 UniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
2218 if (!ref) {
reed@google.com83787c52013-03-26 17:19:15 +00002219 return false;
2220 }
bungemanc292b5f2016-12-20 12:04:29 -05002221 CFStringToSkString(ref.get(), value);
reed@google.com83787c52013-03-26 17:19:15 +00002222 return true;
2223}
2224
reed@google.com95625db2013-03-25 20:44:02 +00002225#include "SkFontMgr.h"
2226
reed@google.com964988f2013-03-29 14:57:22 +00002227static inline int sqr(int value) {
2228 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2229 return value * value;
2230}
2231
2232// We normalize each axis (weight, width, italic) to be base-900
2233static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2234 return sqr(a.weight() - b.weight()) +
2235 sqr((a.width() - b.width()) * 100) +
bungemanb4bb7d82016-04-27 10:21:04 -07002236 sqr((a.slant() != b.slant()) * 900);
reed@google.com964988f2013-03-29 14:57:22 +00002237}
2238
reed@google.com83787c52013-03-26 17:19:15 +00002239class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002240public:
bungeman53d5c6e2016-04-08 07:22:29 -07002241 SkFontStyleSet_Mac(CTFontDescriptorRef desc)
halcanary96fcdcc2015-08-27 07:41:13 -07002242 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
bungemanc292b5f2016-12-20 12:04:29 -05002243 , fCount(0)
2244 {
2245 if (!fArray) {
2246 fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
reed@google.comdea7ee02013-03-28 14:12:10 +00002247 }
bungemanc292b5f2016-12-20 12:04:29 -05002248 fCount = SkToInt(CFArrayGetCount(fArray.get()));
reed@google.com83787c52013-03-26 17:19:15 +00002249 }
2250
mtklein36352bf2015-03-25 18:17:31 -07002251 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002252 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002253 }
2254
mtklein36352bf2015-03-25 18:17:31 -07002255 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002256 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002257 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002258 if (style) {
bungeman2873c762017-01-13 11:40:21 -05002259 *style = fontstyle_from_descriptor(desc, false);
reed@google.com83787c52013-03-26 17:19:15 +00002260 }
2261 if (name) {
2262 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2263 name->reset();
2264 }
2265 }
2266 }
2267
mtklein36352bf2015-03-25 18:17:31 -07002268 SkTypeface* createTypeface(int index) override {
bungemanc292b5f2016-12-20 12:04:29 -05002269 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
2270 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002271
Mike Reed59227392017-09-26 09:46:08 -04002272 return create_from_desc(desc).release();
reed@google.com83787c52013-03-26 17:19:15 +00002273 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002274
mtklein36352bf2015-03-25 18:17:31 -07002275 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002276 if (0 == fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002277 return nullptr;
reed@google.com964988f2013-03-29 14:57:22 +00002278 }
Mike Reed59227392017-09-26 09:46:08 -04002279 return create_from_desc(findMatchingDesc(pattern)).release();
reed@google.com964988f2013-03-29 14:57:22 +00002280 }
2281
reed@google.com83787c52013-03-26 17:19:15 +00002282private:
bungemanc292b5f2016-12-20 12:04:29 -05002283 UniqueCFRef<CFArrayRef> fArray;
2284 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002285
2286 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2287 int bestMetric = SK_MaxS32;
halcanary96fcdcc2015-08-27 07:41:13 -07002288 CTFontDescriptorRef bestDesc = nullptr;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002289
reed@google.com964988f2013-03-29 14:57:22 +00002290 for (int i = 0; i < fCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002291 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
bungeman2873c762017-01-13 11:40:21 -05002292 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc, false));
reed@google.com964988f2013-03-29 14:57:22 +00002293 if (0 == metric) {
2294 return desc;
2295 }
2296 if (metric < bestMetric) {
2297 bestMetric = metric;
2298 bestDesc = desc;
2299 }
2300 }
2301 SkASSERT(bestDesc);
2302 return bestDesc;
2303 }
reed@google.com83787c52013-03-26 17:19:15 +00002304};
2305
2306class SkFontMgr_Mac : public SkFontMgr {
bungemanc292b5f2016-12-20 12:04:29 -05002307 UniqueCFRef<CFArrayRef> fNames;
2308 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002309
bungemanc292b5f2016-12-20 12:04:29 -05002310 CFStringRef getFamilyNameAt(int index) const {
reed@google.com83787c52013-03-26 17:19:15 +00002311 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002312 return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002313 }
2314
reed@google.com964988f2013-03-29 14:57:22 +00002315 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
bungemanc292b5f2016-12-20 12:04:29 -05002316 UniqueCFRef<CFMutableDictionaryRef> cfAttr(
reed@google.com964988f2013-03-29 14:57:22 +00002317 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2318 &kCFTypeDictionaryKeyCallBacks,
2319 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002320
bungemanc292b5f2016-12-20 12:04:29 -05002321 CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002322
bungemanc292b5f2016-12-20 12:04:29 -05002323 UniqueCFRef<CTFontDescriptorRef> desc(
2324 CTFontDescriptorCreateWithAttributes(cfAttr.get()));
2325 return new SkFontStyleSet_Mac(desc.get());
2326 }
2327
2328 /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
2329 * provide a wrapper here that will return an empty array if need be.
2330 */
2331 static UniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
2332#ifdef SK_BUILD_FOR_IOS
2333 return UniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
2334#else
2335 return UniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
2336#endif
reed@google.com964988f2013-03-29 14:57:22 +00002337 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002338
reed@google.com83787c52013-03-26 17:19:15 +00002339public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002340 SkFontMgr_Mac()
bungemanc292b5f2016-12-20 12:04:29 -05002341 : fNames(CopyAvailableFontFamilyNames())
2342 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0) {}
reed@google.com95625db2013-03-25 20:44:02 +00002343
2344protected:
mtklein36352bf2015-03-25 18:17:31 -07002345 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002346 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002347 }
2348
mtklein36352bf2015-03-25 18:17:31 -07002349 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002350 if ((unsigned)index < (unsigned)fCount) {
bungemanc292b5f2016-12-20 12:04:29 -05002351 CFStringToSkString(this->getFamilyNameAt(index), familyName);
reed@google.com83787c52013-03-26 17:19:15 +00002352 } else {
2353 familyName->reset();
2354 }
reed@google.com95625db2013-03-25 20:44:02 +00002355 }
2356
mtklein36352bf2015-03-25 18:17:31 -07002357 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002358 if ((unsigned)index >= (unsigned)fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002359 return nullptr;
reed@google.com83787c52013-03-26 17:19:15 +00002360 }
bungemanc292b5f2016-12-20 12:04:29 -05002361 return CreateSet(this->getFamilyNameAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002362 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002363
mtklein36352bf2015-03-25 18:17:31 -07002364 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
bungeman7575bb12017-05-01 13:02:42 -04002365 if (!familyName) {
2366 return nullptr;
2367 }
bungemanc292b5f2016-12-20 12:04:29 -05002368 UniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
2369 return CreateSet(cfName.get());
reed@google.com964988f2013-03-29 14:57:22 +00002370 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002371
bungemanc292b5f2016-12-20 12:04:29 -05002372 SkTypeface* onMatchFamilyStyle(const char familyName[],
bungeman7575bb12017-05-01 13:02:42 -04002373 const SkFontStyle& style) const override {
2374 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
Mike Reed59227392017-09-26 09:46:08 -04002375 return create_from_desc(desc.get()).release();
reed@google.com95625db2013-03-25 20:44:02 +00002376 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002377
bungemanc292b5f2016-12-20 12:04:29 -05002378 SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
2379 const SkFontStyle& style,
2380 const char* bcp47[], int bcp47Count,
2381 SkUnichar character) const override {
2382 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
2383 UniqueCFRef<CTFontRef> currentFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -07002384
2385 // kCFStringEncodingUTF32 is BE unless there is a BOM.
2386 // Since there is no machine endian option, explicitly state machine endian.
2387#ifdef SK_CPU_LENDIAN
2388 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
2389#else
2390 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
2391#endif
bungemanc292b5f2016-12-20 12:04:29 -05002392 UniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
bungemanbea97482016-08-24 08:29:50 -07002393 kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
2394 encoding, false));
bungemanc292b5f2016-12-20 12:04:29 -05002395 CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units.
2396 UniqueCFRef<CTFontRef> fallbackFont(
2397 CTFontCreateForString(currentFont.get(), string.get(), range));
Mike Reed59227392017-09-26 09:46:08 -04002398 return create_from_CTFontRef(std::move(fallbackFont), nullptr, false).release();
djsollen33068c12014-11-14 10:52:53 -08002399 }
2400
bungemanc292b5f2016-12-20 12:04:29 -05002401 SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2402 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002403 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002404 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002405
Mike Reed59227392017-09-26 09:46:08 -04002406 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
2407 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromData(std::move(data)));
bungemanc292b5f2016-12-20 12:04:29 -05002408 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002409 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002410 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002411 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002412 }
2413
Mike Reed59227392017-09-26 09:46:08 -04002414 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2415 int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002416 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromStream(std::move(stream)));
2417 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002418 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002419 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002420 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002421 }
2422
Ben Wagnerfc497342017-02-24 11:15:26 -05002423 /** Creates a dictionary suitable for setting the axes on a CGFont. */
2424 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, const SkFontArguments& args) {
2425 // The CGFont variation data is keyed by name, but lacks the tag.
bungemanf6c71072016-01-21 14:17:47 -08002426 // The CTFont variation data is keyed by tag, and also has the name.
Ben Wagnerfc497342017-02-24 11:15:26 -05002427 // We would like to work with CTFont variations, but creating a CTFont font with
bungemanf6c71072016-01-21 14:17:47 -08002428 // CTFont variation dictionary runs into bugs. So use the CTFont variation data
2429 // to match names to tags to create the appropriate CGFont.
bungemanc292b5f2016-12-20 12:04:29 -05002430 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
bungemanbc096bf2017-04-25 13:32:50 -04002431 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
2432 // 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 -05002433 UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct.get()));
Ben Wagnerfc497342017-02-24 11:15:26 -05002434 if (!ctAxes) {
bungemanf6c71072016-01-21 14:17:47 -08002435 return nullptr;
2436 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002437 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
bungemanf6c71072016-01-21 14:17:47 -08002438
Ben Wagnerfc497342017-02-24 11:15:26 -05002439 const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
bungemanf6c71072016-01-21 14:17:47 -08002440
bungemanc292b5f2016-12-20 12:04:29 -05002441 UniqueCFRef<CFMutableDictionaryRef> dict(
2442 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2443 &kCFTypeDictionaryKeyCallBacks,
2444 &kCFTypeDictionaryValueCallBacks));
2445
bungemanf6c71072016-01-21 14:17:47 -08002446 for (int i = 0; i < axisCount; ++i) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002447 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
bungemanf6c71072016-01-21 14:17:47 -08002448 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2449 return nullptr;
2450 }
2451 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2452
Ben Wagnerfc497342017-02-24 11:15:26 -05002453 // The assumption is that values produced by kCTFontVariationAxisNameKey and
2454 // kCGFontVariationAxisName will always be equal.
2455 // If they are ever not, seach the project history for "get_tag_for_name".
2456 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
bungemanf6c71072016-01-21 14:17:47 -08002457 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2458 return nullptr;
2459 }
2460
Ben Wagnerfc497342017-02-24 11:15:26 -05002461 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
2462 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2463 return nullptr;
bungemanf6c71072016-01-21 14:17:47 -08002464 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002465 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
bungemanf6c71072016-01-21 14:17:47 -08002466 int64_t tagLong;
2467 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2468 return nullptr;
2469 }
2470
2471 // The variation axes can be set to any value, but cg will effectively pin them.
2472 // Pin them here to normalize.
Ben Wagnerfc497342017-02-24 11:15:26 -05002473 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
2474 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
2475 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
bungemanf6c71072016-01-21 14:17:47 -08002476 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2477 !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
2478 !def || CFGetTypeID(def) != CFNumberGetTypeID())
2479 {
2480 return nullptr;
2481 }
2482 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2483 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2484 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2485 double minDouble;
2486 double maxDouble;
2487 double defDouble;
2488 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2489 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
2490 !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
2491 {
2492 return nullptr;
2493 }
2494
2495 double value = defDouble;
bungeman9aec8942017-03-29 13:38:53 -04002496 // The position may be over specified. If there are multiple values for a given axis,
2497 // use the last one since that's what css-fonts-4 requires.
2498 for (int j = position.coordinateCount; j --> 0;) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002499 if (position.coordinates[j].axis == tagLong) {
2500 value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
2501 minDouble, maxDouble);
bungemanf6c71072016-01-21 14:17:47 -08002502 break;
2503 }
2504 }
bungemanc292b5f2016-12-20 12:04:29 -05002505 UniqueCFRef<CFNumberRef> valueNumber(
2506 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2507 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungemanf6c71072016-01-21 14:17:47 -08002508 }
bungemanc292b5f2016-12-20 12:04:29 -05002509 return std::move(dict);
bungemanf6c71072016-01-21 14:17:47 -08002510 }
Mike Reed59227392017-09-26 09:46:08 -04002511 sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> s,
2512 const SkFontArguments& args) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002513 if (args.getCollectionIndex() != 0) {
2514 return nullptr;
2515 }
bungemanc292b5f2016-12-20 12:04:29 -05002516 UniqueCFRef<CGDataProviderRef> provider(SkCreateDataProviderFromStream(std::move(s)));
2517 if (!provider) {
bungemanf6c71072016-01-21 14:17:47 -08002518 return nullptr;
2519 }
bungemanc292b5f2016-12-20 12:04:29 -05002520 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2521 if (!cg) {
bungemanf6c71072016-01-21 14:17:47 -08002522 return nullptr;
2523 }
2524
Ben Wagnerfc497342017-02-24 11:15:26 -05002525 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), args);
bungemanf6c71072016-01-21 14:17:47 -08002526 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2527 // created from a data provider does not appear to have any ownership of the underlying
2528 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002529 UniqueCFRef<CGFontRef> cgVariant;
bungemanf6c71072016-01-21 14:17:47 -08002530 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002531 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungemanf6c71072016-01-21 14:17:47 -08002532 } else {
mtklein18300a32016-03-16 13:53:35 -07002533 cgVariant.reset(cg.release());
bungemanf6c71072016-01-21 14:17:47 -08002534 }
2535
bungemanc292b5f2016-12-20 12:04:29 -05002536 UniqueCFRef<CTFontRef> ct(
2537 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungemanf6c71072016-01-21 14:17:47 -08002538 if (!ct) {
2539 return nullptr;
2540 }
bungemanc292b5f2016-12-20 12:04:29 -05002541 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungemanf6c71072016-01-21 14:17:47 -08002542 }
2543
Ben Wagnerfc497342017-02-24 11:15:26 -05002544 /** Creates a dictionary suitable for setting the axes on a CGFont. */
bungemanc292b5f2016-12-20 12:04:29 -05002545 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, SkFontData* fontData) {
2546 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
bungeman41868fe2015-05-20 09:21:04 -07002547 if (!cgAxes) {
halcanary96fcdcc2015-08-27 07:41:13 -07002548 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002549 }
2550
bungemanc292b5f2016-12-20 12:04:29 -05002551 CFIndex axisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07002552 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002553 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002554 }
2555
bungemanc292b5f2016-12-20 12:04:29 -05002556 UniqueCFRef<CFMutableDictionaryRef> dict(
2557 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2558 &kCFTypeDictionaryKeyCallBacks,
2559 &kCFTypeDictionaryValueCallBacks));
2560
bungeman41868fe2015-05-20 09:21:04 -07002561 for (int i = 0; i < fontData->getAxisCount(); ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002562 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07002563 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002564 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002565 }
2566 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2567
2568 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2569 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002570 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002571 }
2572
2573 // The variation axes can be set to any value, but cg will effectively pin them.
2574 // Pin them here to normalize.
2575 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2576 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2577 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2578 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2579 {
halcanary96fcdcc2015-08-27 07:41:13 -07002580 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002581 }
2582 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2583 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2584 double minDouble;
2585 double maxDouble;
2586 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2587 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2588 {
halcanary96fcdcc2015-08-27 07:41:13 -07002589 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002590 }
2591 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
bungemanc292b5f2016-12-20 12:04:29 -05002592 UniqueCFRef<CFNumberRef> valueNumber(
2593 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2594 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungeman41868fe2015-05-20 09:21:04 -07002595 }
bungemanc292b5f2016-12-20 12:04:29 -05002596 return std::move(dict);
bungeman41868fe2015-05-20 09:21:04 -07002597 }
Mike Reed59227392017-09-26 09:46:08 -04002598 sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData> fontData) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002599 if (fontData->getIndex() != 0) {
2600 return nullptr;
2601 }
bungemanc292b5f2016-12-20 12:04:29 -05002602 UniqueCFRef<CGDataProviderRef> provider(
bungemanf93d7112016-09-16 06:24:20 -07002603 SkCreateDataProviderFromStream(fontData->detachStream()));
bungemanc292b5f2016-12-20 12:04:29 -05002604 if (!provider) {
halcanary96fcdcc2015-08-27 07:41:13 -07002605 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002606 }
bungemanc292b5f2016-12-20 12:04:29 -05002607 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2608 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07002609 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002610 }
2611
bungemanc292b5f2016-12-20 12:04:29 -05002612 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), fontData.get());
bungeman41868fe2015-05-20 09:21:04 -07002613 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2614 // created from a data provider does not appear to have any ownership of the underlying
2615 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002616 UniqueCFRef<CGFontRef> cgVariant;
bungeman41868fe2015-05-20 09:21:04 -07002617 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002618 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungeman41868fe2015-05-20 09:21:04 -07002619 } else {
mtklein18300a32016-03-16 13:53:35 -07002620 cgVariant.reset(cg.release());
bungeman41868fe2015-05-20 09:21:04 -07002621 }
2622
bungemanc292b5f2016-12-20 12:04:29 -05002623 UniqueCFRef<CTFontRef> ct(
2624 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07002625 if (!ct) {
halcanary96fcdcc2015-08-27 07:41:13 -07002626 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002627 }
bungemanc292b5f2016-12-20 12:04:29 -05002628 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungeman41868fe2015-05-20 09:21:04 -07002629 }
2630
Mike Reed59227392017-09-26 09:46:08 -04002631 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002632 UniqueCFRef<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2633 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002634 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002635 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002636 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002637 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002638
Mike Reed59227392017-09-26 09:46:08 -04002639 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002640 if (familyName) {
2641 familyName = map_css_names(familyName);
2642 }
2643
Mike Reed59227392017-09-26 09:46:08 -04002644 sk_sp<SkTypeface> face = create_from_name(familyName, style);
bungeman53d5c6e2016-04-08 07:22:29 -07002645 if (face) {
2646 return face;
bungemana4c4a2d2014-10-20 13:33:19 -07002647 }
bungeman53d5c6e2016-04-08 07:22:29 -07002648
bungemanc292b5f2016-12-20 12:04:29 -05002649 static SkTypeface* gDefaultFace;
2650 static SkOnce lookupDefault;
bungeman83f1f442017-02-06 13:21:33 -05002651 static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
bungemanc292b5f2016-12-20 12:04:29 -05002652 lookupDefault([]{
Mike Reed59227392017-09-26 09:46:08 -04002653 gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
bungemanc292b5f2016-12-20 12:04:29 -05002654 });
Mike Reed59227392017-09-26 09:46:08 -04002655 return sk_ref_sp(gDefaultFace);
reed@google.com7fdcd442013-07-30 21:25:49 +00002656 }
reed@google.com95625db2013-03-25 20:44:02 +00002657};
2658
reed@google.com7fdcd442013-07-30 21:25:49 +00002659///////////////////////////////////////////////////////////////////////////////
2660
Ben Wagner3546ff12017-01-03 13:32:36 -05002661sk_sp<SkFontMgr> SkFontMgr::Factory() { return sk_make_sp<SkFontMgr_Mac>(); }
mtklein1ee76512015-11-02 10:20:27 -08002662
2663#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)