blob: 4bfc7fa72abb1f281fbedf11b9579bd4a4c2a8a3 [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
Ben Wagner7b562292018-09-19 22:31:07 -040022#include "mac/SkUniqueCFRef.h"
reed39a9a502015-05-12 09:50:04 -070023#include "SkAdvancedTypefaceMetrics.h"
Hal Canary95e3c052017-01-11 12:44:43 -050024#include "SkAutoMalloc.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000025#include "SkCGUtils.h"
Cary Clarka4083c92017-09-15 11:59:23 -040026#include "SkColorData.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000027#include "SkDescriptor.h"
28#include "SkEndian.h"
jvanverth02802f62015-07-02 06:42:49 -070029#include "SkFloatingPoint.h"
mtklein1b249332015-07-07 12:21:21 -070030#include "SkFontDescriptor.h"
Mike Reed77f94ea2019-01-22 16:30:40 -050031#include "SkFontMetrics.h"
mtklein1b249332015-07-07 12:21:21 -070032#include "SkFontMgr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000033#include "SkGlyph.h"
bungemanf93d7112016-09-16 06:24:20 -070034#include "SkMakeUnique.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000035#include "SkMaskGamma.h"
halcanary4dbbd042016-06-07 17:21:10 -070036#include "SkMathPriv.h"
mtklein1b249332015-07-07 12:21:21 -070037#include "SkMutex.h"
bungeman4ec46aa2017-02-15 17:49:12 -050038#include "SkOTTable_OS_2.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000039#include "SkOTUtils.h"
mtkleinffa4a922016-05-05 16:05:56 -070040#include "SkOnce.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000041#include "SkPaint.h"
42#include "SkPath.h"
mtklein1b249332015-07-07 12:21:21 -070043#include "SkSFNTHeader.h"
jvanverth02802f62015-07-02 06:42:49 -070044#include "SkStream.h"
mtklein1b249332015-07-07 12:21:21 -070045#include "SkString.h"
bungemane280d062016-03-24 11:27:05 -070046#include "SkTemplates.h"
Hal Canaryc640d0d2018-06-13 09:59:02 -040047#include "SkTo.h"
mtklein1b249332015-07-07 12:21:21 -070048#include "SkTypefaceCache.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000049#include "SkTypeface_mac.h"
Ben Wagner7b562292018-09-19 22:31:07 -040050#include "SkUTF.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000051#include "SkUtils.h"
reed@google.comf77b35d2013-05-02 20:39:44 +000052
bungeman3e306f62017-03-29 11:32:02 -040053#include <dlfcn.h>
54
Ben Wagnerf08d1d02018-06-18 15:11:00 -040055#include <utility>
56
reedd0f41732015-07-10 12:08:38 -070057// Experimental code to use a global lock whenever we access CG, to see if this reduces
58// crashes in Chrome
59#define USE_GLOBAL_MUTEX_FOR_CG_ACCESS
60
61#ifdef USE_GLOBAL_MUTEX_FOR_CG_ACCESS
reed086eea92016-05-04 17:12:46 -070062 SK_DECLARE_STATIC_MUTEX(gCGMutex);
reedd0f41732015-07-10 12:08:38 -070063 #define AUTO_CG_LOCK() SkAutoMutexAcquire amc(gCGMutex)
64#else
65 #define AUTO_CG_LOCK()
66#endif
67
bungeman3b4b66c2015-01-08 08:33:44 -080068// Set to make glyph bounding boxes visible.
69#define SK_SHOW_TEXT_BLIT_COVERAGE 0
70
Mike Reed342a9fa2017-05-03 15:48:22 -040071CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
72 return face ? (CTFontRef)face->internal_private_getCTFontRef() : nullptr;
73}
74
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000075class SkScalerContext_Mac;
76
Ben Wagner7b562292018-09-19 22:31:07 -040077static SkUniqueCFRef<CFStringRef> make_CFString(const char s[]) {
78 return SkUniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, s, kCFStringEncodingUTF8));
reed@google.com964988f2013-03-29 14:57:22 +000079}
80
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000081// inline versions of these rect helpers
82
83static bool CGRectIsEmpty_inline(const CGRect& rect) {
84 return rect.size.width <= 0 || rect.size.height <= 0;
85}
86
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000087static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
88 return rect.origin.x;
89}
90
91static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
92 return rect.origin.x + rect.size.width;
93}
94
95static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
96 return rect.origin.y;
97}
98
99static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
100 return rect.origin.y + rect.size.height;
101}
102
103static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
104 return rect.size.width;
105}
106
107///////////////////////////////////////////////////////////////////////////////
108
109static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
reed@google.com7fa2a652014-01-27 13:42:58 +0000110 int width, int height, size_t rowBytes) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000111 SkASSERT(width);
112 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
113
114 if (width >= 32) {
115 while (height) {
116 sk_memset32(ptr, value, width);
117 ptr = (uint32_t*)((char*)ptr + rowBytes);
118 height -= 1;
119 }
120 return;
121 }
122
123 rowBytes -= width * sizeof(uint32_t);
124
125 if (width >= 8) {
126 while (height) {
127 int w = width;
128 do {
129 *ptr++ = value; *ptr++ = value;
130 *ptr++ = value; *ptr++ = value;
131 *ptr++ = value; *ptr++ = value;
132 *ptr++ = value; *ptr++ = value;
133 w -= 8;
134 } while (w >= 8);
135 while (--w >= 0) {
136 *ptr++ = value;
137 }
138 ptr = (uint32_t*)((char*)ptr + rowBytes);
139 height -= 1;
140 }
141 } else {
142 while (height) {
143 int w = width;
144 do {
145 *ptr++ = value;
146 } while (--w > 0);
147 ptr = (uint32_t*)((char*)ptr + rowBytes);
148 height -= 1;
149 }
150 }
151}
152
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000153typedef uint32_t CGRGBPixel;
154
155static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
156 return pixel & 0xFF;
157}
158
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000159static CGFloat ScalarToCG(SkScalar scalar) {
160 if (sizeof(CGFloat) == sizeof(float)) {
161 return SkScalarToFloat(scalar);
162 } else {
163 SkASSERT(sizeof(CGFloat) == sizeof(double));
164 return (CGFloat) SkScalarToDouble(scalar);
165 }
166}
167
168static SkScalar CGToScalar(CGFloat cgFloat) {
169 if (sizeof(CGFloat) == sizeof(float)) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700170 return SkFloatToScalar(cgFloat);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000171 } else {
172 SkASSERT(sizeof(CGFloat) == sizeof(double));
173 return SkDoubleToScalar(cgFloat);
174 }
175}
176
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700177static float CGToFloat(CGFloat cgFloat) {
178 if (sizeof(CGFloat) == sizeof(float)) {
179 return cgFloat;
180 } else {
181 SkASSERT(sizeof(CGFloat) == sizeof(double));
182 return static_cast<float>(cgFloat);
183 }
184}
185
bungemanc292b5f2016-12-20 12:04:29 -0500186static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) {
187 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX]),
188 -ScalarToCG(matrix[SkMatrix::kMSkewY] ),
189 -ScalarToCG(matrix[SkMatrix::kMSkewX] ),
190 ScalarToCG(matrix[SkMatrix::kMScaleY]),
191 ScalarToCG(matrix[SkMatrix::kMTransX]),
192 ScalarToCG(matrix[SkMatrix::kMTransY]));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000193}
194
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000195///////////////////////////////////////////////////////////////////////////////
196
197#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000198
Ben Wagner428ab732018-04-02 15:44:54 -0400199/** Drawn in FontForge, reduced with fonttools ttx, converted by xxd -i,
200 * this TrueType font contains a glyph of the spider.
201 *
202 * To re-forge the original bytes of the TrueType font file,
203 * remove all ',|( +0x)' from this definition,
204 * copy the data to the clipboard,
205 * run 'pbpaste | xxd -p -r - spider.ttf'.
206 */
207static constexpr const uint8_t kSpiderSymbol_ttf[] = {
208 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x80, 0x00, 0x03, 0x00, 0x40,
209 0x47, 0x44, 0x45, 0x46, 0x00, 0x14, 0x00, 0x14, 0x00, 0x00, 0x07, 0xa8,
210 0x00, 0x00, 0x00, 0x18, 0x4f, 0x53, 0x2f, 0x32, 0x8a, 0xf4, 0xfb, 0xdb,
211 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x00, 0x60, 0x63, 0x6d, 0x61, 0x70,
212 0xe0, 0x7f, 0x10, 0x7e, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x54,
213 0x67, 0x61, 0x73, 0x70, 0xff, 0xff, 0x00, 0x03, 0x00, 0x00, 0x07, 0xa0,
214 0x00, 0x00, 0x00, 0x08, 0x67, 0x6c, 0x79, 0x66, 0x97, 0x0b, 0x6a, 0xf6,
215 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x03, 0x40, 0x68, 0x65, 0x61, 0x64,
216 0x0f, 0xa2, 0x24, 0x1a, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x36,
217 0x68, 0x68, 0x65, 0x61, 0x0e, 0xd3, 0x07, 0x3f, 0x00, 0x00, 0x01, 0x04,
218 0x00, 0x00, 0x00, 0x24, 0x68, 0x6d, 0x74, 0x78, 0x10, 0x03, 0x00, 0x44,
219 0x00, 0x00, 0x01, 0xa8, 0x00, 0x00, 0x00, 0x0e, 0x6c, 0x6f, 0x63, 0x61,
220 0x01, 0xb4, 0x00, 0x28, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x0a,
221 0x6d, 0x61, 0x78, 0x70, 0x00, 0x4a, 0x01, 0x4d, 0x00, 0x00, 0x01, 0x28,
222 0x00, 0x00, 0x00, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0xc3, 0xe5, 0x39, 0xd4,
223 0x00, 0x00, 0x05, 0x58, 0x00, 0x00, 0x02, 0x28, 0x70, 0x6f, 0x73, 0x74,
224 0xff, 0x03, 0x00, 0x67, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x20,
225 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0b, 0x0f, 0x08, 0x1d,
226 0x5f, 0x0f, 0x3c, 0xf5, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0xd1, 0x97, 0xa8, 0x5a, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xe8, 0x32, 0x33,
228 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x55, 0x00, 0x00, 0x00, 0x08,
229 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
230 0x05, 0x55, 0xff, 0x3b, 0x01, 0x79, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00,
231 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00,
233 0x00, 0x04, 0x01, 0x1c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
234 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x2e,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x01, 0x90, 0x00, 0x05,
236 0x00, 0x00, 0x05, 0x33, 0x05, 0x99, 0x00, 0x00, 0x01, 0x1e, 0x05, 0x33,
237 0x05, 0x99, 0x00, 0x00, 0x03, 0xd7, 0x00, 0x66, 0x02, 0x12, 0x00, 0x00,
238 0x05, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x73, 0x6b, 0x69, 0x61, 0x00, 0xc0, 0x00, 0x00, 0xf0, 0x21,
241 0x06, 0x66, 0xfe, 0x66, 0x01, 0x79, 0x05, 0x55, 0x00, 0xc5, 0x80, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x20, 0x00, 0x01, 0x08, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00,
244 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
245 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x48,
246 0x00, 0x00, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00,
247 0x00, 0x09, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21, 0xf0, 0x21, 0xff, 0xff,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21,
249 0xf0, 0x21, 0xff, 0xff, 0x00, 0x01, 0xff, 0xf9, 0xff, 0xf5, 0xff, 0xe4,
250 0xff, 0xe2, 0x0f, 0xe2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
252 0x00, 0x14, 0x00, 0x14, 0x01, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x44,
253 0x00, 0x00, 0x02, 0x64, 0x05, 0x55, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00,
254 0x33, 0x11, 0x21, 0x11, 0x25, 0x21, 0x11, 0x21, 0x44, 0x02, 0x20, 0xfe,
255 0x24, 0x01, 0x98, 0xfe, 0x68, 0x05, 0x55, 0xfa, 0xab, 0x44, 0x04, 0xcd,
256 0x00, 0x04, 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x4c, 0x00, 0x15,
257 0x00, 0x1d, 0x00, 0x25, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x36, 0x37, 0x36,
258 0x27, 0x26, 0x07, 0x06, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27, 0x26, 0x07,
259 0x06, 0x17, 0x16, 0x17, 0x16, 0x32, 0x37, 0x32, 0x35, 0x34, 0x23, 0x22,
260 0x15, 0x14, 0x27, 0x32, 0x35, 0x34, 0x23, 0x22, 0x15, 0x14, 0x03, 0x32,
261 0x17, 0x30, 0x17, 0x31, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33,
262 0x32, 0x33, 0x16, 0x33, 0x32, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27,
263 0x26, 0x27, 0x26, 0x23, 0x22, 0x07, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
264 0x1f, 0x02, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32, 0x17, 0x17, 0x16, 0x33,
265 0x16, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27, 0x27, 0x26, 0x23, 0x22,
266 0x07, 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x33, 0x32, 0x33, 0x32,
267 0x37, 0x36, 0x37, 0x36, 0x17, 0x16, 0x1f, 0x02, 0x16, 0x17, 0x16, 0x15,
268 0x14, 0x23, 0x22, 0x27, 0x27, 0x26, 0x27, 0x27, 0x26, 0x27, 0x26, 0x07,
269 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x07,
270 0x06, 0x23, 0x22, 0x27, 0x26, 0x07, 0x06, 0x07, 0x06, 0x15, 0x14, 0x17,
271 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27,
272 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x34, 0x27, 0x26, 0x07,
273 0x06, 0x07, 0x06, 0x0f, 0x02, 0x06, 0x23, 0x22, 0x27, 0x26, 0x35, 0x34,
274 0x37, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x27, 0x26, 0x27,
275 0x26, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x07, 0x06, 0x23, 0x22,
276 0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x37, 0x36, 0x37, 0x37, 0x36,
277 0x37, 0x36, 0x37, 0x36, 0x35, 0x34, 0x27, 0x26, 0x27, 0x26, 0x27, 0x26,
278 0x23, 0x22, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x27, 0x26, 0x27, 0x26,
279 0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32,
280 0x17, 0x16, 0x33, 0x32, 0x37, 0x36, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36,
281 0x33, 0x04, 0xf5, 0x23, 0x13, 0x11, 0x14, 0x16, 0x1d, 0x1b, 0x4c, 0x1f,
282 0x0e, 0x2d, 0x23, 0x14, 0x2c, 0x13, 0x18, 0x25, 0x2c, 0x10, 0x3c, 0x71,
283 0x1d, 0x5c, 0x5c, 0x3f, 0xae, 0x5c, 0x5c, 0x3f, 0x6a, 0x27, 0x31, 0x5b,
284 0x09, 0x27, 0x36, 0x03, 0x0a, 0x26, 0x35, 0x2e, 0x09, 0x08, 0xc6, 0x13,
285 0x81, 0x17, 0x20, 0x18, 0x21, 0x1e, 0x04, 0x04, 0x15, 0x5c, 0x22, 0x26,
286 0x48, 0x56, 0x3b, 0x10, 0x21, 0x01, 0x0c, 0x06, 0x06, 0x0f, 0x31, 0x44,
287 0x3c, 0x52, 0x4a, 0x1d, 0x11, 0x3f, 0xb4, 0x71, 0x01, 0x26, 0x06, 0x0d,
288 0x15, 0x1a, 0x2a, 0x13, 0x53, 0xaa, 0x42, 0x1d, 0x0a, 0x33, 0x20, 0x21,
289 0x2b, 0x01, 0x02, 0x3e, 0x21, 0x09, 0x02, 0x02, 0x0f, 0x2d, 0x4b, 0x0a,
290 0x22, 0x15, 0x20, 0x1f, 0x72, 0x8b, 0x2d, 0x2f, 0x1d, 0x1f, 0x0e, 0x25,
291 0x3f, 0x4d, 0x1b, 0x63, 0x2a, 0x2c, 0x14, 0x22, 0x18, 0x1c, 0x0f, 0x08,
292 0x2a, 0x08, 0x08, 0x0d, 0x3b, 0x4c, 0x52, 0x74, 0x27, 0x71, 0x2e, 0x01,
293 0x0c, 0x10, 0x15, 0x0d, 0x06, 0x0d, 0x05, 0x01, 0x06, 0x2c, 0x28, 0x14,
294 0x1b, 0x05, 0x04, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x16, 0x27, 0x03, 0x0d,
295 0x30, 0x4c, 0x4c, 0x4b, 0x1f, 0x0b, 0x22, 0x26, 0x0d, 0x15, 0x0d, 0x2d,
296 0x68, 0x34, 0x14, 0x3c, 0x25, 0x12, 0x04, 0x10, 0x18, 0x0b, 0x09, 0x30,
297 0x2b, 0x44, 0x66, 0x14, 0x47, 0x47, 0x59, 0x73, 0x25, 0x05, 0x03, 0x1f,
298 0x01, 0x08, 0x3f, 0x48, 0x4b, 0x4b, 0x76, 0x2f, 0x49, 0x2d, 0x22, 0x24,
299 0x0c, 0x15, 0x08, 0x0e, 0x33, 0x03, 0x44, 0x4c, 0x10, 0x46, 0x13, 0x1f,
300 0x27, 0x1b, 0x1d, 0x13, 0x02, 0x24, 0x08, 0x02, 0x42, 0x0e, 0x4d, 0x3c,
301 0x19, 0x1b, 0x40, 0x2b, 0x2b, 0x1e, 0x16, 0x11, 0x04, 0x1f, 0x11, 0x04,
302 0x18, 0x11, 0x35, 0x01, 0xa3, 0x13, 0x24, 0x1f, 0x0b, 0x0c, 0x19, 0x19,
303 0x18, 0x13, 0x0f, 0x0c, 0x1a, 0x18, 0x1f, 0x19, 0x1e, 0x07, 0x1a, 0xc3,
304 0x54, 0x51, 0x54, 0x51, 0x04, 0x53, 0x51, 0x54, 0x50, 0x02, 0x48, 0x1a,
305 0x31, 0x18, 0x55, 0x74, 0x04, 0x0e, 0x09, 0x0d, 0x06, 0x10, 0x16, 0x1b,
306 0x24, 0x01, 0x04, 0x0b, 0x04, 0x10, 0x3f, 0x0a, 0x41, 0x02, 0x41, 0x20,
307 0x06, 0x12, 0x16, 0x21, 0x17, 0x2a, 0x1e, 0x15, 0x40, 0x27, 0x11, 0x0e,
308 0x1e, 0x11, 0x15, 0x1f, 0x43, 0x13, 0x1a, 0x10, 0x15, 0x1b, 0x04, 0x09,
309 0x4d, 0x2a, 0x0f, 0x19, 0x0a, 0x0a, 0x03, 0x05, 0x15, 0x3c, 0x64, 0x21,
310 0x4b, 0x2e, 0x21, 0x28, 0x13, 0x47, 0x44, 0x19, 0x3f, 0x11, 0x18, 0x0b,
311 0x0a, 0x07, 0x18, 0x0d, 0x07, 0x24, 0x2c, 0x2b, 0x21, 0x32, 0x10, 0x48,
312 0x2a, 0x2d, 0x1e, 0x1a, 0x01, 0x0c, 0x43, 0x59, 0x28, 0x4e, 0x1c, 0x0d,
313 0x5d, 0x24, 0x14, 0x0a, 0x05, 0x1f, 0x24, 0x32, 0x46, 0x3e, 0x5f, 0x3e,
314 0x44, 0x1a, 0x30, 0x15, 0x0d, 0x07, 0x18, 0x2b, 0x03, 0x0d, 0x1a, 0x28,
315 0x28, 0x57, 0xb2, 0x29, 0x27, 0x40, 0x2c, 0x23, 0x16, 0x63, 0x58, 0x1a,
316 0x0a, 0x18, 0x11, 0x23, 0x08, 0x1b, 0x29, 0x05, 0x04, 0x0b, 0x15, 0x0d,
317 0x14, 0x0b, 0x2a, 0x29, 0x5a, 0x62, 0x01, 0x19, 0x1e, 0x05, 0x05, 0x26,
318 0x42, 0x42, 0x2a, 0x2a, 0x3f, 0x0d, 0x0f, 0x09, 0x05, 0x07, 0x01, 0x0b,
319 0x25, 0x3e, 0x0d, 0x17, 0x11, 0x01, 0x03, 0x0d, 0x13, 0x20, 0x19, 0x11,
320 0x03, 0x02, 0x01, 0x04, 0x11, 0x04, 0x05, 0x1b, 0x3d, 0x10, 0x29, 0x20,
321 0x04, 0x04, 0x0a, 0x07, 0x04, 0x1f, 0x15, 0x20, 0x3e, 0x0f, 0x2a, 0x1e,
322 0x00, 0x00, 0x00, 0x1b, 0x01, 0x4a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
323 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
324 0x00, 0x01, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
325 0x00, 0x02, 0x00, 0x07, 0x00, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
326 0x00, 0x03, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
327 0x00, 0x04, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
328 0x00, 0x05, 0x00, 0x02, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
329 0x00, 0x06, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
330 0x00, 0x0d, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
331 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
332 0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
333 0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
334 0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
335 0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
336 0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
337 0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
338 0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
339 0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
340 0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
341 0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
342 0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
343 0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
344 0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
345 0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
346 0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
347 0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
348 0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
349 0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69,
350 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x35,
351 0x2c, 0x20, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x53, 0x70, 0x69,
352 0x64, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x52, 0x65, 0x67,
353 0x75, 0x6c, 0x61, 0x72, 0x56, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
354 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x2e, 0x73, 0x69, 0x6c,
355 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x4f, 0x46, 0x4c, 0x00, 0x43, 0x00, 0x6f,
356 0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68,
357 0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20,
358 0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x20,
359 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65,
360 0x00, 0x2e, 0x00, 0x53, 0x00, 0x70, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65,
361 0x00, 0x72, 0x00, 0x53, 0x00, 0x79, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x6f,
362 0x00, 0x6c, 0x00, 0x52, 0x00, 0x65, 0x00, 0x67, 0x00, 0x75, 0x00, 0x6c,
363 0x00, 0x61, 0x00, 0x72, 0x00, 0x56, 0x00, 0x31, 0x00, 0x68, 0x00, 0x74,
364 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x73,
365 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70, 0x00, 0x74, 0x00, 0x73,
366 0x00, 0x2e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x2e, 0x00, 0x6f,
367 0x00, 0x72, 0x00, 0x67, 0x00, 0x2f, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x4c,
368 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x66,
369 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
370 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
371 0xff, 0xff, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
372 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
373 0x00, 0x02, 0x00, 0x00
374};
375
Ben Wagner6112f0b2018-09-27 15:44:56 -0400376enum class SmoothBehavior {
377 none, // SmoothFonts produces no effect.
378 some, // SmoothFonts produces some effect, but not subpixel coverage.
379 subpixel, // SmoothFonts produces some effect and provides subpixel coverage.
380};
381
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000382/**
383 * There does not appear to be a publicly accessable API for determining if lcd
384 * font smoothing will be applied if we request it. The main issue is that if
385 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
386 */
Ben Wagner6112f0b2018-09-27 15:44:56 -0400387static SmoothBehavior smooth_behavior() {
388 static SmoothBehavior gSmoothBehavior = []{
389 uint32_t noSmoothBitmap[16][16] = {};
390 uint32_t smoothBitmap[16][16] = {};
391
Ben Wagner7b562292018-09-19 22:31:07 -0400392 SkUniqueCFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
Ben Wagner6112f0b2018-09-27 15:44:56 -0400393 SkUniqueCFRef<CGContextRef> noSmoothContext(
394 CGBitmapContextCreate(&noSmoothBitmap, 16, 16, 8, 16*4,
395 colorspace.get(), BITMAP_INFO_RGB));
396 SkUniqueCFRef<CGContextRef> smoothContext(
397 CGBitmapContextCreate(&smoothBitmap, 16, 16, 8, 16*4,
398 colorspace.get(), BITMAP_INFO_RGB));
Ben Wagner428ab732018-04-02 15:44:54 -0400399
Ben Wagner7b562292018-09-19 22:31:07 -0400400 SkUniqueCFRef<CGDataProviderRef> data(
Ben Wagner428ab732018-04-02 15:44:54 -0400401 CGDataProviderCreateWithData(nullptr, kSpiderSymbol_ttf,
402 SK_ARRAY_COUNT(kSpiderSymbol_ttf), nullptr));
Ben Wagner7b562292018-09-19 22:31:07 -0400403 SkUniqueCFRef<CGFontRef> cgFont(CGFontCreateWithDataProvider(data.get()));
Ben Wagner428ab732018-04-02 15:44:54 -0400404 SkASSERT(cgFont);
Ben Wagner7b562292018-09-19 22:31:07 -0400405 SkUniqueCFRef<CTFontRef> ctFont(
Ben Wagner428ab732018-04-02 15:44:54 -0400406 CTFontCreateWithGraphicsFont(cgFont.get(), 16, nullptr, nullptr));
407 SkASSERT(ctFont);
408
Ben Wagner6112f0b2018-09-27 15:44:56 -0400409 CGContextSetShouldSmoothFonts(noSmoothContext.get(), false);
410 CGContextSetShouldAntialias(noSmoothContext.get(), true);
411 CGContextSetTextDrawingMode(noSmoothContext.get(), kCGTextFill);
412 CGContextSetGrayFillColor(noSmoothContext.get(), 1, 1);
413
414 CGContextSetShouldSmoothFonts(smoothContext.get(), true);
415 CGContextSetShouldAntialias(smoothContext.get(), true);
416 CGContextSetTextDrawingMode(smoothContext.get(), kCGTextFill);
417 CGContextSetGrayFillColor(smoothContext.get(), 1, 1);
418
Ben Wagner428ab732018-04-02 15:44:54 -0400419 CGPoint point = CGPointMake(0, 3);
420 CGGlyph spiderGlyph = 3;
Ben Wagner6112f0b2018-09-27 15:44:56 -0400421 CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, noSmoothContext.get());
422 CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, smoothContext.get());
ccameronf8ee5b42016-10-04 15:02:02 -0700423
Ben Wagner428ab732018-04-02 15:44:54 -0400424 // For debugging.
Ben Wagner6112f0b2018-09-27 15:44:56 -0400425 //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(noSmoothContext()));
426 //SkUniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(smoothContext()));
Ben Wagner428ab732018-04-02 15:44:54 -0400427
Ben Wagner6112f0b2018-09-27 15:44:56 -0400428 SmoothBehavior smoothBehavior = SmoothBehavior::none;
Ben Wagner428ab732018-04-02 15:44:54 -0400429 for (int x = 0; x < 16; ++x) {
430 for (int y = 0; y < 16; ++y) {
Ben Wagner6112f0b2018-09-27 15:44:56 -0400431 uint32_t smoothPixel = smoothBitmap[x][y];
432 uint32_t r = (smoothPixel >> 16) & 0xFF;
433 uint32_t g = (smoothPixel >> 8) & 0xFF;
434 uint32_t b = (smoothPixel >> 0) & 0xFF;
Ben Wagner428ab732018-04-02 15:44:54 -0400435 if (r != g || r != b) {
Ben Wagner6112f0b2018-09-27 15:44:56 -0400436 return SmoothBehavior::subpixel;
437 }
438 if (noSmoothBitmap[x][y] != smoothPixel) {
439 smoothBehavior = SmoothBehavior::some;
Ben Wagner428ab732018-04-02 15:44:54 -0400440 }
441 }
442 }
Ben Wagner6112f0b2018-09-27 15:44:56 -0400443 return smoothBehavior;
Mike Klein70284432017-09-19 13:07:27 -0400444 }();
Ben Wagner6112f0b2018-09-27 15:44:56 -0400445 return gSmoothBehavior;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000446}
447
448class Offscreen {
449public:
bungeman34902632014-12-10 21:43:27 -0800450 Offscreen()
halcanary96fcdcc2015-08-27 07:41:13 -0700451 : fRGBSpace(nullptr)
452 , fCG(nullptr)
bungeman34902632014-12-10 21:43:27 -0800453 , fDoAA(false)
454 , fDoLCD(false)
455 {
456 fSize.set(0, 0);
457 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000458
459 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
bungemanc292b5f2016-12-20 12:04:29 -0500460 CGGlyph glyphID, size_t* rowBytesPtr, bool generateA8FromLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000461
462private:
463 enum {
464 kSize = 32 * 32 * sizeof(CGRGBPixel)
465 };
466 SkAutoSMalloc<kSize> fImageStorage;
Ben Wagner7b562292018-09-19 22:31:07 -0400467 SkUniqueCFRef<CGColorSpaceRef> fRGBSpace;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000468
469 // cached state
Ben Wagner7b562292018-09-19 22:31:07 -0400470 SkUniqueCFRef<CGContextRef> fCG;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000471 SkISize fSize;
472 bool fDoAA;
473 bool fDoLCD;
474
475 static int RoundSize(int dimension) {
476 return SkNextPow2(dimension);
477 }
478};
479
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000480///////////////////////////////////////////////////////////////////////////////
481
bungeman6e45bda2016-07-25 15:11:49 -0700482static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
bungemana4c4a2d2014-10-20 13:33:19 -0700483 CFNumberRef num;
484 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
bungemanc292b5f2016-12-20 12:04:29 -0500485 && CFNumberIsFloatType(num)
486 && CFNumberGetValue(num, kCFNumberCGFloatType, value);
bungemana4c4a2d2014-10-20 13:33:19 -0700487}
488
bungeman6e45bda2016-07-25 15:11:49 -0700489template <typename S, typename D, typename C> struct LinearInterpolater {
490 struct Mapping {
491 S src_val;
492 D dst_val;
493 };
494 constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
495 : fMapping(mapping), fMappingCount(mappingCount) {}
496
497 static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
498 SkASSERT(src_min < src_max);
499 SkASSERT(dst_min <= dst_max);
500 return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000501 }
bungeman6e45bda2016-07-25 15:11:49 -0700502
bungemanf1ef1fa2017-02-09 14:23:46 -0500503 D map(S val) const {
bungeman6e45bda2016-07-25 15:11:49 -0700504 // -Inf to [0]
505 if (val < fMapping[0].src_val) {
506 return fMapping[0].dst_val;
507 }
508
509 // Linear from [i] to [i+1]
510 for (int i = 0; i < fMappingCount - 1; ++i) {
511 if (val < fMapping[i+1].src_val) {
512 return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
513 fMapping[i].dst_val, fMapping[i+1].dst_val);
514 }
515 }
516
517 // From [n] to +Inf
518 // if (fcweight < Inf)
519 return fMapping[fMappingCount - 1].dst_val;
520 }
521
522 Mapping const * fMapping;
523 int fMappingCount;
524};
525
526struct RoundCGFloatToInt {
527 int operator()(CGFloat s) { return s + 0.5; }
528};
bungemanf1ef1fa2017-02-09 14:23:46 -0500529struct CGFloatIdentity {
530 CGFloat operator()(CGFloat s) { return s; }
531};
532
bungeman3e306f62017-03-29 11:32:02 -0400533/** Returns the [-1, 1] CTFontDescriptor weights for the
534 * <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
535 *
536 * It is assumed that the values will be interpolated linearly between these points.
537 * NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
538 * The actual values appear to be stable, but they may change in the future without notice.
539 */
540static CGFloat(&get_NSFontWeight_mapping())[11] {
541
542 // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
543#ifdef SK_BUILD_FOR_MAC
544# define SK_KIT_FONT_WEIGHT_PREFIX "NS"
545#endif
546#ifdef SK_BUILD_FOR_IOS
547# define SK_KIT_FONT_WEIGHT_PREFIX "UI"
548#endif
549 static constexpr struct {
550 CGFloat defaultValue;
551 const char* name;
552 } nsFontWeightLoaderInfos[] = {
553 { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
554 { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
555 { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
556 { 0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
557 { 0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
558 { 0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
559 { 0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
560 { 0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
561 { 0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
562 };
563
564 static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
565 static CGFloat nsFontWeights[11];
566 static SkOnce once;
567 once([&] {
568 size_t i = 0;
569 nsFontWeights[i++] = -1.00;
570 for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
571 void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
572 if (nsFontWeightValuePtr) {
573 nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
574 } else {
575 nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
576 }
577 }
578 nsFontWeights[i++] = 1.00;
579 });
580 return nsFontWeights;
581}
582
bungemanf1ef1fa2017-02-09 14:23:46 -0500583/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
584 *
585 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
586 * CTFont is native or created from a CGDataProvider.
587 */
588static CGFloat fontstyle_to_ct_weight(int fontstyleWeight) {
589 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
590
591 // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
592 // However, on this end we can't tell, so this is ignored.
593
bungeman3e306f62017-03-29 11:32:02 -0400594 static Interpolator::Mapping nativeWeightMappings[11];
595 static SkOnce once;
596 once([&] {
597 CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
598 for (int i = 0; i < 11; ++i) {
599 nativeWeightMappings[i].src_val = i * 100;
600 nativeWeightMappings[i].dst_val = nsFontWeights[i];
601 }
602 });
bungemanf1ef1fa2017-02-09 14:23:46 -0500603 static constexpr Interpolator nativeInterpolator(
604 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
605
606 return nativeInterpolator.map(fontstyleWeight);
607}
608
bungeman6e45bda2016-07-25 15:11:49 -0700609
bungeman2873c762017-01-13 11:40:21 -0500610/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
611 *
612 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
613 * CTFont is native or created from a CGDataProvider.
614 */
615static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
bungeman6e45bda2016-07-25 15:11:49 -0700616 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
617
bungeman6e45bda2016-07-25 15:11:49 -0700618 // 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 -0500619 // However, on this end we can't tell, so this is ignored.
620
621 /** This mapping for CGDataProvider created fonts is determined by creating font data with every
622 * weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
623 * in tests/TypefaceTest.cpp for the code used to determine these values.
624 */
625 static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
bungeman6e45bda2016-07-25 15:11:49 -0700626 { -1.00, 0 },
627 { -0.70, 100 },
628 { -0.50, 200 },
629 { -0.23, 300 },
630 { 0.00, 400 },
631 { 0.20, 500 },
632 { 0.30, 600 },
633 { 0.40, 700 },
634 { 0.60, 800 },
635 { 0.80, 900 },
636 { 1.00, 1000 },
637 };
bungeman2873c762017-01-13 11:40:21 -0500638 static constexpr Interpolator dataProviderInterpolator(
639 dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
640
bungeman3e306f62017-03-29 11:32:02 -0400641 static Interpolator::Mapping nativeWeightMappings[11];
642 static SkOnce once;
643 once([&] {
644 CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
645 for (int i = 0; i < 11; ++i) {
646 nativeWeightMappings[i].src_val = nsFontWeights[i];
647 nativeWeightMappings[i].dst_val = i * 100;
648 }
649 });
bungeman2873c762017-01-13 11:40:21 -0500650 static constexpr Interpolator nativeInterpolator(
651 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
652
653 return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
654 : nativeInterpolator.map(cgWeight);
bungemana4c4a2d2014-10-20 13:33:19 -0700655}
656
bungemanf1ef1fa2017-02-09 14:23:46 -0500657/** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
658static int fontstyle_to_ct_width(int fontstyleWidth) {
659 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
660
661 // Values determined by creating font data with every width, creating a CTFont,
662 // and asking the CTFont for its width. See TypefaceStyle test for basics.
663 static constexpr Interpolator::Mapping widthMappings[] = {
664 { 0, -0.5 },
665 { 10, 0.5 },
666 };
667 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
668 return interpolator.map(fontstyleWidth);
669}
670
671/** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
bungeman6e45bda2016-07-25 15:11:49 -0700672static int ct_width_to_fontstyle(CGFloat cgWidth) {
673 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
674
675 // Values determined by creating font data with every width, creating a CTFont,
676 // and asking the CTFont for its width. See TypefaceStyle test for basics.
677 static constexpr Interpolator::Mapping widthMappings[] = {
678 { -0.5, 0 },
679 { 0.5, 10 },
680 };
bungeman2873c762017-01-13 11:40:21 -0500681 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
682 return interpolator.map(cgWidth);
bungemana4c4a2d2014-10-20 13:33:19 -0700683}
684
bungeman2873c762017-01-13 11:40:21 -0500685static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc, bool fromDataProvider) {
Ben Wagner7b562292018-09-19 22:31:07 -0400686 SkUniqueCFRef<CFTypeRef> traits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
687 if (!traits || CFDictionaryGetTypeID() != CFGetTypeID(traits.get())) {
bungemana4c4a2d2014-10-20 13:33:19 -0700688 return SkFontStyle();
689 }
Ben Wagner7b562292018-09-19 22:31:07 -0400690 SkUniqueCFRef<CFDictionaryRef> fontTraitsDict(static_cast<CFDictionaryRef>(traits.release()));
bungemana4c4a2d2014-10-20 13:33:19 -0700691
bungeman6e45bda2016-07-25 15:11:49 -0700692 CGFloat weight, width, slant;
bungemanc292b5f2016-12-20 12:04:29 -0500693 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800694 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700695 }
bungemanc292b5f2016-12-20 12:04:29 -0500696 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWidthTrait, &width)) {
bungemana4c4a2d2014-10-20 13:33:19 -0700697 width = 0;
698 }
bungemanc292b5f2016-12-20 12:04:29 -0500699 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800700 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700701 }
702
bungeman2873c762017-01-13 11:40:21 -0500703 return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
bungeman6e45bda2016-07-25 15:11:49 -0700704 ct_width_to_fontstyle(width),
bungemana4c4a2d2014-10-20 13:33:19 -0700705 slant ? SkFontStyle::kItalic_Slant
bungemanb4bb7d82016-04-27 10:21:04 -0700706 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000707}
708
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000709class SkTypeface_Mac : public SkTypeface {
710public:
Ben Wagner7b562292018-09-19 22:31:07 -0400711 SkTypeface_Mac(SkUniqueCFRef<CTFontRef> fontRef, SkUniqueCFRef<CFTypeRef> resourceRef,
bungeman78884012015-06-08 13:39:12 -0700712 const SkFontStyle& fs, bool isFixedPitch,
bungeman53d5c6e2016-04-08 07:22:29 -0700713 bool isLocalStream)
bungemane3aea102016-07-13 05:16:58 -0700714 : SkTypeface(fs, isFixedPitch)
bungemanc292b5f2016-12-20 12:04:29 -0500715 , fFontRef(std::move(fontRef))
716 , fOriginatingCFTypeRef(std::move(resourceRef))
717 , fHasColorGlyphs(
718 SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
caseq26337e92014-06-30 12:14:52 -0700719 , fIsLocalStream(isLocalStream)
reed@google.comce8b3de2013-03-26 19:30:16 +0000720 {
bungemanc292b5f2016-12-20 12:04:29 -0500721 SkASSERT(fFontRef);
reed@google.comce8b3de2013-03-26 19:30:16 +0000722 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000723
Ben Wagner7b562292018-09-19 22:31:07 -0400724 SkUniqueCFRef<CTFontRef> fFontRef;
725 SkUniqueCFRef<CFTypeRef> fOriginatingCFTypeRef;
bungemane3bea5c2015-04-07 07:34:36 -0700726 const bool fHasColorGlyphs;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000727
728protected:
mtklein36352bf2015-03-25 18:17:31 -0700729 int onGetUPEM() const override;
730 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
bungemanf93d7112016-09-16 06:24:20 -0700731 std::unique_ptr<SkFontData> onMakeFontData() const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500732 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
733 int coordinateCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700734 void onGetFamilyName(SkString* familyName) const override;
735 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
736 int onGetTableTags(SkFontTableTag tags[]) const override;
bungemanc292b5f2016-12-20 12:04:29 -0500737 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
reeda9322c22016-04-12 06:47:05 -0700738 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
739 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700740 void onFilterRec(SkScalerContextRec*) const override;
741 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
Hal Canary46cc3da2018-05-09 11:50:34 -0400742 void getGlyphToUnicodeMap(SkUnichar*) const override;
Hal Canary209e4b12017-05-04 14:23:55 -0400743 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
bungemanc292b5f2016-12-20 12:04:29 -0500744 int onCharsToGlyphs(const void* chars, Encoding,
745 uint16_t glyphs[], int glyphCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700746 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000747
Mike Reed342a9fa2017-05-03 15:48:22 -0400748 void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
749
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000750private:
caseq26337e92014-06-30 12:14:52 -0700751 bool fIsLocalStream;
reed@google.comce8b3de2013-03-26 19:30:16 +0000752
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000753 typedef SkTypeface INHERITED;
754};
755
bungeman82a455f2016-04-14 08:04:45 -0700756static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
bungeman64fb51d2015-05-04 12:03:50 -0700757 CTFontRef self = (CTFontRef)context;
Mike Reed342a9fa2017-05-03 15:48:22 -0400758 CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000759
bungeman64fb51d2015-05-04 12:03:50 -0700760 return CFEqual(self, other);
761}
762
bungemanc292b5f2016-12-20 12:04:29 -0500763/** Creates a typeface, searching the cache if isLocalStream is false. */
Ben Wagner7b562292018-09-19 22:31:07 -0400764static sk_sp<SkTypeface> create_from_CTFontRef(SkUniqueCFRef<CTFontRef> font,
765 SkUniqueCFRef<CFTypeRef> resource,
Mike Reed59227392017-09-26 09:46:08 -0400766 bool isLocalStream) {
bungemanc292b5f2016-12-20 12:04:29 -0500767 SkASSERT(font);
bungemanbea97482016-08-24 08:29:50 -0700768
769 if (!isLocalStream) {
770 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)font.get());
771 if (face) {
Mike Reed59227392017-09-26 09:46:08 -0400772 return sk_sp<SkTypeface>(face);
bungemanbea97482016-08-24 08:29:50 -0700773 }
774 }
775
Ben Wagner7b562292018-09-19 22:31:07 -0400776 SkUniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
bungeman2873c762017-01-13 11:40:21 -0500777 SkFontStyle style = fontstyle_from_descriptor(desc.get(), isLocalStream);
bungemanc292b5f2016-12-20 12:04:29 -0500778 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
bungemanbea97482016-08-24 08:29:50 -0700779 bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
780
bungemanc292b5f2016-12-20 12:04:29 -0500781 SkTypeface* face = new SkTypeface_Mac(std::move(font), std::move(resource),
bungemanbea97482016-08-24 08:29:50 -0700782 style, isFixedPitch, isLocalStream);
783 if (!isLocalStream) {
784 SkTypefaceCache::Add(face);
785 }
Mike Reed59227392017-09-26 09:46:08 -0400786 return sk_sp<SkTypeface>(face);
bungemanbea97482016-08-24 08:29:50 -0700787}
788
789/** Creates a typeface from a descriptor, searching the cache. */
Mike Reed59227392017-09-26 09:46:08 -0400790static sk_sp<SkTypeface> create_from_desc(CTFontDescriptorRef desc) {
Ben Wagner7b562292018-09-19 22:31:07 -0400791 SkUniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -0700792 if (!ctFont) {
793 return nullptr;
794 }
795
bungemanc292b5f2016-12-20 12:04:29 -0500796 return create_from_CTFontRef(std::move(ctFont), nullptr, false);
bungemanbea97482016-08-24 08:29:50 -0700797}
798
Ben Wagner7b562292018-09-19 22:31:07 -0400799static SkUniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
800 const SkFontStyle& style) {
801 SkUniqueCFRef<CFMutableDictionaryRef> cfAttributes(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000802 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
803 &kCFTypeDictionaryKeyCallBacks,
804 &kCFTypeDictionaryValueCallBacks));
805
Ben Wagner7b562292018-09-19 22:31:07 -0400806 SkUniqueCFRef<CFMutableDictionaryRef> cfTraits(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000807 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
808 &kCFTypeDictionaryKeyCallBacks,
809 &kCFTypeDictionaryValueCallBacks));
810
bungeman83f1f442017-02-06 13:21:33 -0500811 if (!cfAttributes || !cfTraits) {
halcanary96fcdcc2015-08-27 07:41:13 -0700812 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000813 }
814
bungemanf1ef1fa2017-02-09 14:23:46 -0500815 // CTFontTraits (symbolic)
Ben Wagner9f9c2ea2018-10-01 16:04:09 -0400816 // macOS 14 and iOS 12 seem to behave badly when kCTFontSymbolicTrait is set.
817
bungemanf1ef1fa2017-02-09 14:23:46 -0500818 // CTFontTraits (weight)
819 CGFloat ctWeight = fontstyle_to_ct_weight(style.weight());
Ben Wagner7b562292018-09-19 22:31:07 -0400820 SkUniqueCFRef<CFNumberRef> cfFontWeight(
bungemanf1ef1fa2017-02-09 14:23:46 -0500821 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
822 if (cfFontWeight) {
823 CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
824 }
825 // CTFontTraits (width)
Ben Wagner9df43922018-11-26 14:46:13 -0500826 CGFloat ctWidth = fontstyle_to_ct_width(style.width());
Ben Wagner7b562292018-09-19 22:31:07 -0400827 SkUniqueCFRef<CFNumberRef> cfFontWidth(
bungemanf1ef1fa2017-02-09 14:23:46 -0500828 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
829 if (cfFontWidth) {
830 CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
831 }
832 // CTFontTraits (slant)
833 CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1;
Ben Wagner7b562292018-09-19 22:31:07 -0400834 SkUniqueCFRef<CFNumberRef> cfFontSlant(
bungemanf1ef1fa2017-02-09 14:23:46 -0500835 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
836 if (cfFontSlant) {
837 CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
838 }
839 // CTFontTraits
840 CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
bungeman83f1f442017-02-06 13:21:33 -0500841
842 // CTFontFamilyName
843 if (familyName) {
Ben Wagner7b562292018-09-19 22:31:07 -0400844 SkUniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
bungeman83f1f442017-02-06 13:21:33 -0500845 if (cfFontName) {
846 CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
847 }
848 }
bungeman64fb51d2015-05-04 12:03:50 -0700849
Ben Wagner7b562292018-09-19 22:31:07 -0400850 return SkUniqueCFRef<CTFontDescriptorRef>(
bungemanc292b5f2016-12-20 12:04:29 -0500851 CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
bungemanbea97482016-08-24 08:29:50 -0700852}
853
854/** Creates a typeface from a name, searching the cache. */
Mike Reed59227392017-09-26 09:46:08 -0400855static sk_sp<SkTypeface> create_from_name(const char familyName[], const SkFontStyle& style) {
Ben Wagner7b562292018-09-19 22:31:07 -0400856 SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
bungemanbea97482016-08-24 08:29:50 -0700857 if (!desc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700858 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700859 }
bungemanc292b5f2016-12-20 12:04:29 -0500860 return create_from_desc(desc.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000861}
862
863///////////////////////////////////////////////////////////////////////////////
864
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000865/* This function is visible on the outside. It first searches the cache, and if
866 * not found, returns a new entry (after adding it to the cache).
867 */
bungemanc292b5f2016-12-20 12:04:29 -0500868SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef font, CFTypeRef resource) {
869 CFRetain(font);
870 if (resource) {
871 CFRetain(resource);
bungeman53d5c6e2016-04-08 07:22:29 -0700872 }
Ben Wagner7b562292018-09-19 22:31:07 -0400873 return create_from_CTFontRef(SkUniqueCFRef<CTFontRef>(font),
874 SkUniqueCFRef<CFTypeRef>(resource),
Mike Reed59227392017-09-26 09:46:08 -0400875 false).release();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000876}
877
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000878static const char* map_css_names(const char* name) {
879 static const struct {
880 const char* fFrom; // name the caller specified
881 const char* fTo; // "canonical" name we map to
882 } gPairs[] = {
883 { "sans-serif", "Helvetica" },
884 { "serif", "Times" },
885 { "monospace", "Courier" }
886 };
887
888 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
889 if (strcmp(name, gPairs[i].fFrom) == 0) {
890 return gPairs[i].fTo;
891 }
892 }
893 return name; // no change
894}
895
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000896///////////////////////////////////////////////////////////////////////////////
897
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000898class SkScalerContext_Mac : public SkScalerContext {
899public:
bungeman7cfd46a2016-10-20 16:06:52 -0400900 SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000901
902protected:
mtklein36352bf2015-03-25 18:17:31 -0700903 unsigned generateGlyphCount(void) override;
904 uint16_t generateCharToGlyph(SkUnichar uni) override;
Ben Wagnere5416452018-08-09 14:03:42 -0400905 bool generateAdvance(SkGlyph* glyph) override;
mtklein36352bf2015-03-25 18:17:31 -0700906 void generateMetrics(SkGlyph* glyph) override;
907 void generateImage(const SkGlyph& glyph) override;
Ben Wagner5ddb3082018-03-29 11:18:06 -0400908 bool generatePath(SkGlyphID glyph, SkPath* path) override;
Mike Reedb5784ac2018-11-12 09:35:15 -0500909 void generateFontMetrics(SkFontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000910
911private:
912 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000913
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000914 Offscreen fOffscreen;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000915
bungeman3b4b66c2015-01-08 08:33:44 -0800916 /** Unrotated variant of fCTFont.
917 *
918 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
919 * advances, but always sets the height to 0. This font is used to get the advances of the
920 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000921 *
922 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
bungeman18ec7b92017-02-09 17:15:59 -0500923 * This makes kCTFontOrientationDefault dangerous, because the metrics from
924 * kCTFontOrientationHorizontal are in a different space from kCTFontOrientationVertical.
925 * With kCTFontOrientationVertical the advances must be unrotated.
bungemanef27ce32015-10-29 09:30:32 -0700926 *
927 * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
928 * different underlying font data. As a result, avoid ever creating more than one CTFont per
929 * SkScalerContext to ensure that only one CTFont is used.
930 *
931 * As a result of the above (and other constraints) this font contains the size, but not the
932 * transform. The transform must always be applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000933 */
Ben Wagner7b562292018-09-19 22:31:07 -0400934 SkUniqueCFRef<CTFontRef> fCTFont;
bungemanef27ce32015-10-29 09:30:32 -0700935
936 /** The transform without the font size. */
937 CGAffineTransform fTransform;
938 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000939
Ben Wagner7b562292018-09-19 22:31:07 -0400940 SkUniqueCFRef<CGFontRef> fCGFont;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000941 uint16_t fGlyphCount;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000942 const bool fDoSubPosition;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000943
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000944 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000945
946 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000947};
948
bungeman7cbeaae2015-09-22 09:54:56 -0700949// CTFontCreateCopyWithAttributes or CTFontCreateCopyWithSymbolicTraits cannot be used on 10.10
bungeman05846312015-09-23 12:51:28 -0700950// and later, as they will return different underlying fonts depending on the size requested.
951// It is not possible to use descriptors with CTFontCreateWithFontDescriptor, since that does not
952// work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
953// CGFont.
Ben Wagner7b562292018-09-19 22:31:07 -0400954static SkUniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
955 const CGAffineTransform* transform)
bungeman7cbeaae2015-09-22 09:54:56 -0700956{
Ben Wagner7b562292018-09-19 22:31:07 -0400957 SkUniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700958
bungeman05846312015-09-23 12:51:28 -0700959 // The last parameter (CTFontDescriptorRef attributes) *must* be nullptr.
960 // If non-nullptr then with fonts with variation axes, the copy will fail in
961 // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
962 // which it quite obviously is not.
bungeman7cbeaae2015-09-22 09:54:56 -0700963
bungeman05846312015-09-23 12:51:28 -0700964 // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
965 // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
966 // the scaler context, since they aren't 'normal'.
Ben Wagner7b562292018-09-19 22:31:07 -0400967 return SkUniqueCFRef<CTFontRef>(
bungemanc292b5f2016-12-20 12:04:29 -0500968 CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, transform, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700969}
970
bungeman7cfd46a2016-10-20 16:06:52 -0400971SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
reeda9322c22016-04-12 06:47:05 -0700972 const SkScalerContextEffects& effects,
reed@google.com0da48612013-03-19 16:06:52 +0000973 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400974 : INHERITED(std::move(typeface), effects, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000975 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
bungeman@google.comcefd9812013-05-15 15:07:32 +0000976
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000977{
reedd0f41732015-07-10 12:08:38 -0700978 AUTO_CG_LOCK();
979
Mike Reed342a9fa2017-05-03 15:48:22 -0400980 CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000981 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000982 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
983 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000984
bungeman34902632014-12-10 21:43:27 -0800985 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
986 // As a result, it is necessary to know the actual device size and request that.
987 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800988 SkMatrix skTransform;
bungemane55131c2016-08-24 12:01:31 -0700989 bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
bungeman4ec46aa2017-02-15 17:49:12 -0500990 &scale, &skTransform, nullptr, nullptr, nullptr);
bungemanaae30912015-03-02 13:43:26 -0800991 fTransform = MatrixToCGAffineTransform(skTransform);
bungeman1f0e78d2016-08-23 13:19:01 -0700992 // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
993 // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
bungemane55131c2016-08-24 12:01:31 -0700994 if (invertible) {
bungeman1f0e78d2016-08-23 13:19:01 -0700995 fInvTransform = CGAffineTransformInvert(fTransform);
996 } else {
997 fInvTransform = fTransform;
998 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000999
bungemanbe2284d2014-11-25 08:08:09 -08001000 // The transform contains everything except the requested text size.
1001 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -08001002 CGFloat textSize = ScalarToCG(scale.y());
bungemanc292b5f2016-12-20 12:04:29 -05001003 fCTFont = ctfont_create_exact_copy(ctFont, textSize, nullptr);
1004 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001005}
1006
1007CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
1008 CGGlyph glyphID, size_t* rowBytesPtr,
mtkleinbbd40182015-09-08 08:19:33 -07001009 bool generateA8FromLCD) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001010 if (!fRGBSpace) {
1011 //It doesn't appear to matter what color space is specified.
1012 //Regular blends and antialiased text are always (s*a + d*(1-a))
Ben Wagner6112f0b2018-09-27 15:44:56 -04001013 //and subpixel antialiased text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -07001014 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001015 }
1016
1017 // default to kBW_Format
1018 bool doAA = false;
1019 bool doLCD = false;
1020
1021 if (SkMask::kBW_Format != glyph.fMaskFormat) {
1022 doLCD = true;
1023 doAA = true;
1024 }
1025
1026 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1027 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
1028 doLCD = false;
1029 doAA = true;
1030 }
1031
bungeman34902632014-12-10 21:43:27 -08001032 // If this font might have color glyphs, disable LCD as there's no way to support it.
1033 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
bungemane280d062016-03-24 11:27:05 -07001034 // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
bungeman34902632014-12-10 21:43:27 -08001035 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
1036 doLCD = false;
1037 }
1038
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001039 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
1040 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
1041 if (fSize.fWidth < glyph.fWidth) {
1042 fSize.fWidth = RoundSize(glyph.fWidth);
1043 }
1044 if (fSize.fHeight < glyph.fHeight) {
1045 fSize.fHeight = RoundSize(glyph.fHeight);
1046 }
1047
1048 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
1049 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -08001050 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
1051 ? kCGImageAlphaPremultipliedFirst
1052 : kCGImageAlphaNoneSkipFirst;
1053 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -07001054 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungemanc292b5f2016-12-20 12:04:29 -05001055 rowBytes, fRGBSpace.get(), bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001056
bungeman34902632014-12-10 21:43:27 -08001057 // Skia handles quantization and subpixel positioning,
1058 // so disable quantization and enabe subpixel positioning in CG.
bungemanc292b5f2016-12-20 12:04:29 -05001059 CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
1060 CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001061
bungeman@google.comcefd9812013-05-15 15:07:32 +00001062 // Because CG always draws from the horizontal baseline,
1063 // if there is a non-integral translation from the horizontal origin to the vertical origin,
1064 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungemanc292b5f2016-12-20 12:04:29 -05001065 CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
1066 CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
bungeman34902632014-12-10 21:43:27 -08001067
bungemanc292b5f2016-12-20 12:04:29 -05001068 CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001069
bungemane280d062016-03-24 11:27:05 -07001070 // Draw black on white to create mask. (Special path exists to speed this up in CG.)
bungemanc292b5f2016-12-20 12:04:29 -05001071 CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001072
1073 // force our checks below to happen
1074 fDoAA = !doAA;
1075 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -08001076
bungemanc292b5f2016-12-20 12:04:29 -05001077 CGContextSetTextMatrix(fCG.get(), context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001078 }
1079
1080 if (fDoAA != doAA) {
bungemanc292b5f2016-12-20 12:04:29 -05001081 CGContextSetShouldAntialias(fCG.get(), doAA);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001082 fDoAA = doAA;
1083 }
1084 if (fDoLCD != doLCD) {
bungemanc292b5f2016-12-20 12:04:29 -05001085 CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001086 fDoLCD = doLCD;
1087 }
1088
1089 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
1090 // skip rows based on the glyph's height
1091 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
1092
bungemane280d062016-03-24 11:27:05 -07001093 // Erase to white (or transparent black if it's a color glyph, to not composite against white).
1094 uint32_t bgColor = (SkMask::kARGB32_Format != glyph.fMaskFormat) ? 0xFFFFFFFF : 0x00000000;
1095 sk_memset_rect32(image, bgColor, glyph.fWidth, glyph.fHeight, rowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001096
1097 float subX = 0;
1098 float subY = 0;
1099 if (context.fDoSubPosition) {
1100 subX = SkFixedToFloat(glyph.getSubXFixed());
1101 subY = SkFixedToFloat(glyph.getSubYFixed());
1102 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001103
bungeman34902632014-12-10 21:43:27 -08001104 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -08001105 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
1106 // 'positions' which are in text space. The glyph location (in device space) must be
1107 // mapped into text space, so that CG can convert it back into device space.
Hal Canary55325b72017-01-03 10:36:17 -05001108 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
bungeman1b5f25c2015-06-08 14:32:24 -07001109 //
bungemanaae30912015-03-02 13:43:26 -08001110 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
1111 // So always make the font transform identity and place the transform on the context.
1112 point = CGPointApplyAffineTransform(point, context.fInvTransform);
1113
bungemanc292b5f2016-12-20 12:04:29 -05001114 CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001115
1116 SkASSERT(rowBytesPtr);
1117 *rowBytesPtr = rowBytes;
1118 return image;
1119}
1120
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001121unsigned SkScalerContext_Mac::generateGlyphCount(void) {
1122 return fGlyphCount;
1123}
1124
1125uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
reedd0f41732015-07-10 12:08:38 -07001126 AUTO_CG_LOCK();
1127
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001128 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001129 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001130
1131 // Get the glyph
Hal Canaryf107a2f2018-07-25 16:52:48 -04001132 size_t numUniChar = SkUTF::ToUTF16(uni, theChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001133 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001134
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001135 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
1136 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1137 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
bungemanc292b5f2016-12-20 12:04:29 -05001138 CTFontGetGlyphsForCharacters(fCTFont.get(), theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001139 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001140}
1141
Ben Wagnere5416452018-08-09 14:03:42 -04001142bool SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
1143 return false;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001144}
1145
bungeman@google.comcefd9812013-05-15 15:07:32 +00001146void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
reedd0f41732015-07-10 12:08:38 -07001147 AUTO_CG_LOCK();
1148
Ben Wagnere5416452018-08-09 14:03:42 -04001149 glyph->fMaskFormat = fRec.fMaskFormat;
1150
djsollen1b277042014-08-06 06:58:06 -07001151 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +00001152 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001153
bungeman@google.comcefd9812013-05-15 15:07:32 +00001154 // The following block produces cgAdvance in CG units (pixels, y up).
1155 CGSize cgAdvance;
Mike Reedc88cc772018-10-23 12:05:47 -04001156 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
1157 &cgGlyph, &cgAdvance, 1);
bungemanef27ce32015-10-29 09:30:32 -07001158 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001159 glyph->fAdvanceX = CGToFloat(cgAdvance.width);
1160 glyph->fAdvanceY = -CGToFloat(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001161
bungeman@google.comcefd9812013-05-15 15:07:32 +00001162 // The following produces skBounds in SkGlyph units (pixels, y down),
1163 // or returns early if skBounds would be empty.
1164 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001165
bungemanc2ba75b2016-12-14 16:53:00 -05001166 // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
1167 // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
1168 // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
1169 // glyph is vertical. This avoids any diagreement between the various means of retrieving
1170 // vertical metrics.
bungeman@google.comcefd9812013-05-15 15:07:32 +00001171 {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001172 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1173 CGRect cgBounds;
bungeman18ec7b92017-02-09 17:15:59 -05001174 CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001175 &cgGlyph, &cgBounds, 1);
bungemanef27ce32015-10-29 09:30:32 -07001176 cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001177
bungeman@google.comcefd9812013-05-15 15:07:32 +00001178 // BUG?
1179 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1180 // it should be empty. So, if we see a zero-advance, we check if it has an
1181 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1182 // is rare, so we won't incur a big performance cost for this extra check.
1183 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
Ben Wagner7b562292018-09-19 22:31:07 -04001184 SkUniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph,nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05001185 if (!path || CGPathIsEmpty(path.get())) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001186 return;
1187 }
1188 }
1189
1190 if (CGRectIsEmpty_inline(cgBounds)) {
1191 return;
1192 }
1193
1194 // Convert cgBounds to SkGlyph units (pixels, y down).
1195 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1196 cgBounds.size.width, cgBounds.size.height);
1197 }
1198
bungeman@google.comcefd9812013-05-15 15:07:32 +00001199 // Currently the bounds are based on being rendered at (0,0).
1200 // The top left must not move, since that is the base from which subpixel positioning is offset.
1201 if (fDoSubPosition) {
1202 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1203 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1204 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001205
Mike Kleind94e00c2017-10-05 11:40:57 -04001206 // We're trying to pack left and top into int16_t,
1207 // and width and height into uint16_t, after outsetting by 1.
1208 if (!SkRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(skBounds)) {
1209 return;
1210 }
1211
bungeman@google.comcefd9812013-05-15 15:07:32 +00001212 SkIRect skIBounds;
1213 skBounds.roundOut(&skIBounds);
1214 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1215 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1216 // is not currently known, as CG dilates the outlines by some percentage.
1217 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1218 skIBounds.outset(1, 1);
1219 glyph->fLeft = SkToS16(skIBounds.fLeft);
1220 glyph->fTop = SkToS16(skIBounds.fTop);
1221 glyph->fWidth = SkToU16(skIBounds.width());
1222 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001223}
1224
Cary Clarka4083c92017-09-15 11:59:23 -04001225#include "SkColorData.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001226
Ben Wagner7a0248f2017-10-18 11:30:56 -04001227static constexpr uint8_t sk_pow2_table(size_t i) {
1228 return SkToU8(((i * i + 128) / 255));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001229}
1230
1231/**
1232 * This will invert the gamma applied by CoreGraphics, so we can get linear
1233 * values.
1234 *
Ben Wagner6112f0b2018-09-27 15:44:56 -04001235 * CoreGraphics obscurely defaults to 2.0 as the subpixel coverage gamma value.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001236 * The color space used does not appear to affect this choice.
1237 */
Ben Wagner7a0248f2017-10-18 11:30:56 -04001238static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001239
1240static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1241 while (count > 0) {
1242 uint8_t mask = 0;
1243 for (int i = 7; i >= 0; --i) {
bungemane280d062016-03-24 11:27:05 -07001244 mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001245 if (0 == --count) {
1246 break;
1247 }
1248 }
1249 *dst++ = mask;
1250 }
1251}
1252
1253template<bool APPLY_PREBLEND>
1254static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
bungemane280d062016-03-24 11:27:05 -07001255 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
1256 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
1257 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
bungeman3b4b66c2015-01-08 08:33:44 -08001258 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1259#if SK_SHOW_TEXT_BLIT_COVERAGE
1260 lum = SkTMax(lum, (U8CPU)0x30);
1261#endif
1262 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001263}
1264template<bool APPLY_PREBLEND>
1265static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1266 const SkGlyph& glyph, const uint8_t* table8) {
1267 const int width = glyph.fWidth;
1268 size_t dstRB = glyph.rowBytes();
1269 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1270
1271 for (int y = 0; y < glyph.fHeight; y++) {
1272 for (int i = 0; i < width; ++i) {
1273 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1274 }
bungemanc292b5f2016-12-20 12:04:29 -05001275 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1276 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001277 }
1278}
1279
1280template<bool APPLY_PREBLEND>
1281static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1282 const uint8_t* tableG,
1283 const uint8_t* tableB) {
bungemane280d062016-03-24 11:27:05 -07001284 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
1285 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
1286 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001287#if SK_SHOW_TEXT_BLIT_COVERAGE
1288 r = SkTMax(r, (U8CPU)0x30);
1289 g = SkTMax(g, (U8CPU)0x30);
1290 b = SkTMax(b, (U8CPU)0x30);
1291#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001292 return SkPack888ToRGB16(r, g, b);
1293}
1294template<bool APPLY_PREBLEND>
1295static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1296 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1297 const int width = glyph.fWidth;
1298 size_t dstRB = glyph.rowBytes();
1299 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1300
1301 for (int y = 0; y < glyph.fHeight; y++) {
1302 for (int i = 0; i < width; i++) {
1303 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1304 }
bungemanc292b5f2016-12-20 12:04:29 -05001305 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1306 dst = SkTAddOffset<uint16_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001307 }
1308}
1309
bungeman34902632014-12-10 21:43:27 -08001310static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1311 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001312 U8CPU r = (rgb >> 16) & 0xFF;
1313 U8CPU g = (rgb >> 8) & 0xFF;
1314 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001315#if SK_SHOW_TEXT_BLIT_COVERAGE
1316 a = SkTMax(a, (U8CPU)0x30);
1317#endif
bungeman34902632014-12-10 21:43:27 -08001318 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001319}
reed@google.comf77b35d2013-05-02 20:39:44 +00001320
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001321void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
bungemanc292b5f2016-12-20 12:04:29 -05001322 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001323
1324 // FIXME: lcd smoothed un-hinted rasterization unsupported.
Mike Reed04346d52018-11-05 12:45:32 -05001325 bool requestSmooth = fRec.getHinting() != kNo_SkFontHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001326
1327 // Draw the glyph
1328 size_t cgRowBytes;
Ben Wagner6112f0b2018-09-27 15:44:56 -04001329 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, requestSmooth);
halcanary96fcdcc2015-08-27 07:41:13 -07001330 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001331 return;
1332 }
1333
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001334 // Fix the glyph
bungemanc292b5f2016-12-20 12:04:29 -05001335 if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
Ben Wagner6112f0b2018-09-27 15:44:56 -04001336 (glyph.fMaskFormat == SkMask::kA8_Format
1337 && requestSmooth
Ben Wagner16d00ee2018-12-14 15:43:07 -05001338 && smooth_behavior() != SmoothBehavior::none))
bungemanc292b5f2016-12-20 12:04:29 -05001339 {
Ben Wagner7a0248f2017-10-18 11:30:56 -04001340 const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001341
1342 //Note that the following cannot really be integrated into the
1343 //pre-blend, since we may not be applying the pre-blend; when we aren't
1344 //applying the pre-blend it means that a filter wants linear anyway.
1345 //Other code may also be applying the pre-blend, so we'd need another
1346 //one with this and one without.
1347 CGRGBPixel* addr = cgPixels;
1348 for (int y = 0; y < glyph.fHeight; ++y) {
1349 for (int x = 0; x < glyph.fWidth; ++x) {
1350 int r = (addr[x] >> 16) & 0xFF;
1351 int g = (addr[x] >> 8) & 0xFF;
1352 int b = (addr[x] >> 0) & 0xFF;
Ben Wagner7a0248f2017-10-18 11:30:56 -04001353 addr[x] = (linear[r] << 16) | (linear[g] << 8) | linear[b];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001354 }
bungemane280d062016-03-24 11:27:05 -07001355 addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001356 }
1357 }
1358
1359 // Convert glyph to mask
1360 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001361 case SkMask::kLCD16_Format: {
1362 if (fPreBlend.isApplicable()) {
1363 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1364 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1365 } else {
1366 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1367 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1368 }
1369 } break;
1370 case SkMask::kA8_Format: {
1371 if (fPreBlend.isApplicable()) {
1372 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1373 } else {
1374 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1375 }
1376 } break;
1377 case SkMask::kBW_Format: {
1378 const int width = glyph.fWidth;
1379 size_t dstRB = glyph.rowBytes();
1380 uint8_t* dst = (uint8_t*)glyph.fImage;
1381 for (int y = 0; y < glyph.fHeight; y++) {
1382 cgpixels_to_bits(dst, cgPixels, width);
bungemanc292b5f2016-12-20 12:04:29 -05001383 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1384 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001385 }
1386 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001387 case SkMask::kARGB32_Format: {
1388 const int width = glyph.fWidth;
1389 size_t dstRB = glyph.rowBytes();
1390 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1391 for (int y = 0; y < glyph.fHeight; y++) {
1392 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001393 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001394 }
bungemanc292b5f2016-12-20 12:04:29 -05001395 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1396 dst = SkTAddOffset<SkPMColor>(dst, dstRB);
reed@google.comf77b35d2013-05-02 20:39:44 +00001397 }
1398 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001399 default:
1400 SkDEBUGFAIL("unexpected mask format");
1401 break;
1402 }
1403}
1404
1405/*
1406 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1407 * seems sufficient, and possibly even correct, to allow the hinted outline
1408 * to be subpixel positioned.
1409 */
1410#define kScaleForSubPixelPositionHinting (4.0f)
1411
Ben Wagner5ddb3082018-03-29 11:18:06 -04001412bool SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001413 AUTO_CG_LOCK();
1414
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001415 SkScalar scaleX = SK_Scalar1;
1416 SkScalar scaleY = SK_Scalar1;
1417
bungemanef27ce32015-10-29 09:30:32 -07001418 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001419 /*
1420 * For subpixel positioning, we want to return an unhinted outline, so it
1421 * can be positioned nicely at fractional offsets. However, we special-case
1422 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1423 * we want to retain hinting in the direction orthogonal to the baseline.
1424 * e.g. for horizontal baseline, we want to retain hinting in Y.
1425 * The way we remove hinting is to scale the font by some value (4) in that
1426 * direction, ask for the path, and then scale the path back down.
1427 */
bungeman7cbeaae2015-09-22 09:54:56 -07001428 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001429 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001430 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001431 // now see if we need to restore hinting for axis-aligned baselines
bungeman27876bc2016-02-29 11:22:55 -08001432 switch (this->computeAxisAlignmentForHText()) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001433 case kX_SkAxisAlignment:
1434 scaleY = SK_Scalar1; // want hinting in the Y direction
1435 break;
1436 case kY_SkAxisAlignment:
1437 scaleX = SK_Scalar1; // want hinting in the X direction
1438 break;
1439 default:
1440 break;
1441 }
1442
bungemanef27ce32015-10-29 09:30:32 -07001443 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1444 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001445 }
1446
Ben Wagner6e9ac122016-11-11 14:31:06 -05001447 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
Ben Wagner7b562292018-09-19 22:31:07 -04001448 SkUniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001449
1450 path->reset();
Ben Wagner5ddb3082018-03-29 11:18:06 -04001451 if (!cgPath) {
1452 return false;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001453 }
1454
Ben Wagner5ddb3082018-03-29 11:18:06 -04001455 CGPathApply(cgPath.get(), path, SkScalerContext_Mac::CTPathElement);
bungeman@google.comcefd9812013-05-15 15:07:32 +00001456 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001457 SkMatrix m;
1458 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1459 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001460 }
Ben Wagner5ddb3082018-03-29 11:18:06 -04001461 return true;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001462}
1463
Mike Reedb5784ac2018-11-12 09:35:15 -05001464void SkScalerContext_Mac::generateFontMetrics(SkFontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001465 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001466 return;
1467 }
1468
reedd0f41732015-07-10 12:08:38 -07001469 AUTO_CG_LOCK();
1470
bungemanc292b5f2016-12-20 12:04:29 -05001471 CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001472
bungeman41078062014-07-07 08:16:37 -07001473 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001474 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont.get()));
1475 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001476 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001477 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001478 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1479 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1480 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001481 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungemanc292b5f2016-12-20 12:04:29 -05001482 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont.get()));
1483 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont.get()));
1484 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont.get()));
1485 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont.get()));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001486
Ben Wagner219f3622017-07-17 15:32:25 -04001487 metrics->fFlags = 0;
Mike Reedb5784ac2018-11-12 09:35:15 -05001488 metrics->fFlags |= SkFontMetrics::kUnderlineThicknessIsValid_Flag;
1489 metrics->fFlags |= SkFontMetrics::kUnderlinePositionIsValid_Flag;
bungeman4ec46aa2017-02-15 17:49:12 -05001490
1491 // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
1492 // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
1493 // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
1494 // table are read, but then overwritten if the font is not a system font. As a result, if there
1495 // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
1496 struct OS2HeightMetrics {
1497 SK_OT_SHORT sxHeight;
1498 SK_OT_SHORT sCapHeight;
1499 } heights;
1500 size_t bytesRead = this->getTypeface()->getTableData(
1501 SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
1502 sizeof(heights), &heights);
1503 if (bytesRead == sizeof(heights)) {
1504 // 'fontSize' is correct because the entire resolved size is set by the constructor.
1505 CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
1506 unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
1507 unsigned maxSaneHeight = upem * 2;
1508 uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
1509 if (xHeight && xHeight < maxSaneHeight) {
1510 metrics->fXHeight = CGToScalar(xHeight * fontSize / upem);
1511 }
1512 uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
1513 if (capHeight && capHeight < maxSaneHeight) {
1514 metrics->fCapHeight = CGToScalar(capHeight * fontSize / upem);
1515 }
1516 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001517}
1518
1519void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1520 SkPath* skPath = (SkPath*)info;
1521
1522 // Process the path element
1523 switch (element->type) {
1524 case kCGPathElementMoveToPoint:
1525 skPath->moveTo(element->points[0].x, -element->points[0].y);
1526 break;
1527
1528 case kCGPathElementAddLineToPoint:
1529 skPath->lineTo(element->points[0].x, -element->points[0].y);
1530 break;
1531
1532 case kCGPathElementAddQuadCurveToPoint:
1533 skPath->quadTo(element->points[0].x, -element->points[0].y,
1534 element->points[1].x, -element->points[1].y);
1535 break;
1536
1537 case kCGPathElementAddCurveToPoint:
1538 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1539 element->points[1].x, -element->points[1].y,
1540 element->points[2].x, -element->points[2].y);
1541 break;
1542
1543 case kCGPathElementCloseSubpath:
1544 skPath->close();
1545 break;
1546
1547 default:
1548 SkDEBUGFAIL("Unknown path element!");
1549 break;
1550 }
1551}
1552
1553
1554///////////////////////////////////////////////////////////////////////////////
1555
halcanary96fcdcc2015-08-27 07:41:13 -07001556// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001557// Call must still manage its ownership of provider
Ben Wagner7b562292018-09-19 22:31:07 -04001558static sk_sp<SkTypeface> create_from_dataProvider(SkUniqueCFRef<CGDataProviderRef> provider,
Mike Reed59227392017-09-26 09:46:08 -04001559 int ttcIndex) {
Ben Wagnerfc497342017-02-24 11:15:26 -05001560 if (ttcIndex != 0) {
1561 return nullptr;
1562 }
Ben Wagner7b562292018-09-19 22:31:07 -04001563 SkUniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
bungemanc292b5f2016-12-20 12:04:29 -05001564 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07001565 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001566 }
Ben Wagner7b562292018-09-19 22:31:07 -04001567 SkUniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg.get(), 0, nullptr, nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05001568 if (!ct) {
1569 return nullptr;
1570 }
1571 return create_from_CTFontRef(std::move(ct), nullptr, true);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001572}
1573
bungemanc292b5f2016-12-20 12:04:29 -05001574// Web fonts added to the CTFont registry do not return their character set.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001575// Iterate through the font in this case. The existing caller caches the result,
1576// so the performance impact isn't too bad.
1577static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
Hal Canary46cc3da2018-05-09 11:50:34 -04001578 SkUnichar* out) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001579 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1580 UniChar unichar = 0;
1581 while (glyphCount > 0) {
1582 CGGlyph glyph;
1583 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
Hal Canary266dcb02017-04-07 11:49:20 -04001584 if (out[glyph] != 0) {
1585 out[glyph] = unichar;
1586 --glyphCount;
1587 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001588 }
1589 if (++unichar == 0) {
1590 break;
1591 }
1592 }
1593}
1594
1595// Construct Glyph to Unicode table.
1596// Unicode code points that require conjugate pairs in utf16 are not
1597// supported.
1598static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
Hal Canary46cc3da2018-05-09 11:50:34 -04001599 SkUnichar* glyphToUnicode) {
1600 sk_bzero(glyphToUnicode, sizeof(SkUnichar) * glyphCount);
Ben Wagner7b562292018-09-19 22:31:07 -04001601 SkUniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001602 if (!charSet) {
1603 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1604 return;
1605 }
1606
Ben Wagner7b562292018-09-19 22:31:07 -04001607 SkUniqueCFRef<CFDataRef> bitmap(
1608 CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001609 if (!bitmap) {
1610 return;
1611 }
bungemanc292b5f2016-12-20 12:04:29 -05001612 CFIndex length = CFDataGetLength(bitmap.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001613 if (!length) {
1614 return;
1615 }
1616 if (length > 8192) {
1617 // TODO: Add support for Unicode above 0xFFFF
1618 // Consider only the BMP portion of the Unicode character points.
1619 // The bitmap may contain other planes, up to plane 16.
1620 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1621 length = 8192;
1622 }
bungemanc292b5f2016-12-20 12:04:29 -05001623 const UInt8* bits = CFDataGetBytePtr(bitmap.get());
Hal Canary46cc3da2018-05-09 11:50:34 -04001624 sk_bzero(glyphToUnicode, glyphCount * sizeof(SkUnichar));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001625 for (int i = 0; i < length; i++) {
1626 int mask = bits[i];
1627 if (!mask) {
1628 continue;
1629 }
1630 for (int j = 0; j < 8; j++) {
1631 CGGlyph glyph;
1632 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1633 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
Hal Canary46cc3da2018-05-09 11:50:34 -04001634 glyphToUnicode[glyph] = unichar;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001635 }
1636 }
1637 }
1638}
1639
halcanary96fcdcc2015-08-27 07:41:13 -07001640/** Assumes src and dst are not nullptr. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001641static void CFStringToSkString(CFStringRef src, SkString* dst) {
1642 // Reserve enough room for the worst-case string,
1643 // plus 1 byte for the trailing null.
1644 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1645 kCFStringEncodingUTF8) + 1;
1646 dst->resize(length);
1647 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1648 // Resize to the actual UTF-8 length used, stripping the null character.
1649 dst->resize(strlen(dst->c_str()));
1650}
1651
Hal Canary46cc3da2018-05-09 11:50:34 -04001652void SkTypeface_Mac::getGlyphToUnicodeMap(SkUnichar* dstArray) const {
1653 AUTO_CG_LOCK();
Ben Wagner7b562292018-09-19 22:31:07 -04001654 SkUniqueCFRef<CTFontRef> ctFont =
Hal Canary46cc3da2018-05-09 11:50:34 -04001655 ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()), nullptr);
1656 CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
1657 populate_glyph_to_unicode(ctFont.get(), glyphCount, dstArray);
1658}
1659
Hal Canary209e4b12017-05-04 14:23:55 -04001660std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
reed@google.com2689f612013-03-20 20:01:47 +00001661
reedd0f41732015-07-10 12:08:38 -07001662 AUTO_CG_LOCK();
1663
Ben Wagner7b562292018-09-19 22:31:07 -04001664 SkUniqueCFRef<CTFontRef> ctFont =
bungemanc292b5f2016-12-20 12:04:29 -05001665 ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()), nullptr);
bungeman7cbeaae2015-09-22 09:54:56 -07001666
Hal Canary209e4b12017-05-04 14:23:55 -04001667 std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001668
1669 {
Ben Wagner7b562292018-09-19 22:31:07 -04001670 SkUniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
bungeman256b3512014-07-02 07:57:59 -07001671 if (fontName.get()) {
Hal Canary8031b322018-03-29 13:20:30 -07001672 CFStringToSkString(fontName.get(), &info->fPostScriptName);
1673 info->fFontName = info->fPostScriptName;
bungeman256b3512014-07-02 07:57:59 -07001674 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001675 }
1676
Ben Wagnerc6639052017-03-02 13:31:16 -05001677 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1678 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1679 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1680 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
Ben Wagner7b562292018-09-19 22:31:07 -04001681 SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont.get(), nullptr));
Ben Wagnerc6639052017-03-02 13:31:16 -05001682 if (cgFont) {
Ben Wagner7b562292018-09-19 22:31:07 -04001683 SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
Ben Wagnerc6639052017-03-02 13:31:16 -05001684 if (cgAxes && CFArrayGetCount(cgAxes.get()) > 0) {
1685 info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
1686 }
1687 }
1688
Hal Canary4a851ca2017-11-09 11:09:34 -05001689 SkOTTableOS2_V4::Type fsType;
1690 if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
1691 offsetof(SkOTTableOS2_V4, fsType),
1692 sizeof(fsType),
1693 &fsType)) {
Hal Canarya865d252017-11-09 16:16:09 -05001694 SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
Hal Canary4a851ca2017-11-09 11:09:34 -05001695 }
1696
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001697 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1698 // fonts always have both glyf and loca tables. At the least, this is what
1699 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1700 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001701 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001702 return info;
1703 }
1704
1705 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
bungemanc292b5f2016-12-20 12:04:29 -05001706 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001707 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1708 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1709 }
1710 if (symbolicTraits & kCTFontItalicTrait) {
1711 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1712 }
1713 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001714 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1715 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1716 } else if (stylisticClass & kCTFontScriptsClass) {
1717 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1718 }
bungemanc292b5f2016-12-20 12:04:29 -05001719 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
1720 info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
1721 info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
1722 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
1723 CGRect bbox = CTFontGetBoundingBox(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001724
1725 SkRect r;
1726 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1727 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1728 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1729 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1730
1731 r.roundOut(&(info->fBBox));
1732
1733 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1734 // This probably isn't very good with an italic font.
1735 int16_t min_width = SHRT_MAX;
1736 info->fStemV = 0;
1737 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1738 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1739 CGGlyph glyphs[count];
1740 CGRect boundingRects[count];
bungemanc292b5f2016-12-20 12:04:29 -05001741 if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
bungeman18ec7b92017-02-09 17:15:59 -05001742 CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001743 glyphs, boundingRects, count);
1744 for (size_t i = 0; i < count; i++) {
1745 int16_t width = (int16_t) boundingRects[i].size.width;
1746 if (width > 0 && width < min_width) {
1747 min_width = width;
1748 info->fStemV = min_width;
1749 }
1750 }
1751 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001752 return info;
1753}
1754
1755///////////////////////////////////////////////////////////////////////////////
1756
Ben Wagner93e4ea52018-04-05 11:57:17 -04001757static SK_SFNT_ULONG get_font_type_tag(CTFontRef ctFont) {
Ben Wagner7b562292018-09-19 22:31:07 -04001758 SkUniqueCFRef<CFNumberRef> fontFormatRef(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001759 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1760 if (!fontFormatRef) {
1761 return 0;
1762 }
1763
1764 SInt32 fontFormatValue;
bungemanc292b5f2016-12-20 12:04:29 -05001765 if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001766 return 0;
1767 }
1768
1769 switch (fontFormatValue) {
1770 case kCTFontFormatOpenTypePostScript:
1771 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1772 case kCTFontFormatOpenTypeTrueType:
1773 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1774 case kCTFontFormatTrueType:
1775 return SkSFNTHeader::fontType_MacTrueType::TAG;
1776 case kCTFontFormatPostScript:
1777 return SkSFNTHeader::fontType_PostScript::TAG;
1778 case kCTFontFormatBitmap:
1779 return SkSFNTHeader::fontType_MacTrueType::TAG;
1780 case kCTFontFormatUnrecognized:
1781 default:
Ben Wagner93e4ea52018-04-05 11:57:17 -04001782 return 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001783 }
1784}
1785
bungeman5f213d92015-01-27 05:39:10 -08001786SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
Ben Wagner93e4ea52018-04-05 11:57:17 -04001787 SK_SFNT_ULONG fontType = get_font_type_tag(fFontRef.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001788
1789 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001790 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001791 SkTDArray<SkFontTableTag> tableTags;
1792 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001793 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001794
Ben Wagner93e4ea52018-04-05 11:57:17 -04001795 // CT seems to be unreliable in being able to obtain the type,
1796 // even if all we want is the first four bytes of the font resource.
1797 // Just the presence of the FontForge 'FFTM' table seems to throw it off.
1798 if (fontType == 0) {
1799 fontType = SkSFNTHeader::fontType_WindowsTrueType::TAG;
1800
1801 // see https://skbug.com/7630#c7
1802 bool couldBeCFF = false;
1803 constexpr SkFontTableTag CFFTag = SkSetFourByteTag('C', 'F', 'F', ' ');
1804 constexpr SkFontTableTag CFF2Tag = SkSetFourByteTag('C', 'F', 'F', '2');
1805 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1806 if (CFFTag == tableTags[tableIndex] || CFF2Tag == tableTags[tableIndex]) {
1807 couldBeCFF = true;
1808 }
1809 }
1810 if (couldBeCFF) {
1811 fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1812 }
1813 }
1814
1815 // Sometimes CoreGraphics incorrectly thinks a font is kCTFontFormatPostScript.
1816 // It is exceedingly unlikely that this is the case, so double check
1817 // (see https://crbug.com/809763 ).
1818 if (fontType == SkSFNTHeader::fontType_PostScript::TAG) {
1819 // see if there are any required 'typ1' tables (see Adobe Technical Note #5180)
1820 bool couldBeTyp1 = false;
1821 constexpr SkFontTableTag TYPE1Tag = SkSetFourByteTag('T', 'Y', 'P', '1');
1822 constexpr SkFontTableTag CIDTag = SkSetFourByteTag('C', 'I', 'D', ' ');
1823 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1824 if (TYPE1Tag == tableTags[tableIndex] || CIDTag == tableTags[tableIndex]) {
1825 couldBeTyp1 = true;
1826 }
1827 }
1828 if (!couldBeTyp1) {
1829 fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1830 }
1831 }
1832
Ben Wagner03cd6e62018-03-08 16:02:55 -05001833 // get the table sizes and accumulate the total size of the font
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001834 SkTDArray<size_t> tableSizes;
1835 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1836 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001837 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001838 totalSize += (tableSize + 3) & ~3;
1839 *tableSizes.append() = tableSize;
1840 }
1841
1842 // reserve memory for stream, and zero it (tables must be zero padded)
1843 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1844 char* dataStart = (char*)stream->getMemoryBase();
1845 sk_bzero(dataStart, totalSize);
1846 char* dataPtr = dataStart;
1847
1848 // compute font header entries
1849 uint16_t entrySelector = 0;
1850 uint16_t searchRange = 1;
1851 while (searchRange < numTables >> 1) {
1852 entrySelector++;
1853 searchRange <<= 1;
1854 }
1855 searchRange <<= 4;
1856 uint16_t rangeShift = (numTables << 4) - searchRange;
1857
1858 // write font header
1859 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1860 header->fontType = fontType;
1861 header->numTables = SkEndian_SwapBE16(numTables);
1862 header->searchRange = SkEndian_SwapBE16(searchRange);
1863 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1864 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1865 dataPtr += sizeof(SkSFNTHeader);
1866
1867 // write tables
1868 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1869 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1870 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1871 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001872 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001873 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1874 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1875 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001876 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1877 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001878
1879 dataPtr += (tableSize + 3) & ~3;
1880 ++entry;
1881 }
1882
bungemanb3310c22015-03-02 09:05:36 -08001883 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001884 return stream;
1885}
1886
bungeman41868fe2015-05-20 09:21:04 -07001887struct NonDefaultAxesContext {
1888 SkFixed* axisValue;
1889 CFArrayRef cgAxes;
1890};
1891static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1892 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1893
1894 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1895 return;
1896 }
1897
1898 // The key is a CFString which is a string from the 'name' table.
1899 // Search the cgAxes for an axis with this name, and use its index to store the value.
1900 CFIndex keyIndex = -1;
1901 CFStringRef keyString = static_cast<CFStringRef>(key);
1902 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1903 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1904 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1905 continue;
1906 }
1907
1908 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1909 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1910 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1911 continue;
1912 }
1913 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1914 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1915 keyIndex = i;
1916 break;
1917 }
1918 }
1919 if (keyIndex == -1) {
1920 return;
1921 }
1922
1923 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1924 double valueDouble;
1925 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1926 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1927 {
1928 return;
1929 }
1930 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1931}
Ben Wagnerfc497342017-02-24 11:15:26 -05001932static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount,
bungeman41868fe2015-05-20 09:21:04 -07001933 SkAutoSTMalloc<4, SkFixed>* axisValues)
1934{
Ben Wagnerfc497342017-02-24 11:15:26 -05001935 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1936 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1937 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1938 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
Ben Wagner7b562292018-09-19 22:31:07 -04001939 SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05001940 if (!cgFont) {
bungeman41868fe2015-05-20 09:21:04 -07001941 return false;
1942 }
1943
Ben Wagner7b562292018-09-19 22:31:07 -04001944 SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
bungemanc292b5f2016-12-20 12:04:29 -05001945 // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
1946 if (!cgVariations) {
1947 return false;
1948 }
1949
Ben Wagner7b562292018-09-19 22:31:07 -04001950 SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
bungemanc292b5f2016-12-20 12:04:29 -05001951 if (!cgAxes) {
1952 return false;
1953 }
1954 *cgAxisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07001955 axisValues->reset(*cgAxisCount);
1956
1957 // Set all of the axes to their default values.
1958 // Fail if any default value cannot be determined.
1959 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001960 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07001961 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1962 return false;
1963 }
1964
1965 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1966 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1967 kCGFontVariationAxisDefaultValue);
1968 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1969 return false;
1970 }
1971 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
1972 double axisDefaultValueDouble;
1973 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
1974 {
1975 return false;
1976 }
1977 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
1978 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
1979 {
1980 return false;
1981 }
1982 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
1983 }
1984
1985 // Override the default values with the given font's stated axis values.
1986 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
bungemanc292b5f2016-12-20 12:04:29 -05001987 CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
bungeman41868fe2015-05-20 09:21:04 -07001988
1989 return true;
1990}
bungemanf93d7112016-09-16 06:24:20 -07001991std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
bungeman41868fe2015-05-20 09:21:04 -07001992 int index;
bungemanf93d7112016-09-16 06:24:20 -07001993 std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
bungeman41868fe2015-05-20 09:21:04 -07001994
1995 CFIndex cgAxisCount;
1996 SkAutoSTMalloc<4, SkFixed> axisValues;
bungemanc292b5f2016-12-20 12:04:29 -05001997 if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
bungemanf93d7112016-09-16 06:24:20 -07001998 return skstd::make_unique<SkFontData>(std::move(stream), index,
1999 axisValues.get(), cgAxisCount);
bungeman41868fe2015-05-20 09:21:04 -07002000 }
bungemanf93d7112016-09-16 06:24:20 -07002001 return skstd::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
bungeman41868fe2015-05-20 09:21:04 -07002002}
2003
Ben Wagnerfc497342017-02-24 11:15:26 -05002004/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */
Ben Wagner7b562292018-09-19 22:31:07 -04002005static SkUniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
2006 CFArrayRef ctAxes) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002007
Ben Wagner7b562292018-09-19 22:31:07 -04002008 SkUniqueCFRef<CFMutableDictionaryRef> ctVariations(
Ben Wagnerfc497342017-02-24 11:15:26 -05002009 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2010 &kCFTypeDictionaryKeyCallBacks,
2011 &kCFTypeDictionaryValueCallBacks));
2012
2013 CFIndex axisCount = CFArrayGetCount(ctAxes);
2014 for (CFIndex i = 0; i < axisCount; ++i) {
2015 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i);
2016 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2017 return nullptr;
2018 }
2019 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2020
2021 // The assumption is that values produced by kCTFontVariationAxisNameKey and
2022 // kCGFontVariationAxisName will always be equal.
2023 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
2024 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2025 return nullptr;
2026 }
2027
2028 CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName);
2029 if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) {
2030 return nullptr;
2031 }
2032
2033 CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
2034 if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
2035 return nullptr;
2036 }
2037
2038 CFDictionaryAddValue(ctVariations.get(), axisTag, axisValue);
2039 }
2040 return std::move(ctVariations);
2041}
2042
2043int SkTypeface_Mac::onGetVariationDesignPosition(
2044 SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
2045{
2046 // The CGFont variation data does not contain the tag.
2047
bungemanbc096bf2017-04-25 13:32:50 -04002048 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
2049 // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
Ben Wagner7b562292018-09-19 22:31:07 -04002050 SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
Ben Wagnerfc497342017-02-24 11:15:26 -05002051 if (!ctAxes) {
2052 return -1;
2053 }
2054 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
2055 if (!coordinates || coordinateCount < axisCount) {
2056 return axisCount;
2057 }
2058
2059 // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
2060 // When this happens, try converting the CG variation to a CT variation.
2061 // On 10.12 and later, this only returns non-default variations.
Ben Wagner7b562292018-09-19 22:31:07 -04002062 SkUniqueCFRef<CFDictionaryRef> ctVariations(CTFontCopyVariation(fFontRef.get()));
Ben Wagnerfc497342017-02-24 11:15:26 -05002063 if (!ctVariations) {
2064 // When 10.11 and earlier are no longer supported, the following code can be replaced with
2065 // return -1 and ct_variation_from_cg_variation can be removed.
Ben Wagner7b562292018-09-19 22:31:07 -04002066 SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
Ben Wagnerfc497342017-02-24 11:15:26 -05002067 if (!cgFont) {
2068 return -1;
2069 }
Ben Wagner7b562292018-09-19 22:31:07 -04002070 SkUniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
Ben Wagnerfc497342017-02-24 11:15:26 -05002071 if (!cgVariations) {
2072 return -1;
2073 }
2074 ctVariations = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
2075 if (!ctVariations) {
2076 return -1;
2077 }
2078 }
2079
2080 for (int i = 0; i < axisCount; ++i) {
2081 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
2082 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2083 return -1;
2084 }
2085 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2086
2087 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
2088 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2089 return -1;
2090 }
2091 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
2092 int64_t tagLong;
2093 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2094 return -1;
2095 }
2096 coordinates[i].axis = tagLong;
2097
2098 CGFloat variationCGFloat;
2099 CFTypeRef variationValue = CFDictionaryGetValue(ctVariations.get(), tagNumber);
2100 if (variationValue) {
2101 if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
2102 return -1;
2103 }
2104 CFNumberRef variationNumber = static_cast<CFNumberRef>(variationValue);
2105 if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) {
2106 return -1;
2107 }
2108 } else {
2109 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
2110 if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) {
2111 return -1;
2112 }
2113 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2114 if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) {
2115 return -1;
2116 }
2117 }
2118 coordinates[i].value = CGToScalar(variationCGFloat);
2119
2120 }
2121 return axisCount;
2122}
2123
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002124///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002125///////////////////////////////////////////////////////////////////////////////
2126
2127int SkTypeface_Mac::onGetUPEM() const {
Ben Wagner7b562292018-09-19 22:31:07 -04002128 SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05002129 return CGFontGetUnitsPerEm(cgFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002130}
2131
bungeman@google.com839702b2013-08-07 17:09:22 +00002132SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
Ben Wagnerad031f52018-08-20 13:45:57 -04002133 sk_sp<SkTypeface::LocalizedStrings> nameIter =
2134 SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(*this);
2135 if (!nameIter) {
bungemanc292b5f2016-12-20 12:04:29 -05002136 CFStringRef cfLanguageRaw;
Ben Wagner7b562292018-09-19 22:31:07 -04002137 SkUniqueCFRef<CFStringRef> cfFamilyName(
bungemanc292b5f2016-12-20 12:04:29 -05002138 CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
Ben Wagner7b562292018-09-19 22:31:07 -04002139 SkUniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
bungeman@google.coma9802692013-08-07 02:45:25 +00002140
2141 SkString skLanguage;
2142 SkString skFamilyName;
bungemanc292b5f2016-12-20 12:04:29 -05002143 if (cfLanguage) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002144 CFStringToSkString(cfLanguage.get(), &skLanguage);
2145 } else {
2146 skLanguage = "und"; //undetermined
2147 }
bungemanc292b5f2016-12-20 12:04:29 -05002148 if (cfFamilyName) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002149 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
2150 }
2151
Ben Wagnerad031f52018-08-20 13:45:57 -04002152 nameIter = sk_make_sp<SkOTUtils::LocalizedStrings_SingleName>(skFamilyName, skLanguage);
bungeman@google.coma9802692013-08-07 02:45:25 +00002153 }
Ben Wagnerad031f52018-08-20 13:45:57 -04002154 return nameIter.release();
bungeman@google.coma9802692013-08-07 02:45:25 +00002155}
2156
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002157int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
Ben Wagner7b562292018-09-19 22:31:07 -04002158 SkUniqueCFRef<CFArrayRef> cfArray(
bungemanc292b5f2016-12-20 12:04:29 -05002159 CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
2160 if (!cfArray) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002161 return 0;
2162 }
bungemanc292b5f2016-12-20 12:04:29 -05002163 int count = SkToInt(CFArrayGetCount(cfArray.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002164 if (tags) {
2165 for (int i = 0; i < count; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002166 uintptr_t fontTag = reinterpret_cast<uintptr_t>(
2167 CFArrayGetValueAtIndex(cfArray.get(), i));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002168 tags[i] = static_cast<SkFontTableTag>(fontTag);
2169 }
2170 }
2171 return count;
2172}
2173
bungemanc292b5f2016-12-20 12:04:29 -05002174// If, as is the case with web fonts, the CTFont data isn't available,
2175// the CGFont data may work. While the CGFont may always provide the
2176// right result, leave the CTFont code path to minimize disruption.
Ben Wagner7b562292018-09-19 22:31:07 -04002177static SkUniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
2178 SkUniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
2179 kCTFontTableOptionNoOptions));
bungemanc292b5f2016-12-20 12:04:29 -05002180 if (!data) {
Ben Wagner7b562292018-09-19 22:31:07 -04002181 SkUniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05002182 data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
2183 }
2184 return data;
2185}
2186
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002187size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
2188 size_t length, void* dstData) const {
Ben Wagner7b562292018-09-19 22:31:07 -04002189 SkUniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
bungemanc292b5f2016-12-20 12:04:29 -05002190 if (!srcData) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002191 return 0;
2192 }
2193
bungemanc292b5f2016-12-20 12:04:29 -05002194 size_t srcSize = CFDataGetLength(srcData.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002195 if (offset >= srcSize) {
2196 return 0;
2197 }
2198 if (length > srcSize - offset) {
2199 length = srcSize - offset;
2200 }
2201 if (dstData) {
bungemanc292b5f2016-12-20 12:04:29 -05002202 memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002203 }
2204 return length;
2205}
2206
reeda9322c22016-04-12 06:47:05 -07002207SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
2208 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04002209 return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002210}
2211
2212void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002213 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2214 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2215 {
2216 rec->fMaskFormat = SkMask::kA8_Format;
2217 // Render the glyphs as close as possible to what was requested.
2218 // The above turns off subpixel rendering, but the user requested it.
2219 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
2220 // See comments below for more details.
Mike Reed04346d52018-11-05 12:45:32 -05002221 rec->setHinting(kNormal_SkFontHinting);
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002222 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00002223
Herb Derbyfcac00f2018-05-01 11:57:56 -04002224 unsigned flagsWeDontSupport = SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002225 SkScalerContext::kLCD_BGROrder_Flag |
2226 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002227
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002228 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002229
Ben Wagner6112f0b2018-09-27 15:44:56 -04002230 SmoothBehavior smoothBehavior = smooth_behavior();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002231
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002232 // Only two levels of hinting are supported.
2233 // kNo_Hinting means avoid CoreGraphics outline dilation.
2234 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
2235 // If there is no lcd support, hinting (dilation) cannot be supported.
Mike Reed04346d52018-11-05 12:45:32 -05002236 SkFontHinting hinting = rec->getHinting();
2237 if (kSlight_SkFontHinting == hinting || smoothBehavior == SmoothBehavior::none) {
2238 hinting = kNo_SkFontHinting;
2239 } else if (kFull_SkFontHinting == hinting) {
2240 hinting = kNormal_SkFontHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002241 }
2242 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002243
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002244 // FIXME: lcd smoothed un-hinted rasterization unsupported.
2245 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
2246 // There is no current means to honor a request for unhinted lcd,
2247 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002248
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002249 // Hinting and smoothing should be orthogonal, but currently they are not.
2250 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
2251 // output is drawn from auto-dilated outlines (the amount of which is
2252 // determined by AppleFontSmoothing). Its regular anti-aliased output is
2253 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002254
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002255 // The behavior of Skia is as follows:
2256 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
2257 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
2258 // channel. This matches [LCD][yes-hint] in weight.
2259 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
2260 // Currenly side with LCD, effectively ignoring the hinting setting.
2261 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002262
bungemanc292b5f2016-12-20 12:04:29 -05002263 if (rec->fMaskFormat == SkMask::kLCD16_Format) {
Ben Wagner6112f0b2018-09-27 15:44:56 -04002264 if (smoothBehavior == SmoothBehavior::subpixel) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002265 //CoreGraphics creates 555 masks for smoothed text anyway.
2266 rec->fMaskFormat = SkMask::kLCD16_Format;
Mike Reed04346d52018-11-05 12:45:32 -05002267 rec->setHinting(kNormal_SkFontHinting);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002268 } else {
2269 rec->fMaskFormat = SkMask::kA8_Format;
Ben Wagner6112f0b2018-09-27 15:44:56 -04002270 if (smoothBehavior == SmoothBehavior::some) {
Mike Reed04346d52018-11-05 12:45:32 -05002271 rec->setHinting(kNormal_SkFontHinting);
Ben Wagner6112f0b2018-09-27 15:44:56 -04002272 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002273 }
2274 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002275
bungeman34902632014-12-10 21:43:27 -08002276 // CoreText provides no information as to whether a glyph will be color or not.
2277 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
2278 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08002279 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08002280 rec->fMaskFormat = SkMask::kARGB32_Format;
2281 }
2282
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002283 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2284 // All other masks can use regular gamma.
Mike Reed04346d52018-11-05 12:45:32 -05002285 if (SkMask::kA8_Format == rec->fMaskFormat && kNo_SkFontHinting == hinting) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002286#ifndef SK_GAMMA_APPLY_TO_A8
brianosmana1e8f8d2016-04-08 06:47:54 -07002287 // SRGBTODO: Is this correct? Do we want contrast boost?
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002288 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002289#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002290 } else {
2291 //CoreGraphics dialates smoothed text as needed.
2292 rec->setContrast(0);
2293 }
2294}
2295
bungemanc292b5f2016-12-20 12:04:29 -05002296/** Takes ownership of the CFStringRef. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002297static const char* get_str(CFStringRef ref, SkString* str) {
halcanary96fcdcc2015-08-27 07:41:13 -07002298 if (nullptr == ref) {
2299 return nullptr;
bungeman256b3512014-07-02 07:57:59 -07002300 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002301 CFStringToSkString(ref, str);
bungemanc292b5f2016-12-20 12:04:29 -05002302 CFRelease(ref);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002303 return str->c_str();
2304}
2305
bungemanb374d6a2014-09-17 07:48:59 -07002306void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
bungemanc292b5f2016-12-20 12:04:29 -05002307 get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
bungemanb374d6a2014-09-17 07:48:59 -07002308}
2309
reed@google.com5526ede2013-03-25 13:03:37 +00002310void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2311 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002312 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002313
bungemanc292b5f2016-12-20 12:04:29 -05002314 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
2315 desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
2316 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
bungemanb8113782016-07-25 16:54:59 -07002317 desc->setStyle(this->fontStyle());
caseq26337e92014-06-30 12:14:52 -07002318 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002319}
reed@google.com5526ede2013-03-25 13:03:37 +00002320
reed@google.combcb42ae2013-07-02 13:56:39 +00002321int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002322 uint16_t glyphs[], int glyphCount) const
2323{
2324 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
2325 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
2326 // 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 +00002327
reed@google.combcb42ae2013-07-02 13:56:39 +00002328 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002329 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
2330 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00002331 switch (encoding) {
2332 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002333 const char* utf8 = reinterpret_cast<const char*>(chars);
2334 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2335 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00002336 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002337 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
Hal Canaryf107a2f2018-07-25 16:52:48 -04002338 utf16 += SkUTF::ToUTF16(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00002339 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002340 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002341 break;
2342 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002343 case kUTF16_Encoding: {
2344 src = reinterpret_cast<const UniChar*>(chars);
2345 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00002346 for (int i = 0; i < glyphCount; ++i) {
Ben Wagnerad031f52018-08-20 13:45:57 -04002347 if (SkUTF16_IsLeadingSurrogate(src[i + extra])) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002348 ++extra;
2349 }
reed@google.combcb42ae2013-07-02 13:56:39 +00002350 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002351 srcCount = glyphCount + extra;
2352 break;
2353 }
2354 case kUTF32_Encoding: {
2355 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
2356 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2357 src = utf16;
2358 for (int i = 0; i < glyphCount; ++i) {
Hal Canaryf107a2f2018-07-25 16:52:48 -04002359 utf16 += SkUTF::ToUTF16(utf32[i], utf16);
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002360 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002361 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002362 break;
2363 }
2364 }
2365
halcanary96fcdcc2015-08-27 07:41:13 -07002366 // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002367 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002368 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2369 uint16_t* macGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002370 if (nullptr == macGlyphs || srcCount > glyphCount) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002371 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002372 }
2373
bungemanc292b5f2016-12-20 12:04:29 -05002374 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002375
2376 // If there were any non-bmp, then copy and compact.
halcanary96fcdcc2015-08-27 07:41:13 -07002377 // If 'glyphs' is nullptr, then compact glyphStorage in-place.
2378 // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
2379 // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002380 uint16_t* compactedGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002381 if (nullptr == compactedGlyphs) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002382 compactedGlyphs = macGlyphs;
2383 }
2384 if (srcCount > glyphCount) {
2385 int extra = 0;
2386 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002387 compactedGlyphs[i] = macGlyphs[i + extra];
Ben Wagnerad031f52018-08-20 13:45:57 -04002388 if (SkUTF16_IsLeadingSurrogate(src[i + extra])) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002389 ++extra;
2390 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002391 }
2392 }
2393
2394 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002395 return glyphCount;
2396 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002397
2398 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002399 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002400 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002401 return i;
2402 }
2403 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002404 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002405 return glyphCount;
2406}
2407
2408int SkTypeface_Mac::onCountGlyphs() const {
bungemanc292b5f2016-12-20 12:04:29 -05002409 return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
reed@google.combcb42ae2013-07-02 13:56:39 +00002410}
2411
reed@google.com95625db2013-03-25 20:44:02 +00002412///////////////////////////////////////////////////////////////////////////////
2413///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002414
2415static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
Ben Wagner7b562292018-09-19 22:31:07 -04002416 SkUniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
bungemanc292b5f2016-12-20 12:04:29 -05002417 if (!ref) {
reed@google.com83787c52013-03-26 17:19:15 +00002418 return false;
2419 }
bungemanc292b5f2016-12-20 12:04:29 -05002420 CFStringToSkString(ref.get(), value);
reed@google.com83787c52013-03-26 17:19:15 +00002421 return true;
2422}
2423
reed@google.com95625db2013-03-25 20:44:02 +00002424#include "SkFontMgr.h"
2425
reed@google.com964988f2013-03-29 14:57:22 +00002426static inline int sqr(int value) {
2427 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2428 return value * value;
2429}
2430
2431// We normalize each axis (weight, width, italic) to be base-900
2432static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2433 return sqr(a.weight() - b.weight()) +
2434 sqr((a.width() - b.width()) * 100) +
bungemanb4bb7d82016-04-27 10:21:04 -07002435 sqr((a.slant() != b.slant()) * 900);
reed@google.com964988f2013-03-29 14:57:22 +00002436}
2437
reed@google.com83787c52013-03-26 17:19:15 +00002438class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002439public:
bungeman53d5c6e2016-04-08 07:22:29 -07002440 SkFontStyleSet_Mac(CTFontDescriptorRef desc)
halcanary96fcdcc2015-08-27 07:41:13 -07002441 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
bungemanc292b5f2016-12-20 12:04:29 -05002442 , fCount(0)
2443 {
2444 if (!fArray) {
2445 fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
reed@google.comdea7ee02013-03-28 14:12:10 +00002446 }
bungemanc292b5f2016-12-20 12:04:29 -05002447 fCount = SkToInt(CFArrayGetCount(fArray.get()));
reed@google.com83787c52013-03-26 17:19:15 +00002448 }
2449
mtklein36352bf2015-03-25 18:17:31 -07002450 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002451 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002452 }
2453
mtklein36352bf2015-03-25 18:17:31 -07002454 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002455 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002456 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002457 if (style) {
bungeman2873c762017-01-13 11:40:21 -05002458 *style = fontstyle_from_descriptor(desc, false);
reed@google.com83787c52013-03-26 17:19:15 +00002459 }
2460 if (name) {
2461 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2462 name->reset();
2463 }
2464 }
2465 }
2466
mtklein36352bf2015-03-25 18:17:31 -07002467 SkTypeface* createTypeface(int index) override {
bungemanc292b5f2016-12-20 12:04:29 -05002468 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
2469 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002470
Mike Reed59227392017-09-26 09:46:08 -04002471 return create_from_desc(desc).release();
reed@google.com83787c52013-03-26 17:19:15 +00002472 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002473
mtklein36352bf2015-03-25 18:17:31 -07002474 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002475 if (0 == fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002476 return nullptr;
reed@google.com964988f2013-03-29 14:57:22 +00002477 }
Mike Reed59227392017-09-26 09:46:08 -04002478 return create_from_desc(findMatchingDesc(pattern)).release();
reed@google.com964988f2013-03-29 14:57:22 +00002479 }
2480
reed@google.com83787c52013-03-26 17:19:15 +00002481private:
Ben Wagner7b562292018-09-19 22:31:07 -04002482 SkUniqueCFRef<CFArrayRef> fArray;
bungemanc292b5f2016-12-20 12:04:29 -05002483 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002484
2485 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2486 int bestMetric = SK_MaxS32;
halcanary96fcdcc2015-08-27 07:41:13 -07002487 CTFontDescriptorRef bestDesc = nullptr;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002488
reed@google.com964988f2013-03-29 14:57:22 +00002489 for (int i = 0; i < fCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002490 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
bungeman2873c762017-01-13 11:40:21 -05002491 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc, false));
reed@google.com964988f2013-03-29 14:57:22 +00002492 if (0 == metric) {
2493 return desc;
2494 }
2495 if (metric < bestMetric) {
2496 bestMetric = metric;
2497 bestDesc = desc;
2498 }
2499 }
2500 SkASSERT(bestDesc);
2501 return bestDesc;
2502 }
reed@google.com83787c52013-03-26 17:19:15 +00002503};
2504
2505class SkFontMgr_Mac : public SkFontMgr {
Ben Wagner7b562292018-09-19 22:31:07 -04002506 SkUniqueCFRef<CFArrayRef> fNames;
bungemanc292b5f2016-12-20 12:04:29 -05002507 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002508
bungemanc292b5f2016-12-20 12:04:29 -05002509 CFStringRef getFamilyNameAt(int index) const {
reed@google.com83787c52013-03-26 17:19:15 +00002510 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002511 return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002512 }
2513
reed@google.com964988f2013-03-29 14:57:22 +00002514 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
Ben Wagner7b562292018-09-19 22:31:07 -04002515 SkUniqueCFRef<CFMutableDictionaryRef> cfAttr(
reed@google.com964988f2013-03-29 14:57:22 +00002516 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2517 &kCFTypeDictionaryKeyCallBacks,
2518 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002519
bungemanc292b5f2016-12-20 12:04:29 -05002520 CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002521
Ben Wagner7b562292018-09-19 22:31:07 -04002522 SkUniqueCFRef<CTFontDescriptorRef> desc(
bungemanc292b5f2016-12-20 12:04:29 -05002523 CTFontDescriptorCreateWithAttributes(cfAttr.get()));
2524 return new SkFontStyleSet_Mac(desc.get());
2525 }
2526
2527 /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
2528 * provide a wrapper here that will return an empty array if need be.
2529 */
Ben Wagner7b562292018-09-19 22:31:07 -04002530 static SkUniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
bungemanc292b5f2016-12-20 12:04:29 -05002531#ifdef SK_BUILD_FOR_IOS
Ben Wagner7b562292018-09-19 22:31:07 -04002532 return SkUniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05002533#else
Ben Wagner7b562292018-09-19 22:31:07 -04002534 return SkUniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
bungemanc292b5f2016-12-20 12:04:29 -05002535#endif
reed@google.com964988f2013-03-29 14:57:22 +00002536 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002537
reed@google.com83787c52013-03-26 17:19:15 +00002538public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002539 SkFontMgr_Mac()
bungemanc292b5f2016-12-20 12:04:29 -05002540 : fNames(CopyAvailableFontFamilyNames())
2541 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0) {}
reed@google.com95625db2013-03-25 20:44:02 +00002542
2543protected:
mtklein36352bf2015-03-25 18:17:31 -07002544 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002545 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002546 }
2547
mtklein36352bf2015-03-25 18:17:31 -07002548 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002549 if ((unsigned)index < (unsigned)fCount) {
bungemanc292b5f2016-12-20 12:04:29 -05002550 CFStringToSkString(this->getFamilyNameAt(index), familyName);
reed@google.com83787c52013-03-26 17:19:15 +00002551 } else {
2552 familyName->reset();
2553 }
reed@google.com95625db2013-03-25 20:44:02 +00002554 }
2555
mtklein36352bf2015-03-25 18:17:31 -07002556 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002557 if ((unsigned)index >= (unsigned)fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002558 return nullptr;
reed@google.com83787c52013-03-26 17:19:15 +00002559 }
bungemanc292b5f2016-12-20 12:04:29 -05002560 return CreateSet(this->getFamilyNameAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002561 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002562
mtklein36352bf2015-03-25 18:17:31 -07002563 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
bungeman7575bb12017-05-01 13:02:42 -04002564 if (!familyName) {
2565 return nullptr;
2566 }
Ben Wagner7b562292018-09-19 22:31:07 -04002567 SkUniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
bungemanc292b5f2016-12-20 12:04:29 -05002568 return CreateSet(cfName.get());
reed@google.com964988f2013-03-29 14:57:22 +00002569 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002570
bungemanc292b5f2016-12-20 12:04:29 -05002571 SkTypeface* onMatchFamilyStyle(const char familyName[],
bungeman7575bb12017-05-01 13:02:42 -04002572 const SkFontStyle& style) const override {
Ben Wagner7b562292018-09-19 22:31:07 -04002573 SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
Mike Reed59227392017-09-26 09:46:08 -04002574 return create_from_desc(desc.get()).release();
reed@google.com95625db2013-03-25 20:44:02 +00002575 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002576
bungemanc292b5f2016-12-20 12:04:29 -05002577 SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
2578 const SkFontStyle& style,
2579 const char* bcp47[], int bcp47Count,
2580 SkUnichar character) const override {
Ben Wagner7b562292018-09-19 22:31:07 -04002581 SkUniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
2582 SkUniqueCFRef<CTFontRef> familyFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -07002583
2584 // kCFStringEncodingUTF32 is BE unless there is a BOM.
2585 // Since there is no machine endian option, explicitly state machine endian.
2586#ifdef SK_CPU_LENDIAN
2587 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
2588#else
2589 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
2590#endif
Ben Wagner7b562292018-09-19 22:31:07 -04002591 SkUniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
bungemanbea97482016-08-24 08:29:50 -07002592 kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
2593 encoding, false));
bungemanc292b5f2016-12-20 12:04:29 -05002594 CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units.
Ben Wagner7b562292018-09-19 22:31:07 -04002595 SkUniqueCFRef<CTFontRef> fallbackFont(
2596 CTFontCreateForString(familyFont.get(), string.get(), range));
Mike Reed59227392017-09-26 09:46:08 -04002597 return create_from_CTFontRef(std::move(fallbackFont), nullptr, false).release();
djsollen33068c12014-11-14 10:52:53 -08002598 }
2599
bungemanc292b5f2016-12-20 12:04:29 -05002600 SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2601 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002602 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002603 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002604
Mike Reed59227392017-09-26 09:46:08 -04002605 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
Ben Wagner7b562292018-09-19 22:31:07 -04002606 SkUniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromData(std::move(data)));
bungemanc292b5f2016-12-20 12:04:29 -05002607 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002608 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002609 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002610 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002611 }
2612
Mike Reed59227392017-09-26 09:46:08 -04002613 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2614 int ttcIndex) const override {
Ben Wagner7b562292018-09-19 22:31:07 -04002615 SkUniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromStream(std::move(stream)));
bungemanc292b5f2016-12-20 12:04:29 -05002616 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002617 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002618 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002619 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002620 }
2621
Ben Wagnerfc497342017-02-24 11:15:26 -05002622 /** Creates a dictionary suitable for setting the axes on a CGFont. */
Ben Wagner7b562292018-09-19 22:31:07 -04002623 static SkUniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, const SkFontArguments& args) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002624 // The CGFont variation data is keyed by name, but lacks the tag.
bungemanf6c71072016-01-21 14:17:47 -08002625 // The CTFont variation data is keyed by tag, and also has the name.
Ben Wagnerfc497342017-02-24 11:15:26 -05002626 // We would like to work with CTFont variations, but creating a CTFont font with
bungemanf6c71072016-01-21 14:17:47 -08002627 // CTFont variation dictionary runs into bugs. So use the CTFont variation data
2628 // to match names to tags to create the appropriate CGFont.
Ben Wagner7b562292018-09-19 22:31:07 -04002629 SkUniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
bungemanbc096bf2017-04-25 13:32:50 -04002630 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
2631 // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
Ben Wagner7b562292018-09-19 22:31:07 -04002632 SkUniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct.get()));
Ben Wagnerfc497342017-02-24 11:15:26 -05002633 if (!ctAxes) {
bungemanf6c71072016-01-21 14:17:47 -08002634 return nullptr;
2635 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002636 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
bungemanf6c71072016-01-21 14:17:47 -08002637
Ben Wagnerfc497342017-02-24 11:15:26 -05002638 const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
bungemanf6c71072016-01-21 14:17:47 -08002639
Ben Wagner7b562292018-09-19 22:31:07 -04002640 SkUniqueCFRef<CFMutableDictionaryRef> dict(
bungemanc292b5f2016-12-20 12:04:29 -05002641 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2642 &kCFTypeDictionaryKeyCallBacks,
2643 &kCFTypeDictionaryValueCallBacks));
2644
bungemanf6c71072016-01-21 14:17:47 -08002645 for (int i = 0; i < axisCount; ++i) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002646 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
bungemanf6c71072016-01-21 14:17:47 -08002647 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2648 return nullptr;
2649 }
2650 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2651
Ben Wagnerfc497342017-02-24 11:15:26 -05002652 // The assumption is that values produced by kCTFontVariationAxisNameKey and
2653 // kCGFontVariationAxisName will always be equal.
2654 // If they are ever not, seach the project history for "get_tag_for_name".
2655 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
bungemanf6c71072016-01-21 14:17:47 -08002656 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2657 return nullptr;
2658 }
2659
Ben Wagnerfc497342017-02-24 11:15:26 -05002660 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
2661 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2662 return nullptr;
bungemanf6c71072016-01-21 14:17:47 -08002663 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002664 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
bungemanf6c71072016-01-21 14:17:47 -08002665 int64_t tagLong;
2666 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2667 return nullptr;
2668 }
2669
2670 // The variation axes can be set to any value, but cg will effectively pin them.
2671 // Pin them here to normalize.
Ben Wagnerfc497342017-02-24 11:15:26 -05002672 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
2673 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
2674 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
bungemanf6c71072016-01-21 14:17:47 -08002675 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2676 !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
2677 !def || CFGetTypeID(def) != CFNumberGetTypeID())
2678 {
2679 return nullptr;
2680 }
2681 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2682 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2683 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2684 double minDouble;
2685 double maxDouble;
2686 double defDouble;
2687 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2688 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
2689 !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
2690 {
2691 return nullptr;
2692 }
2693
2694 double value = defDouble;
bungeman9aec8942017-03-29 13:38:53 -04002695 // The position may be over specified. If there are multiple values for a given axis,
2696 // use the last one since that's what css-fonts-4 requires.
2697 for (int j = position.coordinateCount; j --> 0;) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002698 if (position.coordinates[j].axis == tagLong) {
2699 value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
2700 minDouble, maxDouble);
bungemanf6c71072016-01-21 14:17:47 -08002701 break;
2702 }
2703 }
Ben Wagner7b562292018-09-19 22:31:07 -04002704 SkUniqueCFRef<CFNumberRef> valueNumber(
bungemanc292b5f2016-12-20 12:04:29 -05002705 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2706 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungemanf6c71072016-01-21 14:17:47 -08002707 }
bungemanc292b5f2016-12-20 12:04:29 -05002708 return std::move(dict);
bungemanf6c71072016-01-21 14:17:47 -08002709 }
Mike Reed59227392017-09-26 09:46:08 -04002710 sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> s,
2711 const SkFontArguments& args) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002712 if (args.getCollectionIndex() != 0) {
2713 return nullptr;
2714 }
Ben Wagner7b562292018-09-19 22:31:07 -04002715 SkUniqueCFRef<CGDataProviderRef> provider(SkCreateDataProviderFromStream(std::move(s)));
bungemanc292b5f2016-12-20 12:04:29 -05002716 if (!provider) {
bungemanf6c71072016-01-21 14:17:47 -08002717 return nullptr;
2718 }
Ben Wagner7b562292018-09-19 22:31:07 -04002719 SkUniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
bungemanc292b5f2016-12-20 12:04:29 -05002720 if (!cg) {
bungemanf6c71072016-01-21 14:17:47 -08002721 return nullptr;
2722 }
2723
Ben Wagner7b562292018-09-19 22:31:07 -04002724 SkUniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), args);
bungemanf6c71072016-01-21 14:17:47 -08002725 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2726 // created from a data provider does not appear to have any ownership of the underlying
2727 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
Ben Wagner7b562292018-09-19 22:31:07 -04002728 SkUniqueCFRef<CGFontRef> cgVariant;
bungemanf6c71072016-01-21 14:17:47 -08002729 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002730 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungemanf6c71072016-01-21 14:17:47 -08002731 } else {
mtklein18300a32016-03-16 13:53:35 -07002732 cgVariant.reset(cg.release());
bungemanf6c71072016-01-21 14:17:47 -08002733 }
2734
Ben Wagner7b562292018-09-19 22:31:07 -04002735 SkUniqueCFRef<CTFontRef> ct(
bungemanc292b5f2016-12-20 12:04:29 -05002736 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungemanf6c71072016-01-21 14:17:47 -08002737 if (!ct) {
2738 return nullptr;
2739 }
bungemanc292b5f2016-12-20 12:04:29 -05002740 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungemanf6c71072016-01-21 14:17:47 -08002741 }
2742
Ben Wagnerfc497342017-02-24 11:15:26 -05002743 /** Creates a dictionary suitable for setting the axes on a CGFont. */
Ben Wagner7b562292018-09-19 22:31:07 -04002744 static SkUniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, SkFontData* fontData) {
2745 SkUniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
bungeman41868fe2015-05-20 09:21:04 -07002746 if (!cgAxes) {
halcanary96fcdcc2015-08-27 07:41:13 -07002747 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002748 }
2749
bungemanc292b5f2016-12-20 12:04:29 -05002750 CFIndex axisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07002751 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002752 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002753 }
2754
Ben Wagner7b562292018-09-19 22:31:07 -04002755 SkUniqueCFRef<CFMutableDictionaryRef> dict(
bungemanc292b5f2016-12-20 12:04:29 -05002756 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2757 &kCFTypeDictionaryKeyCallBacks,
2758 &kCFTypeDictionaryValueCallBacks));
2759
bungeman41868fe2015-05-20 09:21:04 -07002760 for (int i = 0; i < fontData->getAxisCount(); ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002761 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07002762 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002763 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002764 }
2765 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2766
2767 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2768 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002769 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002770 }
2771
2772 // The variation axes can be set to any value, but cg will effectively pin them.
2773 // Pin them here to normalize.
2774 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2775 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2776 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2777 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2778 {
halcanary96fcdcc2015-08-27 07:41:13 -07002779 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002780 }
2781 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2782 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2783 double minDouble;
2784 double maxDouble;
2785 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2786 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2787 {
halcanary96fcdcc2015-08-27 07:41:13 -07002788 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002789 }
2790 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
Ben Wagner7b562292018-09-19 22:31:07 -04002791 SkUniqueCFRef<CFNumberRef> valueNumber(
bungemanc292b5f2016-12-20 12:04:29 -05002792 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2793 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungeman41868fe2015-05-20 09:21:04 -07002794 }
bungemanc292b5f2016-12-20 12:04:29 -05002795 return std::move(dict);
bungeman41868fe2015-05-20 09:21:04 -07002796 }
Mike Reed59227392017-09-26 09:46:08 -04002797 sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData> fontData) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002798 if (fontData->getIndex() != 0) {
2799 return nullptr;
2800 }
Ben Wagner7b562292018-09-19 22:31:07 -04002801 SkUniqueCFRef<CGDataProviderRef> provider(
bungemanf93d7112016-09-16 06:24:20 -07002802 SkCreateDataProviderFromStream(fontData->detachStream()));
bungemanc292b5f2016-12-20 12:04:29 -05002803 if (!provider) {
halcanary96fcdcc2015-08-27 07:41:13 -07002804 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002805 }
Ben Wagner7b562292018-09-19 22:31:07 -04002806 SkUniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
bungemanc292b5f2016-12-20 12:04:29 -05002807 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07002808 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002809 }
2810
Ben Wagner7b562292018-09-19 22:31:07 -04002811 SkUniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), fontData.get());
bungeman41868fe2015-05-20 09:21:04 -07002812 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2813 // created from a data provider does not appear to have any ownership of the underlying
2814 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
Ben Wagner7b562292018-09-19 22:31:07 -04002815 SkUniqueCFRef<CGFontRef> cgVariant;
bungeman41868fe2015-05-20 09:21:04 -07002816 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002817 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungeman41868fe2015-05-20 09:21:04 -07002818 } else {
mtklein18300a32016-03-16 13:53:35 -07002819 cgVariant.reset(cg.release());
bungeman41868fe2015-05-20 09:21:04 -07002820 }
2821
Ben Wagner7b562292018-09-19 22:31:07 -04002822 SkUniqueCFRef<CTFontRef> ct(
bungemanc292b5f2016-12-20 12:04:29 -05002823 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07002824 if (!ct) {
halcanary96fcdcc2015-08-27 07:41:13 -07002825 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002826 }
bungemanc292b5f2016-12-20 12:04:29 -05002827 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungeman41868fe2015-05-20 09:21:04 -07002828 }
2829
Mike Reed59227392017-09-26 09:46:08 -04002830 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
Ben Wagner7b562292018-09-19 22:31:07 -04002831 SkUniqueCFRef<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
bungemanc292b5f2016-12-20 12:04:29 -05002832 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002833 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002834 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002835 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002836 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002837
Mike Reed59227392017-09-26 09:46:08 -04002838 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002839 if (familyName) {
2840 familyName = map_css_names(familyName);
2841 }
2842
Mike Reed59227392017-09-26 09:46:08 -04002843 sk_sp<SkTypeface> face = create_from_name(familyName, style);
bungeman53d5c6e2016-04-08 07:22:29 -07002844 if (face) {
2845 return face;
bungemana4c4a2d2014-10-20 13:33:19 -07002846 }
bungeman53d5c6e2016-04-08 07:22:29 -07002847
bungemanc292b5f2016-12-20 12:04:29 -05002848 static SkTypeface* gDefaultFace;
2849 static SkOnce lookupDefault;
bungeman83f1f442017-02-06 13:21:33 -05002850 static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
bungemanc292b5f2016-12-20 12:04:29 -05002851 lookupDefault([]{
Mike Reed59227392017-09-26 09:46:08 -04002852 gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
bungemanc292b5f2016-12-20 12:04:29 -05002853 });
Mike Reed59227392017-09-26 09:46:08 -04002854 return sk_ref_sp(gDefaultFace);
reed@google.com7fdcd442013-07-30 21:25:49 +00002855 }
reed@google.com95625db2013-03-25 20:44:02 +00002856};
2857
reed@google.com7fdcd442013-07-30 21:25:49 +00002858///////////////////////////////////////////////////////////////////////////////
2859
Ben Wagner3546ff12017-01-03 13:32:36 -05002860sk_sp<SkFontMgr> SkFontMgr::Factory() { return sk_make_sp<SkFontMgr_Mac>(); }
mtklein1ee76512015-11-02 10:20:27 -08002861
2862#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)