blob: efd74f6f8e32b7734f912a3db30bace37ef289d0 [file] [log] [blame]
reed@android.com0680d6c2008-12-19 19:46:15 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
mtkleinde9e7a72015-03-11 12:01:25 -07008#include "SkTypes.h" // Keep this before any #ifdef ...
mtklein1ee76512015-11-02 10:20:27 -08009#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
mtkleinde9e7a72015-03-11 12:01:25 -070010
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000011#ifdef SK_BUILD_FOR_MAC
12#import <ApplicationServices/ApplicationServices.h>
13#endif
reed@android.com0680d6c2008-12-19 19:46:15 +000014
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000015#ifdef SK_BUILD_FOR_IOS
16#include <CoreText/CoreText.h>
reed@google.com3dcbd462013-03-27 13:56:34 +000017#include <CoreText/CTFontManager.h>
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000018#include <CoreGraphics/CoreGraphics.h>
19#include <CoreFoundation/CoreFoundation.h>
20#endif
21
reed39a9a502015-05-12 09:50:04 -070022#include "SkAdvancedTypefaceMetrics.h"
Hal Canary95e3c052017-01-11 12:44:43 -050023#include "SkAutoMalloc.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000024#include "SkCGUtils.h"
Cary Clarka4083c92017-09-15 11:59:23 -040025#include "SkColorData.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000026#include "SkDescriptor.h"
27#include "SkEndian.h"
jvanverth02802f62015-07-02 06:42:49 -070028#include "SkFloatingPoint.h"
mtklein1b249332015-07-07 12:21:21 -070029#include "SkFontDescriptor.h"
30#include "SkFontMgr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000031#include "SkGlyph.h"
bungemanf93d7112016-09-16 06:24:20 -070032#include "SkMakeUnique.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000033#include "SkMaskGamma.h"
halcanary4dbbd042016-06-07 17:21:10 -070034#include "SkMathPriv.h"
mtklein1b249332015-07-07 12:21:21 -070035#include "SkMutex.h"
bungeman4ec46aa2017-02-15 17:49:12 -050036#include "SkOTTable_OS_2.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000037#include "SkOTUtils.h"
mtkleinffa4a922016-05-05 16:05:56 -070038#include "SkOnce.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000039#include "SkPaint.h"
40#include "SkPath.h"
mtklein1b249332015-07-07 12:21:21 -070041#include "SkSFNTHeader.h"
jvanverth02802f62015-07-02 06:42:49 -070042#include "SkStream.h"
mtklein1b249332015-07-07 12:21:21 -070043#include "SkString.h"
bungemane280d062016-03-24 11:27:05 -070044#include "SkTemplates.h"
mtklein1b249332015-07-07 12:21:21 -070045#include "SkTypefaceCache.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000046#include "SkTypeface_mac.h"
47#include "SkUtils.h"
reed@google.comf77b35d2013-05-02 20:39:44 +000048
bungeman3e306f62017-03-29 11:32:02 -040049#include <dlfcn.h>
50
reedd0f41732015-07-10 12:08:38 -070051// Experimental code to use a global lock whenever we access CG, to see if this reduces
52// crashes in Chrome
53#define USE_GLOBAL_MUTEX_FOR_CG_ACCESS
54
55#ifdef USE_GLOBAL_MUTEX_FOR_CG_ACCESS
reed086eea92016-05-04 17:12:46 -070056 SK_DECLARE_STATIC_MUTEX(gCGMutex);
reedd0f41732015-07-10 12:08:38 -070057 #define AUTO_CG_LOCK() SkAutoMutexAcquire amc(gCGMutex)
58#else
59 #define AUTO_CG_LOCK()
60#endif
61
bungeman3b4b66c2015-01-08 08:33:44 -080062// Set to make glyph bounding boxes visible.
63#define SK_SHOW_TEXT_BLIT_COVERAGE 0
64
Mike Reed342a9fa2017-05-03 15:48:22 -040065CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
66 return face ? (CTFontRef)face->internal_private_getCTFontRef() : nullptr;
67}
68
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000069class SkScalerContext_Mac;
70
bungemanc292b5f2016-12-20 12:04:29 -050071struct CFSafeRelease {
72 void operator()(CFTypeRef cfTypeRef) {
73 if (cfTypeRef) {
74 CFRelease(cfTypeRef);
75 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000076 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000077};
bungemanc292b5f2016-12-20 12:04:29 -050078template <typename CFRef> using UniqueCFRef =
79 std::unique_ptr<skstd::remove_pointer_t<CFRef>, CFSafeRelease>;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000080
bungemanc292b5f2016-12-20 12:04:29 -050081static UniqueCFRef<CFStringRef> make_CFString(const char str[]) {
82 return UniqueCFRef<CFStringRef>(CFStringCreateWithCString(nullptr, str, kCFStringEncodingUTF8));
reed@google.com964988f2013-03-29 14:57:22 +000083}
84
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000085// inline versions of these rect helpers
86
87static bool CGRectIsEmpty_inline(const CGRect& rect) {
88 return rect.size.width <= 0 || rect.size.height <= 0;
89}
90
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000091static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
92 return rect.origin.x;
93}
94
95static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
96 return rect.origin.x + rect.size.width;
97}
98
99static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
100 return rect.origin.y;
101}
102
103static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
104 return rect.origin.y + rect.size.height;
105}
106
107static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
108 return rect.size.width;
109}
110
111///////////////////////////////////////////////////////////////////////////////
112
113static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
reed@google.com7fa2a652014-01-27 13:42:58 +0000114 int width, int height, size_t rowBytes) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000115 SkASSERT(width);
116 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
117
118 if (width >= 32) {
119 while (height) {
120 sk_memset32(ptr, value, width);
121 ptr = (uint32_t*)((char*)ptr + rowBytes);
122 height -= 1;
123 }
124 return;
125 }
126
127 rowBytes -= width * sizeof(uint32_t);
128
129 if (width >= 8) {
130 while (height) {
131 int w = width;
132 do {
133 *ptr++ = value; *ptr++ = value;
134 *ptr++ = value; *ptr++ = value;
135 *ptr++ = value; *ptr++ = value;
136 *ptr++ = value; *ptr++ = value;
137 w -= 8;
138 } while (w >= 8);
139 while (--w >= 0) {
140 *ptr++ = value;
141 }
142 ptr = (uint32_t*)((char*)ptr + rowBytes);
143 height -= 1;
144 }
145 } else {
146 while (height) {
147 int w = width;
148 do {
149 *ptr++ = value;
150 } while (--w > 0);
151 ptr = (uint32_t*)((char*)ptr + rowBytes);
152 height -= 1;
153 }
154 }
155}
156
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000157typedef uint32_t CGRGBPixel;
158
159static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
160 return pixel & 0xFF;
161}
162
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000163static CGFloat ScalarToCG(SkScalar scalar) {
164 if (sizeof(CGFloat) == sizeof(float)) {
165 return SkScalarToFloat(scalar);
166 } else {
167 SkASSERT(sizeof(CGFloat) == sizeof(double));
168 return (CGFloat) SkScalarToDouble(scalar);
169 }
170}
171
172static SkScalar CGToScalar(CGFloat cgFloat) {
173 if (sizeof(CGFloat) == sizeof(float)) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700174 return SkFloatToScalar(cgFloat);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000175 } else {
176 SkASSERT(sizeof(CGFloat) == sizeof(double));
177 return SkDoubleToScalar(cgFloat);
178 }
179}
180
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700181static float CGToFloat(CGFloat cgFloat) {
182 if (sizeof(CGFloat) == sizeof(float)) {
183 return cgFloat;
184 } else {
185 SkASSERT(sizeof(CGFloat) == sizeof(double));
186 return static_cast<float>(cgFloat);
187 }
188}
189
bungemanc292b5f2016-12-20 12:04:29 -0500190static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix) {
191 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX]),
192 -ScalarToCG(matrix[SkMatrix::kMSkewY] ),
193 -ScalarToCG(matrix[SkMatrix::kMSkewX] ),
194 ScalarToCG(matrix[SkMatrix::kMScaleY]),
195 ScalarToCG(matrix[SkMatrix::kMTransX]),
196 ScalarToCG(matrix[SkMatrix::kMTransY]));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000197}
198
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000199///////////////////////////////////////////////////////////////////////////////
200
201#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000202
Ben Wagner428ab732018-04-02 15:44:54 -0400203/** Drawn in FontForge, reduced with fonttools ttx, converted by xxd -i,
204 * this TrueType font contains a glyph of the spider.
205 *
206 * To re-forge the original bytes of the TrueType font file,
207 * remove all ',|( +0x)' from this definition,
208 * copy the data to the clipboard,
209 * run 'pbpaste | xxd -p -r - spider.ttf'.
210 */
211static constexpr const uint8_t kSpiderSymbol_ttf[] = {
212 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x80, 0x00, 0x03, 0x00, 0x40,
213 0x47, 0x44, 0x45, 0x46, 0x00, 0x14, 0x00, 0x14, 0x00, 0x00, 0x07, 0xa8,
214 0x00, 0x00, 0x00, 0x18, 0x4f, 0x53, 0x2f, 0x32, 0x8a, 0xf4, 0xfb, 0xdb,
215 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, 0x00, 0x60, 0x63, 0x6d, 0x61, 0x70,
216 0xe0, 0x7f, 0x10, 0x7e, 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x54,
217 0x67, 0x61, 0x73, 0x70, 0xff, 0xff, 0x00, 0x03, 0x00, 0x00, 0x07, 0xa0,
218 0x00, 0x00, 0x00, 0x08, 0x67, 0x6c, 0x79, 0x66, 0x97, 0x0b, 0x6a, 0xf6,
219 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x03, 0x40, 0x68, 0x65, 0x61, 0x64,
220 0x0f, 0xa2, 0x24, 0x1a, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x36,
221 0x68, 0x68, 0x65, 0x61, 0x0e, 0xd3, 0x07, 0x3f, 0x00, 0x00, 0x01, 0x04,
222 0x00, 0x00, 0x00, 0x24, 0x68, 0x6d, 0x74, 0x78, 0x10, 0x03, 0x00, 0x44,
223 0x00, 0x00, 0x01, 0xa8, 0x00, 0x00, 0x00, 0x0e, 0x6c, 0x6f, 0x63, 0x61,
224 0x01, 0xb4, 0x00, 0x28, 0x00, 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x0a,
225 0x6d, 0x61, 0x78, 0x70, 0x00, 0x4a, 0x01, 0x4d, 0x00, 0x00, 0x01, 0x28,
226 0x00, 0x00, 0x00, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0xc3, 0xe5, 0x39, 0xd4,
227 0x00, 0x00, 0x05, 0x58, 0x00, 0x00, 0x02, 0x28, 0x70, 0x6f, 0x73, 0x74,
228 0xff, 0x03, 0x00, 0x67, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x20,
229 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0b, 0x0f, 0x08, 0x1d,
230 0x5f, 0x0f, 0x3c, 0xf5, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
231 0xd1, 0x97, 0xa8, 0x5a, 0x00, 0x00, 0x00, 0x00, 0xd6, 0xe8, 0x32, 0x33,
232 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x55, 0x00, 0x00, 0x00, 0x08,
233 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
234 0x05, 0x55, 0xff, 0x3b, 0x01, 0x79, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00,
235 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00,
237 0x00, 0x04, 0x01, 0x1c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
238 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x2e,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x01, 0x90, 0x00, 0x05,
240 0x00, 0x00, 0x05, 0x33, 0x05, 0x99, 0x00, 0x00, 0x01, 0x1e, 0x05, 0x33,
241 0x05, 0x99, 0x00, 0x00, 0x03, 0xd7, 0x00, 0x66, 0x02, 0x12, 0x00, 0x00,
242 0x05, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x73, 0x6b, 0x69, 0x61, 0x00, 0xc0, 0x00, 0x00, 0xf0, 0x21,
245 0x06, 0x66, 0xfe, 0x66, 0x01, 0x79, 0x05, 0x55, 0x00, 0xc5, 0x80, 0x00,
246 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247 0x00, 0x20, 0x00, 0x01, 0x08, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00,
248 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
249 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x48,
250 0x00, 0x00, 0x00, 0x0e, 0x00, 0x08, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00,
251 0x00, 0x09, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21, 0xf0, 0x21, 0xff, 0xff,
252 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x1d, 0x00, 0x21,
253 0xf0, 0x21, 0xff, 0xff, 0x00, 0x01, 0xff, 0xf9, 0xff, 0xf5, 0xff, 0xe4,
254 0xff, 0xe2, 0x0f, 0xe2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
256 0x00, 0x14, 0x00, 0x14, 0x01, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x44,
257 0x00, 0x00, 0x02, 0x64, 0x05, 0x55, 0x00, 0x03, 0x00, 0x07, 0x00, 0x00,
258 0x33, 0x11, 0x21, 0x11, 0x25, 0x21, 0x11, 0x21, 0x44, 0x02, 0x20, 0xfe,
259 0x24, 0x01, 0x98, 0xfe, 0x68, 0x05, 0x55, 0xfa, 0xab, 0x44, 0x04, 0xcd,
260 0x00, 0x04, 0x00, 0x03, 0xff, 0x3b, 0x08, 0x00, 0x05, 0x4c, 0x00, 0x15,
261 0x00, 0x1d, 0x00, 0x25, 0x01, 0x1b, 0x00, 0x00, 0x01, 0x36, 0x37, 0x36,
262 0x27, 0x26, 0x07, 0x06, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27, 0x26, 0x07,
263 0x06, 0x17, 0x16, 0x17, 0x16, 0x32, 0x37, 0x32, 0x35, 0x34, 0x23, 0x22,
264 0x15, 0x14, 0x27, 0x32, 0x35, 0x34, 0x23, 0x22, 0x15, 0x14, 0x03, 0x32,
265 0x17, 0x30, 0x17, 0x31, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33,
266 0x32, 0x33, 0x16, 0x33, 0x32, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27,
267 0x26, 0x27, 0x26, 0x23, 0x22, 0x07, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06,
268 0x1f, 0x02, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32, 0x17, 0x17, 0x16, 0x33,
269 0x16, 0x17, 0x16, 0x07, 0x06, 0x23, 0x22, 0x27, 0x27, 0x26, 0x23, 0x22,
270 0x07, 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x33, 0x32, 0x33, 0x32,
271 0x37, 0x36, 0x37, 0x36, 0x17, 0x16, 0x1f, 0x02, 0x16, 0x17, 0x16, 0x15,
272 0x14, 0x23, 0x22, 0x27, 0x27, 0x26, 0x27, 0x27, 0x26, 0x27, 0x26, 0x07,
273 0x06, 0x07, 0x06, 0x17, 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x07,
274 0x06, 0x23, 0x22, 0x27, 0x26, 0x07, 0x06, 0x07, 0x06, 0x15, 0x14, 0x17,
275 0x16, 0x17, 0x16, 0x15, 0x14, 0x07, 0x06, 0x23, 0x22, 0x27, 0x26, 0x27,
276 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x34, 0x27, 0x26, 0x07,
277 0x06, 0x07, 0x06, 0x0f, 0x02, 0x06, 0x23, 0x22, 0x27, 0x26, 0x35, 0x34,
278 0x37, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x27, 0x26, 0x27,
279 0x26, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x07, 0x06, 0x23, 0x22,
280 0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x37, 0x36, 0x37, 0x37, 0x36,
281 0x37, 0x36, 0x37, 0x36, 0x35, 0x34, 0x27, 0x26, 0x27, 0x26, 0x27, 0x26,
282 0x23, 0x22, 0x07, 0x06, 0x07, 0x06, 0x07, 0x06, 0x27, 0x26, 0x27, 0x26,
283 0x27, 0x26, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36, 0x37, 0x36, 0x33, 0x32,
284 0x17, 0x16, 0x33, 0x32, 0x37, 0x36, 0x35, 0x34, 0x37, 0x36, 0x37, 0x36,
285 0x33, 0x04, 0xf5, 0x23, 0x13, 0x11, 0x14, 0x16, 0x1d, 0x1b, 0x4c, 0x1f,
286 0x0e, 0x2d, 0x23, 0x14, 0x2c, 0x13, 0x18, 0x25, 0x2c, 0x10, 0x3c, 0x71,
287 0x1d, 0x5c, 0x5c, 0x3f, 0xae, 0x5c, 0x5c, 0x3f, 0x6a, 0x27, 0x31, 0x5b,
288 0x09, 0x27, 0x36, 0x03, 0x0a, 0x26, 0x35, 0x2e, 0x09, 0x08, 0xc6, 0x13,
289 0x81, 0x17, 0x20, 0x18, 0x21, 0x1e, 0x04, 0x04, 0x15, 0x5c, 0x22, 0x26,
290 0x48, 0x56, 0x3b, 0x10, 0x21, 0x01, 0x0c, 0x06, 0x06, 0x0f, 0x31, 0x44,
291 0x3c, 0x52, 0x4a, 0x1d, 0x11, 0x3f, 0xb4, 0x71, 0x01, 0x26, 0x06, 0x0d,
292 0x15, 0x1a, 0x2a, 0x13, 0x53, 0xaa, 0x42, 0x1d, 0x0a, 0x33, 0x20, 0x21,
293 0x2b, 0x01, 0x02, 0x3e, 0x21, 0x09, 0x02, 0x02, 0x0f, 0x2d, 0x4b, 0x0a,
294 0x22, 0x15, 0x20, 0x1f, 0x72, 0x8b, 0x2d, 0x2f, 0x1d, 0x1f, 0x0e, 0x25,
295 0x3f, 0x4d, 0x1b, 0x63, 0x2a, 0x2c, 0x14, 0x22, 0x18, 0x1c, 0x0f, 0x08,
296 0x2a, 0x08, 0x08, 0x0d, 0x3b, 0x4c, 0x52, 0x74, 0x27, 0x71, 0x2e, 0x01,
297 0x0c, 0x10, 0x15, 0x0d, 0x06, 0x0d, 0x05, 0x01, 0x06, 0x2c, 0x28, 0x14,
298 0x1b, 0x05, 0x04, 0x10, 0x06, 0x12, 0x08, 0x0a, 0x16, 0x27, 0x03, 0x0d,
299 0x30, 0x4c, 0x4c, 0x4b, 0x1f, 0x0b, 0x22, 0x26, 0x0d, 0x15, 0x0d, 0x2d,
300 0x68, 0x34, 0x14, 0x3c, 0x25, 0x12, 0x04, 0x10, 0x18, 0x0b, 0x09, 0x30,
301 0x2b, 0x44, 0x66, 0x14, 0x47, 0x47, 0x59, 0x73, 0x25, 0x05, 0x03, 0x1f,
302 0x01, 0x08, 0x3f, 0x48, 0x4b, 0x4b, 0x76, 0x2f, 0x49, 0x2d, 0x22, 0x24,
303 0x0c, 0x15, 0x08, 0x0e, 0x33, 0x03, 0x44, 0x4c, 0x10, 0x46, 0x13, 0x1f,
304 0x27, 0x1b, 0x1d, 0x13, 0x02, 0x24, 0x08, 0x02, 0x42, 0x0e, 0x4d, 0x3c,
305 0x19, 0x1b, 0x40, 0x2b, 0x2b, 0x1e, 0x16, 0x11, 0x04, 0x1f, 0x11, 0x04,
306 0x18, 0x11, 0x35, 0x01, 0xa3, 0x13, 0x24, 0x1f, 0x0b, 0x0c, 0x19, 0x19,
307 0x18, 0x13, 0x0f, 0x0c, 0x1a, 0x18, 0x1f, 0x19, 0x1e, 0x07, 0x1a, 0xc3,
308 0x54, 0x51, 0x54, 0x51, 0x04, 0x53, 0x51, 0x54, 0x50, 0x02, 0x48, 0x1a,
309 0x31, 0x18, 0x55, 0x74, 0x04, 0x0e, 0x09, 0x0d, 0x06, 0x10, 0x16, 0x1b,
310 0x24, 0x01, 0x04, 0x0b, 0x04, 0x10, 0x3f, 0x0a, 0x41, 0x02, 0x41, 0x20,
311 0x06, 0x12, 0x16, 0x21, 0x17, 0x2a, 0x1e, 0x15, 0x40, 0x27, 0x11, 0x0e,
312 0x1e, 0x11, 0x15, 0x1f, 0x43, 0x13, 0x1a, 0x10, 0x15, 0x1b, 0x04, 0x09,
313 0x4d, 0x2a, 0x0f, 0x19, 0x0a, 0x0a, 0x03, 0x05, 0x15, 0x3c, 0x64, 0x21,
314 0x4b, 0x2e, 0x21, 0x28, 0x13, 0x47, 0x44, 0x19, 0x3f, 0x11, 0x18, 0x0b,
315 0x0a, 0x07, 0x18, 0x0d, 0x07, 0x24, 0x2c, 0x2b, 0x21, 0x32, 0x10, 0x48,
316 0x2a, 0x2d, 0x1e, 0x1a, 0x01, 0x0c, 0x43, 0x59, 0x28, 0x4e, 0x1c, 0x0d,
317 0x5d, 0x24, 0x14, 0x0a, 0x05, 0x1f, 0x24, 0x32, 0x46, 0x3e, 0x5f, 0x3e,
318 0x44, 0x1a, 0x30, 0x15, 0x0d, 0x07, 0x18, 0x2b, 0x03, 0x0d, 0x1a, 0x28,
319 0x28, 0x57, 0xb2, 0x29, 0x27, 0x40, 0x2c, 0x23, 0x16, 0x63, 0x58, 0x1a,
320 0x0a, 0x18, 0x11, 0x23, 0x08, 0x1b, 0x29, 0x05, 0x04, 0x0b, 0x15, 0x0d,
321 0x14, 0x0b, 0x2a, 0x29, 0x5a, 0x62, 0x01, 0x19, 0x1e, 0x05, 0x05, 0x26,
322 0x42, 0x42, 0x2a, 0x2a, 0x3f, 0x0d, 0x0f, 0x09, 0x05, 0x07, 0x01, 0x0b,
323 0x25, 0x3e, 0x0d, 0x17, 0x11, 0x01, 0x03, 0x0d, 0x13, 0x20, 0x19, 0x11,
324 0x03, 0x02, 0x01, 0x04, 0x11, 0x04, 0x05, 0x1b, 0x3d, 0x10, 0x29, 0x20,
325 0x04, 0x04, 0x0a, 0x07, 0x04, 0x1f, 0x15, 0x20, 0x3e, 0x0f, 0x2a, 0x1e,
326 0x00, 0x00, 0x00, 0x1b, 0x01, 0x4a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
327 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
328 0x00, 0x01, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
329 0x00, 0x02, 0x00, 0x07, 0x00, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
330 0x00, 0x03, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
331 0x00, 0x04, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
332 0x00, 0x05, 0x00, 0x02, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
333 0x00, 0x06, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
334 0x00, 0x0d, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
335 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x30, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
336 0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
337 0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
338 0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
339 0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
340 0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
341 0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
342 0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
343 0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x00, 0x04, 0x09,
344 0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
345 0x00, 0x00, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
346 0x00, 0x01, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
347 0x00, 0x02, 0x00, 0x0e, 0x00, 0x98, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
348 0x00, 0x03, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
349 0x00, 0x04, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
350 0x00, 0x05, 0x00, 0x04, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
351 0x00, 0x06, 0x00, 0x18, 0x00, 0x80, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
352 0x00, 0x0d, 0x00, 0x36, 0x00, 0x4a, 0x00, 0x03, 0x00, 0x01, 0x04, 0x09,
353 0x00, 0x0e, 0x00, 0x34, 0x00, 0xaa, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69,
354 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x31, 0x35,
355 0x2c, 0x20, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x53, 0x70, 0x69,
356 0x64, 0x65, 0x72, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x52, 0x65, 0x67,
357 0x75, 0x6c, 0x61, 0x72, 0x56, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
358 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x2e, 0x73, 0x69, 0x6c,
359 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x4f, 0x46, 0x4c, 0x00, 0x43, 0x00, 0x6f,
360 0x00, 0x70, 0x00, 0x79, 0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68,
361 0x00, 0x74, 0x00, 0x20, 0x00, 0x28, 0x00, 0x63, 0x00, 0x29, 0x00, 0x20,
362 0x00, 0x32, 0x00, 0x30, 0x00, 0x31, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x20,
363 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x6c, 0x00, 0x65,
364 0x00, 0x2e, 0x00, 0x53, 0x00, 0x70, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65,
365 0x00, 0x72, 0x00, 0x53, 0x00, 0x79, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x6f,
366 0x00, 0x6c, 0x00, 0x52, 0x00, 0x65, 0x00, 0x67, 0x00, 0x75, 0x00, 0x6c,
367 0x00, 0x61, 0x00, 0x72, 0x00, 0x56, 0x00, 0x31, 0x00, 0x68, 0x00, 0x74,
368 0x00, 0x74, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0x73,
369 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70, 0x00, 0x74, 0x00, 0x73,
370 0x00, 0x2e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x2e, 0x00, 0x6f,
371 0x00, 0x72, 0x00, 0x67, 0x00, 0x2f, 0x00, 0x4f, 0x00, 0x46, 0x00, 0x4c,
372 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x66,
373 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
375 0xff, 0xff, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
376 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
377 0x00, 0x02, 0x00, 0x00
378};
379
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000380/**
381 * There does not appear to be a publicly accessable API for determining if lcd
382 * font smoothing will be applied if we request it. The main issue is that if
383 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
384 */
385static bool supports_LCD() {
Mike Klein70284432017-09-19 13:07:27 -0400386 static bool gSupportsLCD = []{
Ben Wagner428ab732018-04-02 15:44:54 -0400387 uint32_t bitmap[16][16] = {};
Mike Klein70284432017-09-19 13:07:27 -0400388 UniqueCFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
389 UniqueCFRef<CGContextRef> cgContext(
Ben Wagner428ab732018-04-02 15:44:54 -0400390 CGBitmapContextCreate(&bitmap, 16, 16, 8, 16*4, colorspace.get(), BITMAP_INFO_RGB));
391
392 UniqueCFRef<CGDataProviderRef> data(
393 CGDataProviderCreateWithData(nullptr, kSpiderSymbol_ttf,
394 SK_ARRAY_COUNT(kSpiderSymbol_ttf), nullptr));
395 UniqueCFRef<CGFontRef> cgFont(CGFontCreateWithDataProvider(data.get()));
396 SkASSERT(cgFont);
397 UniqueCFRef<CTFontRef> ctFont(
398 CTFontCreateWithGraphicsFont(cgFont.get(), 16, nullptr, nullptr));
399 SkASSERT(ctFont);
400
Mike Klein70284432017-09-19 13:07:27 -0400401 CGContextSetShouldSmoothFonts(cgContext.get(), true);
402 CGContextSetShouldAntialias(cgContext.get(), true);
403 CGContextSetTextDrawingMode(cgContext.get(), kCGTextFill);
404 CGContextSetGrayFillColor(cgContext.get(), 1, 1);
Ben Wagner428ab732018-04-02 15:44:54 -0400405 CGPoint point = CGPointMake(0, 3);
406 CGGlyph spiderGlyph = 3;
407 CTFontDrawGlyphs(ctFont.get(), &spiderGlyph, &point, 1, cgContext.get());
ccameronf8ee5b42016-10-04 15:02:02 -0700408
Ben Wagner428ab732018-04-02 15:44:54 -0400409 // For debugging.
410 //UniqueCFRef<CGImageRef> image(CGBitmapContextCreateImage(cgContext.get()));
411
412 for (int x = 0; x < 16; ++x) {
413 for (int y = 0; y < 16; ++y) {
414 uint32_t pixel = bitmap[x][y];
415 uint32_t r = (pixel >> 16) & 0xFF;
416 uint32_t g = (pixel >> 8) & 0xFF;
417 uint32_t b = (pixel >> 0) & 0xFF;
418 if (r != g || r != b) {
419 return true;
420 }
421 }
422 }
423 return false;
Mike Klein70284432017-09-19 13:07:27 -0400424 }();
425 return gSupportsLCD;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000426}
427
428class Offscreen {
429public:
bungeman34902632014-12-10 21:43:27 -0800430 Offscreen()
halcanary96fcdcc2015-08-27 07:41:13 -0700431 : fRGBSpace(nullptr)
432 , fCG(nullptr)
bungeman34902632014-12-10 21:43:27 -0800433 , fDoAA(false)
434 , fDoLCD(false)
435 {
436 fSize.set(0, 0);
437 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000438
439 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
bungemanc292b5f2016-12-20 12:04:29 -0500440 CGGlyph glyphID, size_t* rowBytesPtr, bool generateA8FromLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000441
442private:
443 enum {
444 kSize = 32 * 32 * sizeof(CGRGBPixel)
445 };
446 SkAutoSMalloc<kSize> fImageStorage;
bungemanc292b5f2016-12-20 12:04:29 -0500447 UniqueCFRef<CGColorSpaceRef> fRGBSpace;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000448
449 // cached state
bungemanc292b5f2016-12-20 12:04:29 -0500450 UniqueCFRef<CGContextRef> fCG;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000451 SkISize fSize;
452 bool fDoAA;
453 bool fDoLCD;
454
455 static int RoundSize(int dimension) {
456 return SkNextPow2(dimension);
457 }
458};
459
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000460///////////////////////////////////////////////////////////////////////////////
461
bungeman6e45bda2016-07-25 15:11:49 -0700462static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
bungemana4c4a2d2014-10-20 13:33:19 -0700463 CFNumberRef num;
464 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
bungemanc292b5f2016-12-20 12:04:29 -0500465 && CFNumberIsFloatType(num)
466 && CFNumberGetValue(num, kCFNumberCGFloatType, value);
bungemana4c4a2d2014-10-20 13:33:19 -0700467}
468
bungeman6e45bda2016-07-25 15:11:49 -0700469template <typename S, typename D, typename C> struct LinearInterpolater {
470 struct Mapping {
471 S src_val;
472 D dst_val;
473 };
474 constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
475 : fMapping(mapping), fMappingCount(mappingCount) {}
476
477 static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
478 SkASSERT(src_min < src_max);
479 SkASSERT(dst_min <= dst_max);
480 return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000481 }
bungeman6e45bda2016-07-25 15:11:49 -0700482
bungemanf1ef1fa2017-02-09 14:23:46 -0500483 D map(S val) const {
bungeman6e45bda2016-07-25 15:11:49 -0700484 // -Inf to [0]
485 if (val < fMapping[0].src_val) {
486 return fMapping[0].dst_val;
487 }
488
489 // Linear from [i] to [i+1]
490 for (int i = 0; i < fMappingCount - 1; ++i) {
491 if (val < fMapping[i+1].src_val) {
492 return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
493 fMapping[i].dst_val, fMapping[i+1].dst_val);
494 }
495 }
496
497 // From [n] to +Inf
498 // if (fcweight < Inf)
499 return fMapping[fMappingCount - 1].dst_val;
500 }
501
502 Mapping const * fMapping;
503 int fMappingCount;
504};
505
506struct RoundCGFloatToInt {
507 int operator()(CGFloat s) { return s + 0.5; }
508};
bungemanf1ef1fa2017-02-09 14:23:46 -0500509struct CGFloatIdentity {
510 CGFloat operator()(CGFloat s) { return s; }
511};
512
bungeman3e306f62017-03-29 11:32:02 -0400513/** Returns the [-1, 1] CTFontDescriptor weights for the
514 * <0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000> CSS weights.
515 *
516 * It is assumed that the values will be interpolated linearly between these points.
517 * NSFontWeightXXX were added in 10.11, appear in 10.10, but do not appear in 10.9.
518 * The actual values appear to be stable, but they may change in the future without notice.
519 */
520static CGFloat(&get_NSFontWeight_mapping())[11] {
521
522 // Declarations in <AppKit/AppKit.h> on macOS, <UIKit/UIKit.h> on iOS
523#ifdef SK_BUILD_FOR_MAC
524# define SK_KIT_FONT_WEIGHT_PREFIX "NS"
525#endif
526#ifdef SK_BUILD_FOR_IOS
527# define SK_KIT_FONT_WEIGHT_PREFIX "UI"
528#endif
529 static constexpr struct {
530 CGFloat defaultValue;
531 const char* name;
532 } nsFontWeightLoaderInfos[] = {
533 { -0.80f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightUltraLight" },
534 { -0.60f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightThin" },
535 { -0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightLight" },
536 { 0.00f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightRegular" },
537 { 0.23f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightMedium" },
538 { 0.30f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightSemibold" },
539 { 0.40f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBold" },
540 { 0.56f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightHeavy" },
541 { 0.62f, SK_KIT_FONT_WEIGHT_PREFIX "FontWeightBlack" },
542 };
543
544 static_assert(SK_ARRAY_COUNT(nsFontWeightLoaderInfos) == 9, "");
545 static CGFloat nsFontWeights[11];
546 static SkOnce once;
547 once([&] {
548 size_t i = 0;
549 nsFontWeights[i++] = -1.00;
550 for (const auto& nsFontWeightLoaderInfo : nsFontWeightLoaderInfos) {
551 void* nsFontWeightValuePtr = dlsym(RTLD_DEFAULT, nsFontWeightLoaderInfo.name);
552 if (nsFontWeightValuePtr) {
553 nsFontWeights[i++] = *(static_cast<CGFloat*>(nsFontWeightValuePtr));
554 } else {
555 nsFontWeights[i++] = nsFontWeightLoaderInfo.defaultValue;
556 }
557 }
558 nsFontWeights[i++] = 1.00;
559 });
560 return nsFontWeights;
561}
562
bungemanf1ef1fa2017-02-09 14:23:46 -0500563/** Convert the [0, 1000] CSS weight to [-1, 1] CTFontDescriptor weight (for system fonts).
564 *
565 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
566 * CTFont is native or created from a CGDataProvider.
567 */
568static CGFloat fontstyle_to_ct_weight(int fontstyleWeight) {
569 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
570
571 // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
572 // However, on this end we can't tell, so this is ignored.
573
bungeman3e306f62017-03-29 11:32:02 -0400574 static Interpolator::Mapping nativeWeightMappings[11];
575 static SkOnce once;
576 once([&] {
577 CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
578 for (int i = 0; i < 11; ++i) {
579 nativeWeightMappings[i].src_val = i * 100;
580 nativeWeightMappings[i].dst_val = nsFontWeights[i];
581 }
582 });
bungemanf1ef1fa2017-02-09 14:23:46 -0500583 static constexpr Interpolator nativeInterpolator(
584 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
585
586 return nativeInterpolator.map(fontstyleWeight);
587}
588
bungeman6e45bda2016-07-25 15:11:49 -0700589
bungeman2873c762017-01-13 11:40:21 -0500590/** Convert the [-1, 1] CTFontDescriptor weight to [0, 1000] CSS weight.
591 *
592 * The -1 to 1 weights reported by CTFontDescriptors have different mappings depending on if the
593 * CTFont is native or created from a CGDataProvider.
594 */
595static int ct_weight_to_fontstyle(CGFloat cgWeight, bool fromDataProvider) {
bungeman6e45bda2016-07-25 15:11:49 -0700596 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
597
bungeman6e45bda2016-07-25 15:11:49 -0700598 // 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 -0500599 // However, on this end we can't tell, so this is ignored.
600
601 /** This mapping for CGDataProvider created fonts is determined by creating font data with every
602 * weight, creating a CTFont, and asking the CTFont for its weight. See the TypefaceStyle test
603 * in tests/TypefaceTest.cpp for the code used to determine these values.
604 */
605 static constexpr Interpolator::Mapping dataProviderWeightMappings[] = {
bungeman6e45bda2016-07-25 15:11:49 -0700606 { -1.00, 0 },
607 { -0.70, 100 },
608 { -0.50, 200 },
609 { -0.23, 300 },
610 { 0.00, 400 },
611 { 0.20, 500 },
612 { 0.30, 600 },
613 { 0.40, 700 },
614 { 0.60, 800 },
615 { 0.80, 900 },
616 { 1.00, 1000 },
617 };
bungeman2873c762017-01-13 11:40:21 -0500618 static constexpr Interpolator dataProviderInterpolator(
619 dataProviderWeightMappings, SK_ARRAY_COUNT(dataProviderWeightMappings));
620
bungeman3e306f62017-03-29 11:32:02 -0400621 static Interpolator::Mapping nativeWeightMappings[11];
622 static SkOnce once;
623 once([&] {
624 CGFloat(&nsFontWeights)[11] = get_NSFontWeight_mapping();
625 for (int i = 0; i < 11; ++i) {
626 nativeWeightMappings[i].src_val = nsFontWeights[i];
627 nativeWeightMappings[i].dst_val = i * 100;
628 }
629 });
bungeman2873c762017-01-13 11:40:21 -0500630 static constexpr Interpolator nativeInterpolator(
631 nativeWeightMappings, SK_ARRAY_COUNT(nativeWeightMappings));
632
633 return fromDataProvider ? dataProviderInterpolator.map(cgWeight)
634 : nativeInterpolator.map(cgWeight);
bungemana4c4a2d2014-10-20 13:33:19 -0700635}
636
bungemanf1ef1fa2017-02-09 14:23:46 -0500637/** Convert the [0, 10] CSS weight to [-1, 1] CTFontDescriptor width. */
638static int fontstyle_to_ct_width(int fontstyleWidth) {
639 using Interpolator = LinearInterpolater<int, CGFloat, CGFloatIdentity>;
640
641 // Values determined by creating font data with every width, creating a CTFont,
642 // and asking the CTFont for its width. See TypefaceStyle test for basics.
643 static constexpr Interpolator::Mapping widthMappings[] = {
644 { 0, -0.5 },
645 { 10, 0.5 },
646 };
647 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
648 return interpolator.map(fontstyleWidth);
649}
650
651/** Convert the [-1, 1] CTFontDescriptor width to [0, 10] CSS weight. */
bungeman6e45bda2016-07-25 15:11:49 -0700652static int ct_width_to_fontstyle(CGFloat cgWidth) {
653 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
654
655 // Values determined by creating font data with every width, creating a CTFont,
656 // and asking the CTFont for its width. See TypefaceStyle test for basics.
657 static constexpr Interpolator::Mapping widthMappings[] = {
658 { -0.5, 0 },
659 { 0.5, 10 },
660 };
bungeman2873c762017-01-13 11:40:21 -0500661 static constexpr Interpolator interpolator(widthMappings, SK_ARRAY_COUNT(widthMappings));
662 return interpolator.map(cgWidth);
bungemana4c4a2d2014-10-20 13:33:19 -0700663}
664
bungeman2873c762017-01-13 11:40:21 -0500665static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc, bool fromDataProvider) {
bungemanc292b5f2016-12-20 12:04:29 -0500666 UniqueCFRef<CFTypeRef> fontTraits(CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
667 if (!fontTraits || CFDictionaryGetTypeID() != CFGetTypeID(fontTraits.get())) {
bungemana4c4a2d2014-10-20 13:33:19 -0700668 return SkFontStyle();
669 }
bungemanc292b5f2016-12-20 12:04:29 -0500670 UniqueCFRef<CFDictionaryRef> fontTraitsDict(static_cast<CFDictionaryRef>(fontTraits.release()));
bungemana4c4a2d2014-10-20 13:33:19 -0700671
bungeman6e45bda2016-07-25 15:11:49 -0700672 CGFloat weight, width, slant;
bungemanc292b5f2016-12-20 12:04:29 -0500673 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800674 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700675 }
bungemanc292b5f2016-12-20 12:04:29 -0500676 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontWidthTrait, &width)) {
bungemana4c4a2d2014-10-20 13:33:19 -0700677 width = 0;
678 }
bungemanc292b5f2016-12-20 12:04:29 -0500679 if (!find_dict_CGFloat(fontTraitsDict.get(), kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800680 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700681 }
682
bungeman2873c762017-01-13 11:40:21 -0500683 return SkFontStyle(ct_weight_to_fontstyle(weight, fromDataProvider),
bungeman6e45bda2016-07-25 15:11:49 -0700684 ct_width_to_fontstyle(width),
bungemana4c4a2d2014-10-20 13:33:19 -0700685 slant ? SkFontStyle::kItalic_Slant
bungemanb4bb7d82016-04-27 10:21:04 -0700686 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000687}
688
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000689class SkTypeface_Mac : public SkTypeface {
690public:
bungemanc292b5f2016-12-20 12:04:29 -0500691 SkTypeface_Mac(UniqueCFRef<CTFontRef> fontRef, UniqueCFRef<CFTypeRef> resourceRef,
bungeman78884012015-06-08 13:39:12 -0700692 const SkFontStyle& fs, bool isFixedPitch,
bungeman53d5c6e2016-04-08 07:22:29 -0700693 bool isLocalStream)
bungemane3aea102016-07-13 05:16:58 -0700694 : SkTypeface(fs, isFixedPitch)
bungemanc292b5f2016-12-20 12:04:29 -0500695 , fFontRef(std::move(fontRef))
696 , fOriginatingCFTypeRef(std::move(resourceRef))
697 , fHasColorGlyphs(
698 SkToBool(CTFontGetSymbolicTraits(fFontRef.get()) & kCTFontColorGlyphsTrait))
caseq26337e92014-06-30 12:14:52 -0700699 , fIsLocalStream(isLocalStream)
reed@google.comce8b3de2013-03-26 19:30:16 +0000700 {
bungemanc292b5f2016-12-20 12:04:29 -0500701 SkASSERT(fFontRef);
reed@google.comce8b3de2013-03-26 19:30:16 +0000702 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000703
bungemanc292b5f2016-12-20 12:04:29 -0500704 UniqueCFRef<CTFontRef> fFontRef;
705 UniqueCFRef<CFTypeRef> fOriginatingCFTypeRef;
bungemane3bea5c2015-04-07 07:34:36 -0700706 const bool fHasColorGlyphs;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000707
708protected:
mtklein36352bf2015-03-25 18:17:31 -0700709 int onGetUPEM() const override;
710 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
bungemanf93d7112016-09-16 06:24:20 -0700711 std::unique_ptr<SkFontData> onMakeFontData() const override;
Ben Wagnerfc497342017-02-24 11:15:26 -0500712 int onGetVariationDesignPosition(SkFontArguments::VariationPosition::Coordinate coordinates[],
713 int coordinateCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700714 void onGetFamilyName(SkString* familyName) const override;
715 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
716 int onGetTableTags(SkFontTableTag tags[]) const override;
bungemanc292b5f2016-12-20 12:04:29 -0500717 size_t onGetTableData(SkFontTableTag, size_t offset, size_t length, void* data) const override;
reeda9322c22016-04-12 06:47:05 -0700718 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
719 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700720 void onFilterRec(SkScalerContextRec*) const override;
721 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
Hal Canary209e4b12017-05-04 14:23:55 -0400722 std::unique_ptr<SkAdvancedTypefaceMetrics> onGetAdvancedMetrics() const override;
bungemanc292b5f2016-12-20 12:04:29 -0500723 int onCharsToGlyphs(const void* chars, Encoding,
724 uint16_t glyphs[], int glyphCount) const override;
mtklein36352bf2015-03-25 18:17:31 -0700725 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000726
Mike Reed342a9fa2017-05-03 15:48:22 -0400727 void* onGetCTFontRef() const override { return (void*)fFontRef.get(); }
728
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000729private:
caseq26337e92014-06-30 12:14:52 -0700730 bool fIsLocalStream;
reed@google.comce8b3de2013-03-26 19:30:16 +0000731
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000732 typedef SkTypeface INHERITED;
733};
734
bungeman82a455f2016-04-14 08:04:45 -0700735static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
bungeman64fb51d2015-05-04 12:03:50 -0700736 CTFontRef self = (CTFontRef)context;
Mike Reed342a9fa2017-05-03 15:48:22 -0400737 CTFontRef other = (CTFontRef)cached->internal_private_getCTFontRef();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000738
bungeman64fb51d2015-05-04 12:03:50 -0700739 return CFEqual(self, other);
740}
741
bungemanc292b5f2016-12-20 12:04:29 -0500742/** Creates a typeface, searching the cache if isLocalStream is false. */
Mike Reed59227392017-09-26 09:46:08 -0400743static sk_sp<SkTypeface> create_from_CTFontRef(UniqueCFRef<CTFontRef> font,
744 UniqueCFRef<CFTypeRef> resource,
745 bool isLocalStream) {
bungemanc292b5f2016-12-20 12:04:29 -0500746 SkASSERT(font);
bungemanbea97482016-08-24 08:29:50 -0700747
748 if (!isLocalStream) {
749 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)font.get());
750 if (face) {
Mike Reed59227392017-09-26 09:46:08 -0400751 return sk_sp<SkTypeface>(face);
bungemanbea97482016-08-24 08:29:50 -0700752 }
753 }
754
bungemanc292b5f2016-12-20 12:04:29 -0500755 UniqueCFRef<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font.get()));
bungeman2873c762017-01-13 11:40:21 -0500756 SkFontStyle style = fontstyle_from_descriptor(desc.get(), isLocalStream);
bungemanc292b5f2016-12-20 12:04:29 -0500757 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font.get());
bungemanbea97482016-08-24 08:29:50 -0700758 bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
759
bungemanc292b5f2016-12-20 12:04:29 -0500760 SkTypeface* face = new SkTypeface_Mac(std::move(font), std::move(resource),
bungemanbea97482016-08-24 08:29:50 -0700761 style, isFixedPitch, isLocalStream);
762 if (!isLocalStream) {
763 SkTypefaceCache::Add(face);
764 }
Mike Reed59227392017-09-26 09:46:08 -0400765 return sk_sp<SkTypeface>(face);
bungemanbea97482016-08-24 08:29:50 -0700766}
767
768/** Creates a typeface from a descriptor, searching the cache. */
Mike Reed59227392017-09-26 09:46:08 -0400769static sk_sp<SkTypeface> create_from_desc(CTFontDescriptorRef desc) {
bungemanc292b5f2016-12-20 12:04:29 -0500770 UniqueCFRef<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -0700771 if (!ctFont) {
772 return nullptr;
773 }
774
bungemanc292b5f2016-12-20 12:04:29 -0500775 return create_from_CTFontRef(std::move(ctFont), nullptr, false);
bungemanbea97482016-08-24 08:29:50 -0700776}
777
bungemanc292b5f2016-12-20 12:04:29 -0500778static UniqueCFRef<CTFontDescriptorRef> create_descriptor(const char familyName[],
779 const SkFontStyle& style) {
bungemanc292b5f2016-12-20 12:04:29 -0500780 UniqueCFRef<CFMutableDictionaryRef> cfAttributes(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000781 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
782 &kCFTypeDictionaryKeyCallBacks,
783 &kCFTypeDictionaryValueCallBacks));
784
bungemanc292b5f2016-12-20 12:04:29 -0500785 UniqueCFRef<CFMutableDictionaryRef> cfTraits(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000786 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
787 &kCFTypeDictionaryKeyCallBacks,
788 &kCFTypeDictionaryValueCallBacks));
789
bungeman83f1f442017-02-06 13:21:33 -0500790 if (!cfAttributes || !cfTraits) {
halcanary96fcdcc2015-08-27 07:41:13 -0700791 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000792 }
793
bungemanf1ef1fa2017-02-09 14:23:46 -0500794 // CTFontTraits (symbolic)
bungeman83f1f442017-02-06 13:21:33 -0500795 CTFontSymbolicTraits ctFontTraits = 0;
796 if (style.weight() >= SkFontStyle::kBold_Weight) {
797 ctFontTraits |= kCTFontBoldTrait;
798 }
799 if (style.slant() != SkFontStyle::kUpright_Slant) {
800 ctFontTraits |= kCTFontItalicTrait;
801 }
802 UniqueCFRef<CFNumberRef> cfFontTraits(
803 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
804 if (cfFontTraits) {
805 CFDictionaryAddValue(cfTraits.get(), kCTFontSymbolicTrait, cfFontTraits.get());
bungeman83f1f442017-02-06 13:21:33 -0500806 }
bungemanf1ef1fa2017-02-09 14:23:46 -0500807 // CTFontTraits (weight)
808 CGFloat ctWeight = fontstyle_to_ct_weight(style.weight());
809 UniqueCFRef<CFNumberRef> cfFontWeight(
810 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWeight));
811 if (cfFontWeight) {
812 CFDictionaryAddValue(cfTraits.get(), kCTFontWeightTrait, cfFontWeight.get());
813 }
814 // CTFontTraits (width)
815 CGFloat ctWidth = fontstyle_to_ct_width(style.weight());
816 UniqueCFRef<CFNumberRef> cfFontWidth(
817 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctWidth));
818 if (cfFontWidth) {
819 CFDictionaryAddValue(cfTraits.get(), kCTFontWidthTrait, cfFontWidth.get());
820 }
821 // CTFontTraits (slant)
822 CGFloat ctSlant = style.slant() == SkFontStyle::kUpright_Slant ? 0 : 1;
823 UniqueCFRef<CFNumberRef> cfFontSlant(
824 CFNumberCreate(kCFAllocatorDefault, kCFNumberCGFloatType, &ctSlant));
825 if (cfFontSlant) {
826 CFDictionaryAddValue(cfTraits.get(), kCTFontSlantTrait, cfFontSlant.get());
827 }
828 // CTFontTraits
829 CFDictionaryAddValue(cfAttributes.get(), kCTFontTraitsAttribute, cfTraits.get());
bungeman83f1f442017-02-06 13:21:33 -0500830
831 // CTFontFamilyName
832 if (familyName) {
833 UniqueCFRef<CFStringRef> cfFontName = make_CFString(familyName);
834 if (cfFontName) {
835 CFDictionaryAddValue(cfAttributes.get(), kCTFontFamilyNameAttribute, cfFontName.get());
836 }
837 }
bungeman64fb51d2015-05-04 12:03:50 -0700838
bungemanc292b5f2016-12-20 12:04:29 -0500839 return UniqueCFRef<CTFontDescriptorRef>(
840 CTFontDescriptorCreateWithAttributes(cfAttributes.get()));
bungemanbea97482016-08-24 08:29:50 -0700841}
842
843/** Creates a typeface from a name, searching the cache. */
Mike Reed59227392017-09-26 09:46:08 -0400844static sk_sp<SkTypeface> create_from_name(const char familyName[], const SkFontStyle& style) {
bungemanc292b5f2016-12-20 12:04:29 -0500845 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
bungemanbea97482016-08-24 08:29:50 -0700846 if (!desc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700847 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700848 }
bungemanc292b5f2016-12-20 12:04:29 -0500849 return create_from_desc(desc.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000850}
851
852///////////////////////////////////////////////////////////////////////////////
853
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000854/* This function is visible on the outside. It first searches the cache, and if
855 * not found, returns a new entry (after adding it to the cache).
856 */
bungemanc292b5f2016-12-20 12:04:29 -0500857SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef font, CFTypeRef resource) {
858 CFRetain(font);
859 if (resource) {
860 CFRetain(resource);
bungeman53d5c6e2016-04-08 07:22:29 -0700861 }
bungemanc292b5f2016-12-20 12:04:29 -0500862 return create_from_CTFontRef(UniqueCFRef<CTFontRef>(font),
863 UniqueCFRef<CFTypeRef>(resource),
Mike Reed59227392017-09-26 09:46:08 -0400864 false).release();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000865}
866
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000867static const char* map_css_names(const char* name) {
868 static const struct {
869 const char* fFrom; // name the caller specified
870 const char* fTo; // "canonical" name we map to
871 } gPairs[] = {
872 { "sans-serif", "Helvetica" },
873 { "serif", "Times" },
874 { "monospace", "Courier" }
875 };
876
877 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
878 if (strcmp(name, gPairs[i].fFrom) == 0) {
879 return gPairs[i].fTo;
880 }
881 }
882 return name; // no change
883}
884
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000885///////////////////////////////////////////////////////////////////////////////
886
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000887class SkScalerContext_Mac : public SkScalerContext {
888public:
bungeman7cfd46a2016-10-20 16:06:52 -0400889 SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000890
891protected:
mtklein36352bf2015-03-25 18:17:31 -0700892 unsigned generateGlyphCount(void) override;
893 uint16_t generateCharToGlyph(SkUnichar uni) override;
894 void generateAdvance(SkGlyph* glyph) override;
895 void generateMetrics(SkGlyph* glyph) override;
896 void generateImage(const SkGlyph& glyph) override;
Ben Wagner5ddb3082018-03-29 11:18:06 -0400897 bool generatePath(SkGlyphID glyph, SkPath* path) override;
mtklein36352bf2015-03-25 18:17:31 -0700898 void generateFontMetrics(SkPaint::FontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000899
900private:
901 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000902
bungeman@google.comcefd9812013-05-15 15:07:32 +0000903 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
904 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000905
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000906 Offscreen fOffscreen;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000907
bungeman3b4b66c2015-01-08 08:33:44 -0800908 /** Unrotated variant of fCTFont.
909 *
910 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
911 * advances, but always sets the height to 0. This font is used to get the advances of the
912 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000913 *
914 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
bungeman18ec7b92017-02-09 17:15:59 -0500915 * This makes kCTFontOrientationDefault dangerous, because the metrics from
916 * kCTFontOrientationHorizontal are in a different space from kCTFontOrientationVertical.
917 * With kCTFontOrientationVertical the advances must be unrotated.
bungemanef27ce32015-10-29 09:30:32 -0700918 *
919 * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
920 * different underlying font data. As a result, avoid ever creating more than one CTFont per
921 * SkScalerContext to ensure that only one CTFont is used.
922 *
923 * As a result of the above (and other constraints) this font contains the size, but not the
924 * transform. The transform must always be applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000925 */
bungemanc292b5f2016-12-20 12:04:29 -0500926 UniqueCFRef<CTFontRef> fCTFont;
bungemanef27ce32015-10-29 09:30:32 -0700927
928 /** The transform without the font size. */
929 CGAffineTransform fTransform;
930 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000931
bungemanc292b5f2016-12-20 12:04:29 -0500932 UniqueCFRef<CGFontRef> fCGFont;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000933 uint16_t fGlyphCount;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000934 const bool fDoSubPosition;
935 const bool fVertical;
936
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000937 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000938
939 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000940};
941
bungeman7cbeaae2015-09-22 09:54:56 -0700942// CTFontCreateCopyWithAttributes or CTFontCreateCopyWithSymbolicTraits cannot be used on 10.10
bungeman05846312015-09-23 12:51:28 -0700943// and later, as they will return different underlying fonts depending on the size requested.
944// It is not possible to use descriptors with CTFontCreateWithFontDescriptor, since that does not
945// work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
946// CGFont.
bungemanc292b5f2016-12-20 12:04:29 -0500947static UniqueCFRef<CTFontRef> ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
948 const CGAffineTransform* transform)
bungeman7cbeaae2015-09-22 09:54:56 -0700949{
bungemanc292b5f2016-12-20 12:04:29 -0500950 UniqueCFRef<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700951
bungeman05846312015-09-23 12:51:28 -0700952 // The last parameter (CTFontDescriptorRef attributes) *must* be nullptr.
953 // If non-nullptr then with fonts with variation axes, the copy will fail in
954 // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
955 // which it quite obviously is not.
bungeman7cbeaae2015-09-22 09:54:56 -0700956
bungeman05846312015-09-23 12:51:28 -0700957 // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
958 // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
959 // the scaler context, since they aren't 'normal'.
bungemanc292b5f2016-12-20 12:04:29 -0500960 return UniqueCFRef<CTFontRef>(
961 CTFontCreateWithGraphicsFont(baseCGFont.get(), textSize, transform, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700962}
963
bungeman7cfd46a2016-10-20 16:06:52 -0400964SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
reeda9322c22016-04-12 06:47:05 -0700965 const SkScalerContextEffects& effects,
reed@google.com0da48612013-03-19 16:06:52 +0000966 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400967 : INHERITED(std::move(typeface), effects, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000968 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
969 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
970
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000971{
reedd0f41732015-07-10 12:08:38 -0700972 AUTO_CG_LOCK();
973
Mike Reed342a9fa2017-05-03 15:48:22 -0400974 CTFontRef ctFont = (CTFontRef)this->getTypeface()->internal_private_getCTFontRef();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000975 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000976 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
977 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000978
bungeman34902632014-12-10 21:43:27 -0800979 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
980 // As a result, it is necessary to know the actual device size and request that.
981 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800982 SkMatrix skTransform;
bungemane55131c2016-08-24 12:01:31 -0700983 bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
bungeman4ec46aa2017-02-15 17:49:12 -0500984 &scale, &skTransform, nullptr, nullptr, nullptr);
bungemanaae30912015-03-02 13:43:26 -0800985 fTransform = MatrixToCGAffineTransform(skTransform);
bungeman1f0e78d2016-08-23 13:19:01 -0700986 // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
987 // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
bungemane55131c2016-08-24 12:01:31 -0700988 if (invertible) {
bungeman1f0e78d2016-08-23 13:19:01 -0700989 fInvTransform = CGAffineTransformInvert(fTransform);
990 } else {
991 fInvTransform = fTransform;
992 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000993
bungemanbe2284d2014-11-25 08:08:09 -0800994 // The transform contains everything except the requested text size.
995 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -0800996 CGFloat textSize = ScalarToCG(scale.y());
bungemanc292b5f2016-12-20 12:04:29 -0500997 fCTFont = ctfont_create_exact_copy(ctFont, textSize, nullptr);
998 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont.get(), nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000999}
1000
1001CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
1002 CGGlyph glyphID, size_t* rowBytesPtr,
mtkleinbbd40182015-09-08 08:19:33 -07001003 bool generateA8FromLCD) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001004 if (!fRGBSpace) {
1005 //It doesn't appear to matter what color space is specified.
1006 //Regular blends and antialiased text are always (s*a + d*(1-a))
1007 //and smoothed text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -07001008 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001009 }
1010
1011 // default to kBW_Format
1012 bool doAA = false;
1013 bool doLCD = false;
1014
1015 if (SkMask::kBW_Format != glyph.fMaskFormat) {
1016 doLCD = true;
1017 doAA = true;
1018 }
1019
1020 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1021 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
1022 doLCD = false;
1023 doAA = true;
1024 }
1025
bungeman34902632014-12-10 21:43:27 -08001026 // If this font might have color glyphs, disable LCD as there's no way to support it.
1027 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
bungemane280d062016-03-24 11:27:05 -07001028 // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
bungeman34902632014-12-10 21:43:27 -08001029 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
1030 doLCD = false;
1031 }
1032
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001033 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
1034 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
1035 if (fSize.fWidth < glyph.fWidth) {
1036 fSize.fWidth = RoundSize(glyph.fWidth);
1037 }
1038 if (fSize.fHeight < glyph.fHeight) {
1039 fSize.fHeight = RoundSize(glyph.fHeight);
1040 }
1041
1042 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
1043 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -08001044 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
1045 ? kCGImageAlphaPremultipliedFirst
1046 : kCGImageAlphaNoneSkipFirst;
1047 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -07001048 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungemanc292b5f2016-12-20 12:04:29 -05001049 rowBytes, fRGBSpace.get(), bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001050
bungeman34902632014-12-10 21:43:27 -08001051 // Skia handles quantization and subpixel positioning,
1052 // so disable quantization and enabe subpixel positioning in CG.
bungemanc292b5f2016-12-20 12:04:29 -05001053 CGContextSetAllowsFontSubpixelQuantization(fCG.get(), false);
1054 CGContextSetShouldSubpixelQuantizeFonts(fCG.get(), false);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001055
bungeman@google.comcefd9812013-05-15 15:07:32 +00001056 // Because CG always draws from the horizontal baseline,
1057 // if there is a non-integral translation from the horizontal origin to the vertical origin,
1058 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungemanc292b5f2016-12-20 12:04:29 -05001059 CGContextSetAllowsFontSubpixelPositioning(fCG.get(), true);
1060 CGContextSetShouldSubpixelPositionFonts(fCG.get(), true);
bungeman34902632014-12-10 21:43:27 -08001061
bungemanc292b5f2016-12-20 12:04:29 -05001062 CGContextSetTextDrawingMode(fCG.get(), kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001063
bungemane280d062016-03-24 11:27:05 -07001064 // Draw black on white to create mask. (Special path exists to speed this up in CG.)
bungemanc292b5f2016-12-20 12:04:29 -05001065 CGContextSetGrayFillColor(fCG.get(), 0.0f, 1.0f);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001066
1067 // force our checks below to happen
1068 fDoAA = !doAA;
1069 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -08001070
bungemanc292b5f2016-12-20 12:04:29 -05001071 CGContextSetTextMatrix(fCG.get(), context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001072 }
1073
1074 if (fDoAA != doAA) {
bungemanc292b5f2016-12-20 12:04:29 -05001075 CGContextSetShouldAntialias(fCG.get(), doAA);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001076 fDoAA = doAA;
1077 }
1078 if (fDoLCD != doLCD) {
bungemanc292b5f2016-12-20 12:04:29 -05001079 CGContextSetShouldSmoothFonts(fCG.get(), doLCD);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001080 fDoLCD = doLCD;
1081 }
1082
1083 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
1084 // skip rows based on the glyph's height
1085 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
1086
bungemane280d062016-03-24 11:27:05 -07001087 // Erase to white (or transparent black if it's a color glyph, to not composite against white).
1088 uint32_t bgColor = (SkMask::kARGB32_Format != glyph.fMaskFormat) ? 0xFFFFFFFF : 0x00000000;
1089 sk_memset_rect32(image, bgColor, glyph.fWidth, glyph.fHeight, rowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001090
1091 float subX = 0;
1092 float subY = 0;
1093 if (context.fDoSubPosition) {
1094 subX = SkFixedToFloat(glyph.getSubXFixed());
1095 subY = SkFixedToFloat(glyph.getSubYFixed());
1096 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001097
bungeman34902632014-12-10 21:43:27 -08001098 // CoreText and CoreGraphics always draw using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001099 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001100 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001101 context.getVerticalOffset(glyphID, &offset);
1102 subX += offset.fX;
1103 subY += offset.fY;
1104 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001105
bungeman34902632014-12-10 21:43:27 -08001106 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -08001107 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
1108 // 'positions' which are in text space. The glyph location (in device space) must be
1109 // mapped into text space, so that CG can convert it back into device space.
Hal Canary55325b72017-01-03 10:36:17 -05001110 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
bungeman1b5f25c2015-06-08 14:32:24 -07001111 //
bungemanaae30912015-03-02 13:43:26 -08001112 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
1113 // So always make the font transform identity and place the transform on the context.
1114 point = CGPointApplyAffineTransform(point, context.fInvTransform);
1115
bungemanc292b5f2016-12-20 12:04:29 -05001116 CTFontDrawGlyphs(context.fCTFont.get(), &glyphID, &point, 1, fCG.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001117
1118 SkASSERT(rowBytesPtr);
1119 *rowBytesPtr = rowBytes;
1120 return image;
1121}
1122
bungeman@google.comcefd9812013-05-15 15:07:32 +00001123void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
bungemanc2ba75b2016-12-14 16:53:00 -05001124 // CTFontGetVerticalTranslationsForGlyphs produces cgVertOffset in CG units (pixels, y up).
bungeman@google.comcefd9812013-05-15 15:07:32 +00001125 CGSize cgVertOffset;
bungemanc292b5f2016-12-20 12:04:29 -05001126 CTFontGetVerticalTranslationsForGlyphs(fCTFont.get(), &glyphID, &cgVertOffset, 1);
bungemanef27ce32015-10-29 09:30:32 -07001127 cgVertOffset = CGSizeApplyAffineTransform(cgVertOffset, fTransform);
1128 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
1129 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
1130 skVertOffset.fY = -skVertOffset.fY;
bungeman@google.comcefd9812013-05-15 15:07:32 +00001131 *offset = skVertOffset;
1132}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001133
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001134unsigned SkScalerContext_Mac::generateGlyphCount(void) {
1135 return fGlyphCount;
1136}
1137
1138uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
reedd0f41732015-07-10 12:08:38 -07001139 AUTO_CG_LOCK();
1140
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001141 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001142 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001143
1144 // Get the glyph
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001145 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar);
1146 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001147
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001148 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
1149 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1150 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
bungemanc292b5f2016-12-20 12:04:29 -05001151 CTFontGetGlyphsForCharacters(fCTFont.get(), theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001152 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001153}
1154
1155void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
1156 this->generateMetrics(glyph);
1157}
1158
bungeman@google.comcefd9812013-05-15 15:07:32 +00001159void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
reedd0f41732015-07-10 12:08:38 -07001160 AUTO_CG_LOCK();
1161
djsollen1b277042014-08-06 06:58:06 -07001162 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +00001163 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001164
bungeman@google.comcefd9812013-05-15 15:07:32 +00001165 // The following block produces cgAdvance in CG units (pixels, y up).
1166 CGSize cgAdvance;
1167 if (fVertical) {
bungeman18ec7b92017-02-09 17:15:59 -05001168 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationVertical,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001169 &cgGlyph, &cgAdvance, 1);
bungeman3b4b66c2015-01-08 08:33:44 -08001170 // Vertical advances are returned as widths instead of heights.
1171 SkTSwap(cgAdvance.height, cgAdvance.width);
1172 cgAdvance.height = -cgAdvance.height;
bungeman@google.comcefd9812013-05-15 15:07:32 +00001173 } else {
bungeman18ec7b92017-02-09 17:15:59 -05001174 CTFontGetAdvancesForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001175 &cgGlyph, &cgAdvance, 1);
1176 }
bungemanef27ce32015-10-29 09:30:32 -07001177 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001178 glyph->fAdvanceX = CGToFloat(cgAdvance.width);
1179 glyph->fAdvanceY = -CGToFloat(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001180
bungeman@google.comcefd9812013-05-15 15:07:32 +00001181 // The following produces skBounds in SkGlyph units (pixels, y down),
1182 // or returns early if skBounds would be empty.
1183 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001184
bungemanc2ba75b2016-12-14 16:53:00 -05001185 // Glyphs are always drawn from the horizontal origin. The caller must manually use the result
1186 // of CTFontGetVerticalTranslationsForGlyphs to calculate where to draw the glyph for vertical
1187 // glyphs. As a result, always get the horizontal bounds of a glyph and translate it if the
1188 // glyph is vertical. This avoids any diagreement between the various means of retrieving
1189 // vertical metrics.
bungeman@google.comcefd9812013-05-15 15:07:32 +00001190 {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001191 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1192 CGRect cgBounds;
bungeman18ec7b92017-02-09 17:15:59 -05001193 CTFontGetBoundingRectsForGlyphs(fCTFont.get(), kCTFontOrientationHorizontal,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001194 &cgGlyph, &cgBounds, 1);
bungemanef27ce32015-10-29 09:30:32 -07001195 cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001196
bungeman@google.comcefd9812013-05-15 15:07:32 +00001197 // BUG?
1198 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1199 // it should be empty. So, if we see a zero-advance, we check if it has an
1200 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1201 // is rare, so we won't incur a big performance cost for this extra check.
1202 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
bungemanc292b5f2016-12-20 12:04:29 -05001203 UniqueCFRef<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, nullptr));
1204 if (!path || CGPathIsEmpty(path.get())) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001205 return;
1206 }
1207 }
1208
1209 if (CGRectIsEmpty_inline(cgBounds)) {
1210 return;
1211 }
1212
1213 // Convert cgBounds to SkGlyph units (pixels, y down).
1214 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1215 cgBounds.size.width, cgBounds.size.height);
1216 }
1217
1218 if (fVertical) {
bungemanc2ba75b2016-12-14 16:53:00 -05001219 // Due to possible vertical bounds bugs and simplicity, skBounds is the horizontal bounds.
bungeman@google.comcefd9812013-05-15 15:07:32 +00001220 // Convert these horizontal bounds into vertical bounds.
1221 SkPoint offset;
bungemanc2ba75b2016-12-14 16:53:00 -05001222 this->getVerticalOffset(cgGlyph, &offset);
bungeman@google.comcefd9812013-05-15 15:07:32 +00001223 skBounds.offset(offset);
1224 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001225
bungeman@google.comcefd9812013-05-15 15:07:32 +00001226 // Currently the bounds are based on being rendered at (0,0).
1227 // The top left must not move, since that is the base from which subpixel positioning is offset.
1228 if (fDoSubPosition) {
1229 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1230 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1231 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001232
Mike Kleind94e00c2017-10-05 11:40:57 -04001233 // We're trying to pack left and top into int16_t,
1234 // and width and height into uint16_t, after outsetting by 1.
1235 if (!SkRect::MakeXYWH(-32767, -32767, 65535, 65535).contains(skBounds)) {
1236 return;
1237 }
1238
bungeman@google.comcefd9812013-05-15 15:07:32 +00001239 SkIRect skIBounds;
1240 skBounds.roundOut(&skIBounds);
1241 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1242 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1243 // is not currently known, as CG dilates the outlines by some percentage.
1244 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1245 skIBounds.outset(1, 1);
1246 glyph->fLeft = SkToS16(skIBounds.fLeft);
1247 glyph->fTop = SkToS16(skIBounds.fTop);
1248 glyph->fWidth = SkToU16(skIBounds.width());
1249 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001250}
1251
Cary Clarka4083c92017-09-15 11:59:23 -04001252#include "SkColorData.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001253
Ben Wagner7a0248f2017-10-18 11:30:56 -04001254static constexpr uint8_t sk_pow2_table(size_t i) {
1255 return SkToU8(((i * i + 128) / 255));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001256}
1257
1258/**
1259 * This will invert the gamma applied by CoreGraphics, so we can get linear
1260 * values.
1261 *
1262 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1263 * The color space used does not appear to affect this choice.
1264 */
Ben Wagner7a0248f2017-10-18 11:30:56 -04001265static constexpr auto gLinearCoverageFromCGLCDValue = SkMakeArray<256>(sk_pow2_table);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001266
1267static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1268 while (count > 0) {
1269 uint8_t mask = 0;
1270 for (int i = 7; i >= 0; --i) {
bungemane280d062016-03-24 11:27:05 -07001271 mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001272 if (0 == --count) {
1273 break;
1274 }
1275 }
1276 *dst++ = mask;
1277 }
1278}
1279
1280template<bool APPLY_PREBLEND>
1281static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
bungemane280d062016-03-24 11:27:05 -07001282 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
1283 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
1284 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
bungeman3b4b66c2015-01-08 08:33:44 -08001285 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1286#if SK_SHOW_TEXT_BLIT_COVERAGE
1287 lum = SkTMax(lum, (U8CPU)0x30);
1288#endif
1289 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001290}
1291template<bool APPLY_PREBLEND>
1292static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1293 const SkGlyph& glyph, const uint8_t* table8) {
1294 const int width = glyph.fWidth;
1295 size_t dstRB = glyph.rowBytes();
1296 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1297
1298 for (int y = 0; y < glyph.fHeight; y++) {
1299 for (int i = 0; i < width; ++i) {
1300 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1301 }
bungemanc292b5f2016-12-20 12:04:29 -05001302 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1303 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001304 }
1305}
1306
1307template<bool APPLY_PREBLEND>
1308static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1309 const uint8_t* tableG,
1310 const uint8_t* tableB) {
bungemane280d062016-03-24 11:27:05 -07001311 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
1312 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
1313 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001314#if SK_SHOW_TEXT_BLIT_COVERAGE
1315 r = SkTMax(r, (U8CPU)0x30);
1316 g = SkTMax(g, (U8CPU)0x30);
1317 b = SkTMax(b, (U8CPU)0x30);
1318#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001319 return SkPack888ToRGB16(r, g, b);
1320}
1321template<bool APPLY_PREBLEND>
1322static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1323 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1324 const int width = glyph.fWidth;
1325 size_t dstRB = glyph.rowBytes();
1326 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1327
1328 for (int y = 0; y < glyph.fHeight; y++) {
1329 for (int i = 0; i < width; i++) {
1330 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1331 }
bungemanc292b5f2016-12-20 12:04:29 -05001332 cgPixels = SkTAddOffset<const CGRGBPixel>(cgPixels, cgRowBytes);
1333 dst = SkTAddOffset<uint16_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001334 }
1335}
1336
bungeman34902632014-12-10 21:43:27 -08001337static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1338 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001339 U8CPU r = (rgb >> 16) & 0xFF;
1340 U8CPU g = (rgb >> 8) & 0xFF;
1341 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001342#if SK_SHOW_TEXT_BLIT_COVERAGE
1343 a = SkTMax(a, (U8CPU)0x30);
1344#endif
bungeman34902632014-12-10 21:43:27 -08001345 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001346}
reed@google.comf77b35d2013-05-02 20:39:44 +00001347
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001348void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
bungemanc292b5f2016-12-20 12:04:29 -05001349 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph.getGlyphID());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001350
1351 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1352 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1353
1354 // Draw the glyph
1355 size_t cgRowBytes;
1356 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
halcanary96fcdcc2015-08-27 07:41:13 -07001357 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001358 return;
1359 }
1360
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001361 // Fix the glyph
bungemanc292b5f2016-12-20 12:04:29 -05001362 if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
1363 (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD))
1364 {
Ben Wagner7a0248f2017-10-18 11:30:56 -04001365 const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001366
1367 //Note that the following cannot really be integrated into the
1368 //pre-blend, since we may not be applying the pre-blend; when we aren't
1369 //applying the pre-blend it means that a filter wants linear anyway.
1370 //Other code may also be applying the pre-blend, so we'd need another
1371 //one with this and one without.
1372 CGRGBPixel* addr = cgPixels;
1373 for (int y = 0; y < glyph.fHeight; ++y) {
1374 for (int x = 0; x < glyph.fWidth; ++x) {
1375 int r = (addr[x] >> 16) & 0xFF;
1376 int g = (addr[x] >> 8) & 0xFF;
1377 int b = (addr[x] >> 0) & 0xFF;
Ben Wagner7a0248f2017-10-18 11:30:56 -04001378 addr[x] = (linear[r] << 16) | (linear[g] << 8) | linear[b];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001379 }
bungemane280d062016-03-24 11:27:05 -07001380 addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001381 }
1382 }
1383
1384 // Convert glyph to mask
1385 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001386 case SkMask::kLCD16_Format: {
1387 if (fPreBlend.isApplicable()) {
1388 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1389 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1390 } else {
1391 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1392 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1393 }
1394 } break;
1395 case SkMask::kA8_Format: {
1396 if (fPreBlend.isApplicable()) {
1397 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1398 } else {
1399 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1400 }
1401 } break;
1402 case SkMask::kBW_Format: {
1403 const int width = glyph.fWidth;
1404 size_t dstRB = glyph.rowBytes();
1405 uint8_t* dst = (uint8_t*)glyph.fImage;
1406 for (int y = 0; y < glyph.fHeight; y++) {
1407 cgpixels_to_bits(dst, cgPixels, width);
bungemanc292b5f2016-12-20 12:04:29 -05001408 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1409 dst = SkTAddOffset<uint8_t>(dst, dstRB);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001410 }
1411 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001412 case SkMask::kARGB32_Format: {
1413 const int width = glyph.fWidth;
1414 size_t dstRB = glyph.rowBytes();
1415 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1416 for (int y = 0; y < glyph.fHeight; y++) {
1417 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001418 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001419 }
bungemanc292b5f2016-12-20 12:04:29 -05001420 cgPixels = SkTAddOffset<CGRGBPixel>(cgPixels, cgRowBytes);
1421 dst = SkTAddOffset<SkPMColor>(dst, dstRB);
reed@google.comf77b35d2013-05-02 20:39:44 +00001422 }
1423 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001424 default:
1425 SkDEBUGFAIL("unexpected mask format");
1426 break;
1427 }
1428}
1429
1430/*
1431 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1432 * seems sufficient, and possibly even correct, to allow the hinted outline
1433 * to be subpixel positioned.
1434 */
1435#define kScaleForSubPixelPositionHinting (4.0f)
1436
Ben Wagner5ddb3082018-03-29 11:18:06 -04001437bool SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001438 AUTO_CG_LOCK();
1439
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001440 SkScalar scaleX = SK_Scalar1;
1441 SkScalar scaleY = SK_Scalar1;
1442
bungemanef27ce32015-10-29 09:30:32 -07001443 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001444 /*
1445 * For subpixel positioning, we want to return an unhinted outline, so it
1446 * can be positioned nicely at fractional offsets. However, we special-case
1447 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1448 * we want to retain hinting in the direction orthogonal to the baseline.
1449 * e.g. for horizontal baseline, we want to retain hinting in Y.
1450 * The way we remove hinting is to scale the font by some value (4) in that
1451 * direction, ask for the path, and then scale the path back down.
1452 */
bungeman7cbeaae2015-09-22 09:54:56 -07001453 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001454 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001455 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001456 // now see if we need to restore hinting for axis-aligned baselines
bungeman27876bc2016-02-29 11:22:55 -08001457 switch (this->computeAxisAlignmentForHText()) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001458 case kX_SkAxisAlignment:
1459 scaleY = SK_Scalar1; // want hinting in the Y direction
1460 break;
1461 case kY_SkAxisAlignment:
1462 scaleX = SK_Scalar1; // want hinting in the X direction
1463 break;
1464 default:
1465 break;
1466 }
1467
bungemanef27ce32015-10-29 09:30:32 -07001468 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1469 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001470 }
1471
Ben Wagner6e9ac122016-11-11 14:31:06 -05001472 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
bungemanc292b5f2016-12-20 12:04:29 -05001473 UniqueCFRef<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont.get(), cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001474
1475 path->reset();
Ben Wagner5ddb3082018-03-29 11:18:06 -04001476 if (!cgPath) {
1477 return false;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001478 }
1479
Ben Wagner5ddb3082018-03-29 11:18:06 -04001480 CGPathApply(cgPath.get(), path, SkScalerContext_Mac::CTPathElement);
bungeman@google.comcefd9812013-05-15 15:07:32 +00001481 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001482 SkMatrix m;
1483 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1484 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001485 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001486 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001487 SkPoint offset;
1488 getVerticalOffset(cgGlyph, &offset);
1489 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001490 }
Ben Wagner5ddb3082018-03-29 11:18:06 -04001491 return true;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001492}
1493
bungeman41078062014-07-07 08:16:37 -07001494void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001495 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001496 return;
1497 }
1498
reedd0f41732015-07-10 12:08:38 -07001499 AUTO_CG_LOCK();
1500
bungemanc292b5f2016-12-20 12:04:29 -05001501 CGRect theBounds = CTFontGetBoundingBox(fCTFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001502
bungeman41078062014-07-07 08:16:37 -07001503 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001504 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont.get()));
1505 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001506 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
bungemanc292b5f2016-12-20 12:04:29 -05001507 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont.get()));
bungeman41078062014-07-07 08:16:37 -07001508 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1509 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1510 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001511 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungemanc292b5f2016-12-20 12:04:29 -05001512 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont.get()));
1513 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont.get()));
1514 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont.get()));
1515 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont.get()));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001516
Ben Wagner219f3622017-07-17 15:32:25 -04001517 metrics->fFlags = 0;
Ben Wagner3318da52017-03-23 14:01:22 -04001518 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThicknessIsValid_Flag;
bungeman41078062014-07-07 08:16:37 -07001519 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
bungeman4ec46aa2017-02-15 17:49:12 -05001520
1521 // See https://bugs.chromium.org/p/skia/issues/detail?id=6203
1522 // At least on 10.12.3 with memory based fonts the x-height is always 0.6666 of the ascent and
1523 // the cap-height is always 0.8888 of the ascent. It appears that the values from the 'OS/2'
1524 // table are read, but then overwritten if the font is not a system font. As a result, if there
1525 // is a valid 'OS/2' table available use the values from the table if they aren't too strange.
1526 struct OS2HeightMetrics {
1527 SK_OT_SHORT sxHeight;
1528 SK_OT_SHORT sCapHeight;
1529 } heights;
1530 size_t bytesRead = this->getTypeface()->getTableData(
1531 SkTEndian_SwapBE32(SkOTTableOS2::TAG), offsetof(SkOTTableOS2, version.v2.sxHeight),
1532 sizeof(heights), &heights);
1533 if (bytesRead == sizeof(heights)) {
1534 // 'fontSize' is correct because the entire resolved size is set by the constructor.
1535 CGFloat fontSize = CTFontGetSize(this->fCTFont.get());
1536 unsigned upem = CTFontGetUnitsPerEm(this->fCTFont.get());
1537 unsigned maxSaneHeight = upem * 2;
1538 uint16_t xHeight = SkEndian_SwapBE16(heights.sxHeight);
1539 if (xHeight && xHeight < maxSaneHeight) {
1540 metrics->fXHeight = CGToScalar(xHeight * fontSize / upem);
1541 }
1542 uint16_t capHeight = SkEndian_SwapBE16(heights.sCapHeight);
1543 if (capHeight && capHeight < maxSaneHeight) {
1544 metrics->fCapHeight = CGToScalar(capHeight * fontSize / upem);
1545 }
1546 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001547}
1548
1549void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1550 SkPath* skPath = (SkPath*)info;
1551
1552 // Process the path element
1553 switch (element->type) {
1554 case kCGPathElementMoveToPoint:
1555 skPath->moveTo(element->points[0].x, -element->points[0].y);
1556 break;
1557
1558 case kCGPathElementAddLineToPoint:
1559 skPath->lineTo(element->points[0].x, -element->points[0].y);
1560 break;
1561
1562 case kCGPathElementAddQuadCurveToPoint:
1563 skPath->quadTo(element->points[0].x, -element->points[0].y,
1564 element->points[1].x, -element->points[1].y);
1565 break;
1566
1567 case kCGPathElementAddCurveToPoint:
1568 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1569 element->points[1].x, -element->points[1].y,
1570 element->points[2].x, -element->points[2].y);
1571 break;
1572
1573 case kCGPathElementCloseSubpath:
1574 skPath->close();
1575 break;
1576
1577 default:
1578 SkDEBUGFAIL("Unknown path element!");
1579 break;
1580 }
1581}
1582
1583
1584///////////////////////////////////////////////////////////////////////////////
1585
halcanary96fcdcc2015-08-27 07:41:13 -07001586// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001587// Call must still manage its ownership of provider
Mike Reed59227392017-09-26 09:46:08 -04001588static sk_sp<SkTypeface> create_from_dataProvider(UniqueCFRef<CGDataProviderRef> provider,
1589 int ttcIndex) {
Ben Wagnerfc497342017-02-24 11:15:26 -05001590 if (ttcIndex != 0) {
1591 return nullptr;
1592 }
bungemanc292b5f2016-12-20 12:04:29 -05001593 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
1594 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07001595 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001596 }
bungemanc292b5f2016-12-20 12:04:29 -05001597 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg.get(), 0, nullptr, nullptr));
1598 if (!ct) {
1599 return nullptr;
1600 }
1601 return create_from_CTFontRef(std::move(ct), nullptr, true);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001602}
1603
bungemanc292b5f2016-12-20 12:04:29 -05001604// Web fonts added to the CTFont registry do not return their character set.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001605// Iterate through the font in this case. The existing caller caches the result,
1606// so the performance impact isn't too bad.
1607static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1608 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001609 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001610 SkUnichar* out = glyphToUnicode->begin();
1611 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1612 UniChar unichar = 0;
1613 while (glyphCount > 0) {
1614 CGGlyph glyph;
1615 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
Hal Canary266dcb02017-04-07 11:49:20 -04001616 if (out[glyph] != 0) {
1617 out[glyph] = unichar;
1618 --glyphCount;
1619 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001620 }
1621 if (++unichar == 0) {
1622 break;
1623 }
1624 }
1625}
1626
1627// Construct Glyph to Unicode table.
1628// Unicode code points that require conjugate pairs in utf16 are not
1629// supported.
1630static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1631 SkTDArray<SkUnichar>* glyphToUnicode) {
bungemanc292b5f2016-12-20 12:04:29 -05001632 UniqueCFRef<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001633 if (!charSet) {
1634 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1635 return;
1636 }
1637
bungemanc292b5f2016-12-20 12:04:29 -05001638 UniqueCFRef<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(nullptr, charSet.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001639 if (!bitmap) {
1640 return;
1641 }
bungemanc292b5f2016-12-20 12:04:29 -05001642 CFIndex length = CFDataGetLength(bitmap.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001643 if (!length) {
1644 return;
1645 }
1646 if (length > 8192) {
1647 // TODO: Add support for Unicode above 0xFFFF
1648 // Consider only the BMP portion of the Unicode character points.
1649 // The bitmap may contain other planes, up to plane 16.
1650 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1651 length = 8192;
1652 }
bungemanc292b5f2016-12-20 12:04:29 -05001653 const UInt8* bits = CFDataGetBytePtr(bitmap.get());
reed@google.com7fa2a652014-01-27 13:42:58 +00001654 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001655 SkUnichar* out = glyphToUnicode->begin();
1656 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1657 for (int i = 0; i < length; i++) {
1658 int mask = bits[i];
1659 if (!mask) {
1660 continue;
1661 }
1662 for (int j = 0; j < 8; j++) {
1663 CGGlyph glyph;
1664 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1665 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1666 out[glyph] = unichar;
1667 }
1668 }
1669 }
1670}
1671
halcanary96fcdcc2015-08-27 07:41:13 -07001672/** Assumes src and dst are not nullptr. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001673static void CFStringToSkString(CFStringRef src, SkString* dst) {
1674 // Reserve enough room for the worst-case string,
1675 // plus 1 byte for the trailing null.
1676 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1677 kCFStringEncodingUTF8) + 1;
1678 dst->resize(length);
1679 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1680 // Resize to the actual UTF-8 length used, stripping the null character.
1681 dst->resize(strlen(dst->c_str()));
1682}
1683
Hal Canary209e4b12017-05-04 14:23:55 -04001684std::unique_ptr<SkAdvancedTypefaceMetrics> SkTypeface_Mac::onGetAdvancedMetrics() const {
reed@google.com2689f612013-03-20 20:01:47 +00001685
reedd0f41732015-07-10 12:08:38 -07001686 AUTO_CG_LOCK();
1687
bungemanc292b5f2016-12-20 12:04:29 -05001688 UniqueCFRef<CTFontRef> ctFont =
1689 ctfont_create_exact_copy(fFontRef.get(), CTFontGetUnitsPerEm(fFontRef.get()), nullptr);
bungeman7cbeaae2015-09-22 09:54:56 -07001690
Hal Canary209e4b12017-05-04 14:23:55 -04001691 std::unique_ptr<SkAdvancedTypefaceMetrics> info(new SkAdvancedTypefaceMetrics);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001692
1693 {
bungemanc292b5f2016-12-20 12:04:29 -05001694 UniqueCFRef<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont.get()));
bungeman256b3512014-07-02 07:57:59 -07001695 if (fontName.get()) {
Hal Canary8031b322018-03-29 13:20:30 -07001696 CFStringToSkString(fontName.get(), &info->fPostScriptName);
1697 info->fFontName = info->fPostScriptName;
bungeman256b3512014-07-02 07:57:59 -07001698 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001699 }
1700
Ben Wagnerc6639052017-03-02 13:31:16 -05001701 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1702 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1703 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1704 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
1705 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont.get(), nullptr));
1706 if (cgFont) {
1707 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
1708 if (cgAxes && CFArrayGetCount(cgAxes.get()) > 0) {
1709 info->fFlags |= SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag;
1710 }
1711 }
1712
bungemanc292b5f2016-12-20 12:04:29 -05001713 CFIndex glyphCount = CTFontGetGlyphCount(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001714
Hal Canary209e4b12017-05-04 14:23:55 -04001715 populate_glyph_to_unicode(ctFont.get(), glyphCount, &info->fGlyphToUnicode);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001716
Hal Canary4a851ca2017-11-09 11:09:34 -05001717 SkOTTableOS2_V4::Type fsType;
1718 if (sizeof(fsType) == this->getTableData(SkTEndian_SwapBE32(SkOTTableOS2::TAG),
1719 offsetof(SkOTTableOS2_V4, fsType),
1720 sizeof(fsType),
1721 &fsType)) {
Hal Canarya865d252017-11-09 16:16:09 -05001722 SkOTUtils::SetAdvancedTypefaceFlags(fsType, info.get());
Hal Canary4a851ca2017-11-09 11:09:34 -05001723 }
1724
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001725 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1726 // fonts always have both glyf and loca tables. At the least, this is what
1727 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1728 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001729 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001730 return info;
1731 }
1732
1733 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
bungemanc292b5f2016-12-20 12:04:29 -05001734 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001735 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1736 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1737 }
1738 if (symbolicTraits & kCTFontItalicTrait) {
1739 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1740 }
1741 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001742 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1743 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1744 } else if (stylisticClass & kCTFontScriptsClass) {
1745 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1746 }
bungemanc292b5f2016-12-20 12:04:29 -05001747 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont.get());
1748 info->fAscent = (int16_t) CTFontGetAscent(ctFont.get());
1749 info->fDescent = (int16_t) CTFontGetDescent(ctFont.get());
1750 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont.get());
1751 CGRect bbox = CTFontGetBoundingBox(ctFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001752
1753 SkRect r;
1754 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1755 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1756 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1757 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1758
1759 r.roundOut(&(info->fBBox));
1760
1761 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1762 // This probably isn't very good with an italic font.
1763 int16_t min_width = SHRT_MAX;
1764 info->fStemV = 0;
1765 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1766 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1767 CGGlyph glyphs[count];
1768 CGRect boundingRects[count];
bungemanc292b5f2016-12-20 12:04:29 -05001769 if (CTFontGetGlyphsForCharacters(ctFont.get(), stem_chars, glyphs, count)) {
bungeman18ec7b92017-02-09 17:15:59 -05001770 CTFontGetBoundingRectsForGlyphs(ctFont.get(), kCTFontOrientationHorizontal,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001771 glyphs, boundingRects, count);
1772 for (size_t i = 0; i < count; i++) {
1773 int16_t width = (int16_t) boundingRects[i].size.width;
1774 if (width > 0 && width < min_width) {
1775 min_width = width;
1776 info->fStemV = min_width;
1777 }
1778 }
1779 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001780 return info;
1781}
1782
1783///////////////////////////////////////////////////////////////////////////////
1784
Ben Wagner93e4ea52018-04-05 11:57:17 -04001785static SK_SFNT_ULONG get_font_type_tag(CTFontRef ctFont) {
bungemanc292b5f2016-12-20 12:04:29 -05001786 UniqueCFRef<CFNumberRef> fontFormatRef(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001787 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1788 if (!fontFormatRef) {
1789 return 0;
1790 }
1791
1792 SInt32 fontFormatValue;
bungemanc292b5f2016-12-20 12:04:29 -05001793 if (!CFNumberGetValue(fontFormatRef.get(), kCFNumberSInt32Type, &fontFormatValue)) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001794 return 0;
1795 }
1796
1797 switch (fontFormatValue) {
1798 case kCTFontFormatOpenTypePostScript:
1799 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1800 case kCTFontFormatOpenTypeTrueType:
1801 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1802 case kCTFontFormatTrueType:
1803 return SkSFNTHeader::fontType_MacTrueType::TAG;
1804 case kCTFontFormatPostScript:
1805 return SkSFNTHeader::fontType_PostScript::TAG;
1806 case kCTFontFormatBitmap:
1807 return SkSFNTHeader::fontType_MacTrueType::TAG;
1808 case kCTFontFormatUnrecognized:
1809 default:
Ben Wagner93e4ea52018-04-05 11:57:17 -04001810 return 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001811 }
1812}
1813
bungeman5f213d92015-01-27 05:39:10 -08001814SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
Ben Wagner93e4ea52018-04-05 11:57:17 -04001815 SK_SFNT_ULONG fontType = get_font_type_tag(fFontRef.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001816
1817 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001818 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001819 SkTDArray<SkFontTableTag> tableTags;
1820 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001821 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001822
Ben Wagner93e4ea52018-04-05 11:57:17 -04001823 // CT seems to be unreliable in being able to obtain the type,
1824 // even if all we want is the first four bytes of the font resource.
1825 // Just the presence of the FontForge 'FFTM' table seems to throw it off.
1826 if (fontType == 0) {
1827 fontType = SkSFNTHeader::fontType_WindowsTrueType::TAG;
1828
1829 // see https://skbug.com/7630#c7
1830 bool couldBeCFF = false;
1831 constexpr SkFontTableTag CFFTag = SkSetFourByteTag('C', 'F', 'F', ' ');
1832 constexpr SkFontTableTag CFF2Tag = SkSetFourByteTag('C', 'F', 'F', '2');
1833 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1834 if (CFFTag == tableTags[tableIndex] || CFF2Tag == tableTags[tableIndex]) {
1835 couldBeCFF = true;
1836 }
1837 }
1838 if (couldBeCFF) {
1839 fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1840 }
1841 }
1842
1843 // Sometimes CoreGraphics incorrectly thinks a font is kCTFontFormatPostScript.
1844 // It is exceedingly unlikely that this is the case, so double check
1845 // (see https://crbug.com/809763 ).
1846 if (fontType == SkSFNTHeader::fontType_PostScript::TAG) {
1847 // see if there are any required 'typ1' tables (see Adobe Technical Note #5180)
1848 bool couldBeTyp1 = false;
1849 constexpr SkFontTableTag TYPE1Tag = SkSetFourByteTag('T', 'Y', 'P', '1');
1850 constexpr SkFontTableTag CIDTag = SkSetFourByteTag('C', 'I', 'D', ' ');
1851 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1852 if (TYPE1Tag == tableTags[tableIndex] || CIDTag == tableTags[tableIndex]) {
1853 couldBeTyp1 = true;
1854 }
1855 }
1856 if (!couldBeTyp1) {
1857 fontType = SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1858 }
1859 }
1860
Ben Wagner03cd6e62018-03-08 16:02:55 -05001861 // get the table sizes and accumulate the total size of the font
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001862 SkTDArray<size_t> tableSizes;
1863 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1864 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001865 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001866 totalSize += (tableSize + 3) & ~3;
1867 *tableSizes.append() = tableSize;
1868 }
1869
1870 // reserve memory for stream, and zero it (tables must be zero padded)
1871 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1872 char* dataStart = (char*)stream->getMemoryBase();
1873 sk_bzero(dataStart, totalSize);
1874 char* dataPtr = dataStart;
1875
1876 // compute font header entries
1877 uint16_t entrySelector = 0;
1878 uint16_t searchRange = 1;
1879 while (searchRange < numTables >> 1) {
1880 entrySelector++;
1881 searchRange <<= 1;
1882 }
1883 searchRange <<= 4;
1884 uint16_t rangeShift = (numTables << 4) - searchRange;
1885
1886 // write font header
1887 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1888 header->fontType = fontType;
1889 header->numTables = SkEndian_SwapBE16(numTables);
1890 header->searchRange = SkEndian_SwapBE16(searchRange);
1891 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1892 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1893 dataPtr += sizeof(SkSFNTHeader);
1894
1895 // write tables
1896 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1897 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1898 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1899 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001900 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001901 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1902 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1903 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001904 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1905 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001906
1907 dataPtr += (tableSize + 3) & ~3;
1908 ++entry;
1909 }
1910
bungemanb3310c22015-03-02 09:05:36 -08001911 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001912 return stream;
1913}
1914
bungeman41868fe2015-05-20 09:21:04 -07001915struct NonDefaultAxesContext {
1916 SkFixed* axisValue;
1917 CFArrayRef cgAxes;
1918};
1919static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1920 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1921
1922 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1923 return;
1924 }
1925
1926 // The key is a CFString which is a string from the 'name' table.
1927 // Search the cgAxes for an axis with this name, and use its index to store the value.
1928 CFIndex keyIndex = -1;
1929 CFStringRef keyString = static_cast<CFStringRef>(key);
1930 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1931 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1932 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1933 continue;
1934 }
1935
1936 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1937 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1938 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1939 continue;
1940 }
1941 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1942 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1943 keyIndex = i;
1944 break;
1945 }
1946 }
1947 if (keyIndex == -1) {
1948 return;
1949 }
1950
1951 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1952 double valueDouble;
1953 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1954 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1955 {
1956 return;
1957 }
1958 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1959}
Ben Wagnerfc497342017-02-24 11:15:26 -05001960static bool get_variations(CTFontRef ctFont, CFIndex* cgAxisCount,
bungeman41868fe2015-05-20 09:21:04 -07001961 SkAutoSTMalloc<4, SkFixed>* axisValues)
1962{
Ben Wagnerfc497342017-02-24 11:15:26 -05001963 // In 10.10 and earlier, CTFontCopyVariationAxes and CTFontCopyVariation do not work when
1964 // applied to fonts which started life with CGFontCreateWithDataProvider (they simply always
1965 // return nullptr). As a result, we are limited to CGFontCopyVariationAxes and
1966 // CGFontCopyVariations here until support for 10.10 and earlier is removed.
1967 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
bungemanc292b5f2016-12-20 12:04:29 -05001968 if (!cgFont) {
bungeman41868fe2015-05-20 09:21:04 -07001969 return false;
1970 }
1971
bungemanc292b5f2016-12-20 12:04:29 -05001972 UniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
1973 // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
1974 if (!cgVariations) {
1975 return false;
1976 }
1977
1978 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont.get()));
1979 if (!cgAxes) {
1980 return false;
1981 }
1982 *cgAxisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07001983 axisValues->reset(*cgAxisCount);
1984
1985 // Set all of the axes to their default values.
1986 // Fail if any default value cannot be determined.
1987 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05001988 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07001989 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1990 return false;
1991 }
1992
1993 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1994 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1995 kCGFontVariationAxisDefaultValue);
1996 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1997 return false;
1998 }
1999 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
2000 double axisDefaultValueDouble;
2001 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
2002 {
2003 return false;
2004 }
2005 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
2006 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
2007 {
2008 return false;
2009 }
2010 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
2011 }
2012
2013 // Override the default values with the given font's stated axis values.
2014 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
bungemanc292b5f2016-12-20 12:04:29 -05002015 CFDictionaryApplyFunction(cgVariations.get(), set_non_default_axes, &c);
bungeman41868fe2015-05-20 09:21:04 -07002016
2017 return true;
2018}
bungemanf93d7112016-09-16 06:24:20 -07002019std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
bungeman41868fe2015-05-20 09:21:04 -07002020 int index;
bungemanf93d7112016-09-16 06:24:20 -07002021 std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
bungeman41868fe2015-05-20 09:21:04 -07002022
2023 CFIndex cgAxisCount;
2024 SkAutoSTMalloc<4, SkFixed> axisValues;
bungemanc292b5f2016-12-20 12:04:29 -05002025 if (get_variations(fFontRef.get(), &cgAxisCount, &axisValues)) {
bungemanf93d7112016-09-16 06:24:20 -07002026 return skstd::make_unique<SkFontData>(std::move(stream), index,
2027 axisValues.get(), cgAxisCount);
bungeman41868fe2015-05-20 09:21:04 -07002028 }
bungemanf93d7112016-09-16 06:24:20 -07002029 return skstd::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
bungeman41868fe2015-05-20 09:21:04 -07002030}
2031
Ben Wagnerfc497342017-02-24 11:15:26 -05002032/** Creates a CT variation dictionary {tag, value} from a CG variation dictionary {name, value}. */
2033static UniqueCFRef<CFDictionaryRef> ct_variation_from_cg_variation(CFDictionaryRef cgVariations,
2034 CFArrayRef ctAxes) {
2035
2036 UniqueCFRef<CFMutableDictionaryRef> ctVariations(
2037 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2038 &kCFTypeDictionaryKeyCallBacks,
2039 &kCFTypeDictionaryValueCallBacks));
2040
2041 CFIndex axisCount = CFArrayGetCount(ctAxes);
2042 for (CFIndex i = 0; i < axisCount; ++i) {
2043 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes, i);
2044 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2045 return nullptr;
2046 }
2047 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2048
2049 // The assumption is that values produced by kCTFontVariationAxisNameKey and
2050 // kCGFontVariationAxisName will always be equal.
2051 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
2052 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2053 return nullptr;
2054 }
2055
2056 CFTypeRef axisValue = CFDictionaryGetValue(cgVariations, axisName);
2057 if (!axisValue || CFGetTypeID(axisValue) != CFNumberGetTypeID()) {
2058 return nullptr;
2059 }
2060
2061 CFTypeRef axisTag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
2062 if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
2063 return nullptr;
2064 }
2065
2066 CFDictionaryAddValue(ctVariations.get(), axisTag, axisValue);
2067 }
2068 return std::move(ctVariations);
2069}
2070
2071int SkTypeface_Mac::onGetVariationDesignPosition(
2072 SkFontArguments::VariationPosition::Coordinate coordinates[], int coordinateCount) const
2073{
2074 // The CGFont variation data does not contain the tag.
2075
bungemanbc096bf2017-04-25 13:32:50 -04002076 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
2077 // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
Ben Wagnerfc497342017-02-24 11:15:26 -05002078 UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(fFontRef.get()));
2079 if (!ctAxes) {
2080 return -1;
2081 }
2082 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
2083 if (!coordinates || coordinateCount < axisCount) {
2084 return axisCount;
2085 }
2086
2087 // This call always returns nullptr on 10.11 and under for CGFontCreateWithDataProvider fonts.
2088 // When this happens, try converting the CG variation to a CT variation.
2089 // On 10.12 and later, this only returns non-default variations.
2090 UniqueCFRef<CFDictionaryRef> ctVariations(CTFontCopyVariation(fFontRef.get()));
2091 if (!ctVariations) {
2092 // When 10.11 and earlier are no longer supported, the following code can be replaced with
2093 // return -1 and ct_variation_from_cg_variation can be removed.
2094 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
2095 if (!cgFont) {
2096 return -1;
2097 }
2098 UniqueCFRef<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont.get()));
2099 if (!cgVariations) {
2100 return -1;
2101 }
2102 ctVariations = ct_variation_from_cg_variation(cgVariations.get(), ctAxes.get());
2103 if (!ctVariations) {
2104 return -1;
2105 }
2106 }
2107
2108 for (int i = 0; i < axisCount; ++i) {
2109 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
2110 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2111 return -1;
2112 }
2113 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2114
2115 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
2116 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2117 return -1;
2118 }
2119 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
2120 int64_t tagLong;
2121 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2122 return -1;
2123 }
2124 coordinates[i].axis = tagLong;
2125
2126 CGFloat variationCGFloat;
2127 CFTypeRef variationValue = CFDictionaryGetValue(ctVariations.get(), tagNumber);
2128 if (variationValue) {
2129 if (CFGetTypeID(variationValue) != CFNumberGetTypeID()) {
2130 return -1;
2131 }
2132 CFNumberRef variationNumber = static_cast<CFNumberRef>(variationValue);
2133 if (!CFNumberGetValue(variationNumber, kCFNumberCGFloatType, &variationCGFloat)) {
2134 return -1;
2135 }
2136 } else {
2137 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
2138 if (!def || CFGetTypeID(def) != CFNumberGetTypeID()) {
2139 return -1;
2140 }
2141 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2142 if (!CFNumberGetValue(defNumber, kCFNumberCGFloatType, &variationCGFloat)) {
2143 return -1;
2144 }
2145 }
2146 coordinates[i].value = CGToScalar(variationCGFloat);
2147
2148 }
2149 return axisCount;
2150}
2151
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002152///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002153///////////////////////////////////////////////////////////////////////////////
2154
2155int SkTypeface_Mac::onGetUPEM() const {
bungemanc292b5f2016-12-20 12:04:29 -05002156 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef.get(), nullptr));
2157 return CGFontGetUnitsPerEm(cgFont.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002158}
2159
bungeman@google.com839702b2013-08-07 17:09:22 +00002160SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00002161 SkTypeface::LocalizedStrings* nameIter =
bungemanc292b5f2016-12-20 12:04:29 -05002162 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07002163 if (nullptr == nameIter) {
bungemanc292b5f2016-12-20 12:04:29 -05002164 CFStringRef cfLanguageRaw;
2165 UniqueCFRef<CFStringRef> cfFamilyName(
2166 CTFontCopyLocalizedName(fFontRef.get(), kCTFontFamilyNameKey, &cfLanguageRaw));
2167 UniqueCFRef<CFStringRef> cfLanguage(cfLanguageRaw);
bungeman@google.coma9802692013-08-07 02:45:25 +00002168
2169 SkString skLanguage;
2170 SkString skFamilyName;
bungemanc292b5f2016-12-20 12:04:29 -05002171 if (cfLanguage) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002172 CFStringToSkString(cfLanguage.get(), &skLanguage);
2173 } else {
2174 skLanguage = "und"; //undetermined
2175 }
bungemanc292b5f2016-12-20 12:04:29 -05002176 if (cfFamilyName) {
bungeman@google.coma9802692013-08-07 02:45:25 +00002177 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
2178 }
2179
2180 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
2181 }
2182 return nameIter;
2183}
2184
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002185int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
bungemanc292b5f2016-12-20 12:04:29 -05002186 UniqueCFRef<CFArrayRef> cfArray(
2187 CTFontCopyAvailableTables(fFontRef.get(), kCTFontTableOptionNoOptions));
2188 if (!cfArray) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002189 return 0;
2190 }
bungemanc292b5f2016-12-20 12:04:29 -05002191 int count = SkToInt(CFArrayGetCount(cfArray.get()));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002192 if (tags) {
2193 for (int i = 0; i < count; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002194 uintptr_t fontTag = reinterpret_cast<uintptr_t>(
2195 CFArrayGetValueAtIndex(cfArray.get(), i));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002196 tags[i] = static_cast<SkFontTableTag>(fontTag);
2197 }
2198 }
2199 return count;
2200}
2201
bungemanc292b5f2016-12-20 12:04:29 -05002202// If, as is the case with web fonts, the CTFont data isn't available,
2203// the CGFont data may work. While the CGFont may always provide the
2204// right result, leave the CTFont code path to minimize disruption.
2205static UniqueCFRef<CFDataRef> copy_table_from_font(CTFontRef ctFont, SkFontTableTag tag) {
2206 UniqueCFRef<CFDataRef> data(CTFontCopyTable(ctFont, (CTFontTableTag) tag,
2207 kCTFontTableOptionNoOptions));
2208 if (!data) {
2209 UniqueCFRef<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
2210 data.reset(CGFontCopyTableForTag(cgFont.get(), tag));
2211 }
2212 return data;
2213}
2214
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002215size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
2216 size_t length, void* dstData) const {
bungemanc292b5f2016-12-20 12:04:29 -05002217 UniqueCFRef<CFDataRef> srcData = copy_table_from_font(fFontRef.get(), tag);
2218 if (!srcData) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002219 return 0;
2220 }
2221
bungemanc292b5f2016-12-20 12:04:29 -05002222 size_t srcSize = CFDataGetLength(srcData.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002223 if (offset >= srcSize) {
2224 return 0;
2225 }
2226 if (length > srcSize - offset) {
2227 length = srcSize - offset;
2228 }
2229 if (dstData) {
bungemanc292b5f2016-12-20 12:04:29 -05002230 memcpy(dstData, CFDataGetBytePtr(srcData.get()) + offset, length);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002231 }
2232 return length;
2233}
2234
reeda9322c22016-04-12 06:47:05 -07002235SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
2236 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04002237 return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002238}
2239
2240void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002241 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
2242 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
2243 {
2244 rec->fMaskFormat = SkMask::kA8_Format;
2245 // Render the glyphs as close as possible to what was requested.
2246 // The above turns off subpixel rendering, but the user requested it.
2247 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
2248 // See comments below for more details.
2249 rec->setHinting(SkPaint::kNormal_Hinting);
2250 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00002251
Mike Kleinc76e26a2018-05-02 13:53:53 +00002252 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
2253 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00002254 SkScalerContext::kLCD_BGROrder_Flag |
2255 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002256
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002257 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002258
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002259 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002260
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002261 // Only two levels of hinting are supported.
2262 // kNo_Hinting means avoid CoreGraphics outline dilation.
2263 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
2264 // If there is no lcd support, hinting (dilation) cannot be supported.
2265 SkPaint::Hinting hinting = rec->getHinting();
2266 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
2267 hinting = SkPaint::kNo_Hinting;
2268 } else if (SkPaint::kFull_Hinting == hinting) {
2269 hinting = SkPaint::kNormal_Hinting;
2270 }
2271 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002272
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002273 // FIXME: lcd smoothed un-hinted rasterization unsupported.
2274 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
2275 // There is no current means to honor a request for unhinted lcd,
2276 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002277
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002278 // Hinting and smoothing should be orthogonal, but currently they are not.
2279 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
2280 // output is drawn from auto-dilated outlines (the amount of which is
2281 // determined by AppleFontSmoothing). Its regular anti-aliased output is
2282 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002283
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002284 // The behavior of Skia is as follows:
2285 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
2286 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
2287 // channel. This matches [LCD][yes-hint] in weight.
2288 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
2289 // Currenly side with LCD, effectively ignoring the hinting setting.
2290 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002291
bungemanc292b5f2016-12-20 12:04:29 -05002292 if (rec->fMaskFormat == SkMask::kLCD16_Format) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002293 if (lcdSupport) {
2294 //CoreGraphics creates 555 masks for smoothed text anyway.
2295 rec->fMaskFormat = SkMask::kLCD16_Format;
2296 rec->setHinting(SkPaint::kNormal_Hinting);
2297 } else {
2298 rec->fMaskFormat = SkMask::kA8_Format;
2299 }
2300 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002301
bungeman34902632014-12-10 21:43:27 -08002302 // CoreText provides no information as to whether a glyph will be color or not.
2303 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
2304 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08002305 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08002306 rec->fMaskFormat = SkMask::kARGB32_Format;
2307 }
2308
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002309 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2310 // All other masks can use regular gamma.
2311 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
2312#ifndef SK_GAMMA_APPLY_TO_A8
brianosmana1e8f8d2016-04-08 06:47:54 -07002313 // SRGBTODO: Is this correct? Do we want contrast boost?
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002314 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002315#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002316 } else {
2317 //CoreGraphics dialates smoothed text as needed.
2318 rec->setContrast(0);
2319 }
2320}
2321
bungemanc292b5f2016-12-20 12:04:29 -05002322/** Takes ownership of the CFStringRef. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002323static const char* get_str(CFStringRef ref, SkString* str) {
halcanary96fcdcc2015-08-27 07:41:13 -07002324 if (nullptr == ref) {
2325 return nullptr;
bungeman256b3512014-07-02 07:57:59 -07002326 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002327 CFStringToSkString(ref, str);
bungemanc292b5f2016-12-20 12:04:29 -05002328 CFRelease(ref);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002329 return str->c_str();
2330}
2331
bungemanb374d6a2014-09-17 07:48:59 -07002332void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
bungemanc292b5f2016-12-20 12:04:29 -05002333 get_str(CTFontCopyFamilyName(fFontRef.get()), familyName);
bungemanb374d6a2014-09-17 07:48:59 -07002334}
2335
reed@google.com5526ede2013-03-25 13:03:37 +00002336void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2337 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002338 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002339
bungemanc292b5f2016-12-20 12:04:29 -05002340 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef.get()), &tmpStr));
2341 desc->setFullName(get_str(CTFontCopyFullName(fFontRef.get()), &tmpStr));
2342 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef.get()), &tmpStr));
bungemanb8113782016-07-25 16:54:59 -07002343 desc->setStyle(this->fontStyle());
caseq26337e92014-06-30 12:14:52 -07002344 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002345}
reed@google.com5526ede2013-03-25 13:03:37 +00002346
reed@google.combcb42ae2013-07-02 13:56:39 +00002347int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002348 uint16_t glyphs[], int glyphCount) const
2349{
2350 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
2351 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
2352 // 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 +00002353
reed@google.combcb42ae2013-07-02 13:56:39 +00002354 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002355 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
2356 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00002357 switch (encoding) {
2358 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002359 const char* utf8 = reinterpret_cast<const char*>(chars);
2360 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2361 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00002362 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002363 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
2364 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00002365 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002366 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002367 break;
2368 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002369 case kUTF16_Encoding: {
2370 src = reinterpret_cast<const UniChar*>(chars);
2371 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00002372 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002373 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2374 ++extra;
2375 }
reed@google.combcb42ae2013-07-02 13:56:39 +00002376 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002377 srcCount = glyphCount + extra;
2378 break;
2379 }
2380 case kUTF32_Encoding: {
2381 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
2382 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2383 src = utf16;
2384 for (int i = 0; i < glyphCount; ++i) {
2385 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
2386 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002387 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002388 break;
2389 }
2390 }
2391
halcanary96fcdcc2015-08-27 07:41:13 -07002392 // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002393 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002394 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2395 uint16_t* macGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002396 if (nullptr == macGlyphs || srcCount > glyphCount) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002397 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002398 }
2399
bungemanc292b5f2016-12-20 12:04:29 -05002400 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef.get(), src, macGlyphs, srcCount);
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002401
2402 // If there were any non-bmp, then copy and compact.
halcanary96fcdcc2015-08-27 07:41:13 -07002403 // If 'glyphs' is nullptr, then compact glyphStorage in-place.
2404 // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
2405 // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002406 uint16_t* compactedGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002407 if (nullptr == compactedGlyphs) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002408 compactedGlyphs = macGlyphs;
2409 }
2410 if (srcCount > glyphCount) {
2411 int extra = 0;
2412 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002413 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002414 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2415 ++extra;
2416 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002417 }
2418 }
2419
2420 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002421 return glyphCount;
2422 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002423
2424 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002425 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002426 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002427 return i;
2428 }
2429 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002430 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002431 return glyphCount;
2432}
2433
2434int SkTypeface_Mac::onCountGlyphs() const {
bungemanc292b5f2016-12-20 12:04:29 -05002435 return SkToInt(CTFontGetGlyphCount(fFontRef.get()));
reed@google.combcb42ae2013-07-02 13:56:39 +00002436}
2437
reed@google.com95625db2013-03-25 20:44:02 +00002438///////////////////////////////////////////////////////////////////////////////
2439///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002440
2441static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
bungemanc292b5f2016-12-20 12:04:29 -05002442 UniqueCFRef<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
2443 if (!ref) {
reed@google.com83787c52013-03-26 17:19:15 +00002444 return false;
2445 }
bungemanc292b5f2016-12-20 12:04:29 -05002446 CFStringToSkString(ref.get(), value);
reed@google.com83787c52013-03-26 17:19:15 +00002447 return true;
2448}
2449
reed@google.com95625db2013-03-25 20:44:02 +00002450#include "SkFontMgr.h"
2451
reed@google.com964988f2013-03-29 14:57:22 +00002452static inline int sqr(int value) {
2453 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2454 return value * value;
2455}
2456
2457// We normalize each axis (weight, width, italic) to be base-900
2458static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2459 return sqr(a.weight() - b.weight()) +
2460 sqr((a.width() - b.width()) * 100) +
bungemanb4bb7d82016-04-27 10:21:04 -07002461 sqr((a.slant() != b.slant()) * 900);
reed@google.com964988f2013-03-29 14:57:22 +00002462}
2463
reed@google.com83787c52013-03-26 17:19:15 +00002464class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002465public:
bungeman53d5c6e2016-04-08 07:22:29 -07002466 SkFontStyleSet_Mac(CTFontDescriptorRef desc)
halcanary96fcdcc2015-08-27 07:41:13 -07002467 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
bungemanc292b5f2016-12-20 12:04:29 -05002468 , fCount(0)
2469 {
2470 if (!fArray) {
2471 fArray.reset(CFArrayCreate(nullptr, nullptr, 0, nullptr));
reed@google.comdea7ee02013-03-28 14:12:10 +00002472 }
bungemanc292b5f2016-12-20 12:04:29 -05002473 fCount = SkToInt(CFArrayGetCount(fArray.get()));
reed@google.com83787c52013-03-26 17:19:15 +00002474 }
2475
mtklein36352bf2015-03-25 18:17:31 -07002476 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002477 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002478 }
2479
mtklein36352bf2015-03-25 18:17:31 -07002480 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002481 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002482 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002483 if (style) {
bungeman2873c762017-01-13 11:40:21 -05002484 *style = fontstyle_from_descriptor(desc, false);
reed@google.com83787c52013-03-26 17:19:15 +00002485 }
2486 if (name) {
2487 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2488 name->reset();
2489 }
2490 }
2491 }
2492
mtklein36352bf2015-03-25 18:17:31 -07002493 SkTypeface* createTypeface(int index) override {
bungemanc292b5f2016-12-20 12:04:29 -05002494 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray.get()));
2495 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002496
Mike Reed59227392017-09-26 09:46:08 -04002497 return create_from_desc(desc).release();
reed@google.com83787c52013-03-26 17:19:15 +00002498 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002499
mtklein36352bf2015-03-25 18:17:31 -07002500 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002501 if (0 == fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002502 return nullptr;
reed@google.com964988f2013-03-29 14:57:22 +00002503 }
Mike Reed59227392017-09-26 09:46:08 -04002504 return create_from_desc(findMatchingDesc(pattern)).release();
reed@google.com964988f2013-03-29 14:57:22 +00002505 }
2506
reed@google.com83787c52013-03-26 17:19:15 +00002507private:
bungemanc292b5f2016-12-20 12:04:29 -05002508 UniqueCFRef<CFArrayRef> fArray;
2509 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002510
2511 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2512 int bestMetric = SK_MaxS32;
halcanary96fcdcc2015-08-27 07:41:13 -07002513 CTFontDescriptorRef bestDesc = nullptr;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002514
reed@google.com964988f2013-03-29 14:57:22 +00002515 for (int i = 0; i < fCount; ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002516 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray.get(), i);
bungeman2873c762017-01-13 11:40:21 -05002517 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc, false));
reed@google.com964988f2013-03-29 14:57:22 +00002518 if (0 == metric) {
2519 return desc;
2520 }
2521 if (metric < bestMetric) {
2522 bestMetric = metric;
2523 bestDesc = desc;
2524 }
2525 }
2526 SkASSERT(bestDesc);
2527 return bestDesc;
2528 }
reed@google.com83787c52013-03-26 17:19:15 +00002529};
2530
2531class SkFontMgr_Mac : public SkFontMgr {
bungemanc292b5f2016-12-20 12:04:29 -05002532 UniqueCFRef<CFArrayRef> fNames;
2533 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002534
bungemanc292b5f2016-12-20 12:04:29 -05002535 CFStringRef getFamilyNameAt(int index) const {
reed@google.com83787c52013-03-26 17:19:15 +00002536 SkASSERT((unsigned)index < (unsigned)fCount);
bungemanc292b5f2016-12-20 12:04:29 -05002537 return (CFStringRef)CFArrayGetValueAtIndex(fNames.get(), index);
reed@google.com83787c52013-03-26 17:19:15 +00002538 }
2539
reed@google.com964988f2013-03-29 14:57:22 +00002540 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
bungemanc292b5f2016-12-20 12:04:29 -05002541 UniqueCFRef<CFMutableDictionaryRef> cfAttr(
reed@google.com964988f2013-03-29 14:57:22 +00002542 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2543 &kCFTypeDictionaryKeyCallBacks,
2544 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002545
bungemanc292b5f2016-12-20 12:04:29 -05002546 CFDictionaryAddValue(cfAttr.get(), kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002547
bungemanc292b5f2016-12-20 12:04:29 -05002548 UniqueCFRef<CTFontDescriptorRef> desc(
2549 CTFontDescriptorCreateWithAttributes(cfAttr.get()));
2550 return new SkFontStyleSet_Mac(desc.get());
2551 }
2552
2553 /** CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
2554 * provide a wrapper here that will return an empty array if need be.
2555 */
2556 static UniqueCFRef<CFArrayRef> CopyAvailableFontFamilyNames() {
2557#ifdef SK_BUILD_FOR_IOS
2558 return UniqueCFRef<CFArrayRef>(CFArrayCreate(nullptr, nullptr, 0, nullptr));
2559#else
2560 return UniqueCFRef<CFArrayRef>(CTFontManagerCopyAvailableFontFamilyNames());
2561#endif
reed@google.com964988f2013-03-29 14:57:22 +00002562 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002563
reed@google.com83787c52013-03-26 17:19:15 +00002564public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002565 SkFontMgr_Mac()
bungemanc292b5f2016-12-20 12:04:29 -05002566 : fNames(CopyAvailableFontFamilyNames())
2567 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames.get())) : 0) {}
reed@google.com95625db2013-03-25 20:44:02 +00002568
2569protected:
mtklein36352bf2015-03-25 18:17:31 -07002570 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002571 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002572 }
2573
mtklein36352bf2015-03-25 18:17:31 -07002574 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002575 if ((unsigned)index < (unsigned)fCount) {
bungemanc292b5f2016-12-20 12:04:29 -05002576 CFStringToSkString(this->getFamilyNameAt(index), familyName);
reed@google.com83787c52013-03-26 17:19:15 +00002577 } else {
2578 familyName->reset();
2579 }
reed@google.com95625db2013-03-25 20:44:02 +00002580 }
2581
mtklein36352bf2015-03-25 18:17:31 -07002582 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002583 if ((unsigned)index >= (unsigned)fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002584 return nullptr;
reed@google.com83787c52013-03-26 17:19:15 +00002585 }
bungemanc292b5f2016-12-20 12:04:29 -05002586 return CreateSet(this->getFamilyNameAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002587 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002588
mtklein36352bf2015-03-25 18:17:31 -07002589 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
bungeman7575bb12017-05-01 13:02:42 -04002590 if (!familyName) {
2591 return nullptr;
2592 }
bungemanc292b5f2016-12-20 12:04:29 -05002593 UniqueCFRef<CFStringRef> cfName = make_CFString(familyName);
2594 return CreateSet(cfName.get());
reed@google.com964988f2013-03-29 14:57:22 +00002595 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002596
bungemanc292b5f2016-12-20 12:04:29 -05002597 SkTypeface* onMatchFamilyStyle(const char familyName[],
bungeman7575bb12017-05-01 13:02:42 -04002598 const SkFontStyle& style) const override {
2599 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
Mike Reed59227392017-09-26 09:46:08 -04002600 return create_from_desc(desc.get()).release();
reed@google.com95625db2013-03-25 20:44:02 +00002601 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002602
bungemanc292b5f2016-12-20 12:04:29 -05002603 SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
2604 const SkFontStyle& style,
2605 const char* bcp47[], int bcp47Count,
2606 SkUnichar character) const override {
2607 UniqueCFRef<CTFontDescriptorRef> desc = create_descriptor(familyName, style);
2608 UniqueCFRef<CTFontRef> currentFont(CTFontCreateWithFontDescriptor(desc.get(), 0, nullptr));
bungemanbea97482016-08-24 08:29:50 -07002609
2610 // kCFStringEncodingUTF32 is BE unless there is a BOM.
2611 // Since there is no machine endian option, explicitly state machine endian.
2612#ifdef SK_CPU_LENDIAN
2613 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
2614#else
2615 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
2616#endif
bungemanc292b5f2016-12-20 12:04:29 -05002617 UniqueCFRef<CFStringRef> string(CFStringCreateWithBytes(
bungemanbea97482016-08-24 08:29:50 -07002618 kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
2619 encoding, false));
bungemanc292b5f2016-12-20 12:04:29 -05002620 CFRange range = CFRangeMake(0, CFStringGetLength(string.get())); // in UniChar units.
2621 UniqueCFRef<CTFontRef> fallbackFont(
2622 CTFontCreateForString(currentFont.get(), string.get(), range));
Mike Reed59227392017-09-26 09:46:08 -04002623 return create_from_CTFontRef(std::move(fallbackFont), nullptr, false).release();
djsollen33068c12014-11-14 10:52:53 -08002624 }
2625
bungemanc292b5f2016-12-20 12:04:29 -05002626 SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
2627 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002628 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002629 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002630
Mike Reed59227392017-09-26 09:46:08 -04002631 sk_sp<SkTypeface> onMakeFromData(sk_sp<SkData> data, int ttcIndex) const override {
2632 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromData(std::move(data)));
bungemanc292b5f2016-12-20 12:04:29 -05002633 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002634 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002635 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002636 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002637 }
2638
Mike Reed59227392017-09-26 09:46:08 -04002639 sk_sp<SkTypeface> onMakeFromStreamIndex(std::unique_ptr<SkStreamAsset> stream,
2640 int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002641 UniqueCFRef<CGDataProviderRef> pr(SkCreateDataProviderFromStream(std::move(stream)));
2642 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002643 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002644 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002645 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002646 }
2647
Ben Wagnerfc497342017-02-24 11:15:26 -05002648 /** Creates a dictionary suitable for setting the axes on a CGFont. */
2649 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, const SkFontArguments& args) {
2650 // The CGFont variation data is keyed by name, but lacks the tag.
bungemanf6c71072016-01-21 14:17:47 -08002651 // The CTFont variation data is keyed by tag, and also has the name.
Ben Wagnerfc497342017-02-24 11:15:26 -05002652 // We would like to work with CTFont variations, but creating a CTFont font with
bungemanf6c71072016-01-21 14:17:47 -08002653 // CTFont variation dictionary runs into bugs. So use the CTFont variation data
2654 // to match names to tags to create the appropriate CGFont.
bungemanc292b5f2016-12-20 12:04:29 -05002655 UniqueCFRef<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
bungemanbc096bf2017-04-25 13:32:50 -04002656 // CTFontCopyVariationAxes returns nullptr for CGFontCreateWithDataProvider fonts with
2657 // macOS 10.10 and iOS 9 or earlier. When this happens, there is no API to provide the tag.
bungemanc292b5f2016-12-20 12:04:29 -05002658 UniqueCFRef<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct.get()));
Ben Wagnerfc497342017-02-24 11:15:26 -05002659 if (!ctAxes) {
bungemanf6c71072016-01-21 14:17:47 -08002660 return nullptr;
2661 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002662 CFIndex axisCount = CFArrayGetCount(ctAxes.get());
bungemanf6c71072016-01-21 14:17:47 -08002663
Ben Wagnerfc497342017-02-24 11:15:26 -05002664 const SkFontArguments::VariationPosition position = args.getVariationDesignPosition();
bungemanf6c71072016-01-21 14:17:47 -08002665
bungemanc292b5f2016-12-20 12:04:29 -05002666 UniqueCFRef<CFMutableDictionaryRef> dict(
2667 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2668 &kCFTypeDictionaryKeyCallBacks,
2669 &kCFTypeDictionaryValueCallBacks));
2670
bungemanf6c71072016-01-21 14:17:47 -08002671 for (int i = 0; i < axisCount; ++i) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002672 CFTypeRef axisInfo = CFArrayGetValueAtIndex(ctAxes.get(), i);
bungemanf6c71072016-01-21 14:17:47 -08002673 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2674 return nullptr;
2675 }
2676 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2677
Ben Wagnerfc497342017-02-24 11:15:26 -05002678 // The assumption is that values produced by kCTFontVariationAxisNameKey and
2679 // kCGFontVariationAxisName will always be equal.
2680 // If they are ever not, seach the project history for "get_tag_for_name".
2681 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisNameKey);
bungemanf6c71072016-01-21 14:17:47 -08002682 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2683 return nullptr;
2684 }
2685
Ben Wagnerfc497342017-02-24 11:15:26 -05002686 CFTypeRef tag = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisIdentifierKey);
2687 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2688 return nullptr;
bungemanf6c71072016-01-21 14:17:47 -08002689 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002690 CFNumberRef tagNumber = static_cast<CFNumberRef>(tag);
bungemanf6c71072016-01-21 14:17:47 -08002691 int64_t tagLong;
2692 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2693 return nullptr;
2694 }
2695
2696 // The variation axes can be set to any value, but cg will effectively pin them.
2697 // Pin them here to normalize.
Ben Wagnerfc497342017-02-24 11:15:26 -05002698 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMinimumValueKey);
2699 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisMaximumValueKey);
2700 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCTFontVariationAxisDefaultValueKey);
bungemanf6c71072016-01-21 14:17:47 -08002701 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2702 !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
2703 !def || CFGetTypeID(def) != CFNumberGetTypeID())
2704 {
2705 return nullptr;
2706 }
2707 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2708 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2709 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2710 double minDouble;
2711 double maxDouble;
2712 double defDouble;
2713 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2714 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
2715 !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
2716 {
2717 return nullptr;
2718 }
2719
2720 double value = defDouble;
bungeman9aec8942017-03-29 13:38:53 -04002721 // The position may be over specified. If there are multiple values for a given axis,
2722 // use the last one since that's what css-fonts-4 requires.
2723 for (int j = position.coordinateCount; j --> 0;) {
Ben Wagnerfc497342017-02-24 11:15:26 -05002724 if (position.coordinates[j].axis == tagLong) {
2725 value = SkTPin(SkScalarToDouble(position.coordinates[j].value),
2726 minDouble, maxDouble);
bungemanf6c71072016-01-21 14:17:47 -08002727 break;
2728 }
2729 }
bungemanc292b5f2016-12-20 12:04:29 -05002730 UniqueCFRef<CFNumberRef> valueNumber(
2731 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2732 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungemanf6c71072016-01-21 14:17:47 -08002733 }
bungemanc292b5f2016-12-20 12:04:29 -05002734 return std::move(dict);
bungemanf6c71072016-01-21 14:17:47 -08002735 }
Mike Reed59227392017-09-26 09:46:08 -04002736 sk_sp<SkTypeface> onMakeFromStreamArgs(std::unique_ptr<SkStreamAsset> s,
2737 const SkFontArguments& args) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002738 if (args.getCollectionIndex() != 0) {
2739 return nullptr;
2740 }
bungemanc292b5f2016-12-20 12:04:29 -05002741 UniqueCFRef<CGDataProviderRef> provider(SkCreateDataProviderFromStream(std::move(s)));
2742 if (!provider) {
bungemanf6c71072016-01-21 14:17:47 -08002743 return nullptr;
2744 }
bungemanc292b5f2016-12-20 12:04:29 -05002745 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2746 if (!cg) {
bungemanf6c71072016-01-21 14:17:47 -08002747 return nullptr;
2748 }
2749
Ben Wagnerfc497342017-02-24 11:15:26 -05002750 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), args);
bungemanf6c71072016-01-21 14:17:47 -08002751 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2752 // created from a data provider does not appear to have any ownership of the underlying
2753 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002754 UniqueCFRef<CGFontRef> cgVariant;
bungemanf6c71072016-01-21 14:17:47 -08002755 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002756 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungemanf6c71072016-01-21 14:17:47 -08002757 } else {
mtklein18300a32016-03-16 13:53:35 -07002758 cgVariant.reset(cg.release());
bungemanf6c71072016-01-21 14:17:47 -08002759 }
2760
bungemanc292b5f2016-12-20 12:04:29 -05002761 UniqueCFRef<CTFontRef> ct(
2762 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungemanf6c71072016-01-21 14:17:47 -08002763 if (!ct) {
2764 return nullptr;
2765 }
bungemanc292b5f2016-12-20 12:04:29 -05002766 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungemanf6c71072016-01-21 14:17:47 -08002767 }
2768
Ben Wagnerfc497342017-02-24 11:15:26 -05002769 /** Creates a dictionary suitable for setting the axes on a CGFont. */
bungemanc292b5f2016-12-20 12:04:29 -05002770 static UniqueCFRef<CFDictionaryRef> copy_axes(CGFontRef cg, SkFontData* fontData) {
2771 UniqueCFRef<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
bungeman41868fe2015-05-20 09:21:04 -07002772 if (!cgAxes) {
halcanary96fcdcc2015-08-27 07:41:13 -07002773 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002774 }
2775
bungemanc292b5f2016-12-20 12:04:29 -05002776 CFIndex axisCount = CFArrayGetCount(cgAxes.get());
bungeman41868fe2015-05-20 09:21:04 -07002777 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002778 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002779 }
2780
bungemanc292b5f2016-12-20 12:04:29 -05002781 UniqueCFRef<CFMutableDictionaryRef> dict(
2782 CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2783 &kCFTypeDictionaryKeyCallBacks,
2784 &kCFTypeDictionaryValueCallBacks));
2785
bungeman41868fe2015-05-20 09:21:04 -07002786 for (int i = 0; i < fontData->getAxisCount(); ++i) {
bungemanc292b5f2016-12-20 12:04:29 -05002787 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes.get(), i);
bungeman41868fe2015-05-20 09:21:04 -07002788 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002789 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002790 }
2791 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2792
2793 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2794 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002795 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002796 }
2797
2798 // The variation axes can be set to any value, but cg will effectively pin them.
2799 // Pin them here to normalize.
2800 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2801 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2802 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2803 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2804 {
halcanary96fcdcc2015-08-27 07:41:13 -07002805 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002806 }
2807 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2808 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2809 double minDouble;
2810 double maxDouble;
2811 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2812 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2813 {
halcanary96fcdcc2015-08-27 07:41:13 -07002814 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002815 }
2816 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
bungemanc292b5f2016-12-20 12:04:29 -05002817 UniqueCFRef<CFNumberRef> valueNumber(
2818 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
2819 CFDictionaryAddValue(dict.get(), axisName, valueNumber.get());
bungeman41868fe2015-05-20 09:21:04 -07002820 }
bungemanc292b5f2016-12-20 12:04:29 -05002821 return std::move(dict);
bungeman41868fe2015-05-20 09:21:04 -07002822 }
Mike Reed59227392017-09-26 09:46:08 -04002823 sk_sp<SkTypeface> onMakeFromFontData(std::unique_ptr<SkFontData> fontData) const override {
Ben Wagnerfc497342017-02-24 11:15:26 -05002824 if (fontData->getIndex() != 0) {
2825 return nullptr;
2826 }
bungemanc292b5f2016-12-20 12:04:29 -05002827 UniqueCFRef<CGDataProviderRef> provider(
bungemanf93d7112016-09-16 06:24:20 -07002828 SkCreateDataProviderFromStream(fontData->detachStream()));
bungemanc292b5f2016-12-20 12:04:29 -05002829 if (!provider) {
halcanary96fcdcc2015-08-27 07:41:13 -07002830 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002831 }
bungemanc292b5f2016-12-20 12:04:29 -05002832 UniqueCFRef<CGFontRef> cg(CGFontCreateWithDataProvider(provider.get()));
2833 if (!cg) {
halcanary96fcdcc2015-08-27 07:41:13 -07002834 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002835 }
2836
bungemanc292b5f2016-12-20 12:04:29 -05002837 UniqueCFRef<CFDictionaryRef> cgVariations = copy_axes(cg.get(), fontData.get());
bungeman41868fe2015-05-20 09:21:04 -07002838 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2839 // created from a data provider does not appear to have any ownership of the underlying
2840 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
bungemanc292b5f2016-12-20 12:04:29 -05002841 UniqueCFRef<CGFontRef> cgVariant;
bungeman41868fe2015-05-20 09:21:04 -07002842 if (cgVariations) {
bungemanc292b5f2016-12-20 12:04:29 -05002843 cgVariant.reset(CGFontCreateCopyWithVariations(cg.get(), cgVariations.get()));
bungeman41868fe2015-05-20 09:21:04 -07002844 } else {
mtklein18300a32016-03-16 13:53:35 -07002845 cgVariant.reset(cg.release());
bungeman41868fe2015-05-20 09:21:04 -07002846 }
2847
bungemanc292b5f2016-12-20 12:04:29 -05002848 UniqueCFRef<CTFontRef> ct(
2849 CTFontCreateWithGraphicsFont(cgVariant.get(), 0, nullptr, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07002850 if (!ct) {
halcanary96fcdcc2015-08-27 07:41:13 -07002851 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002852 }
bungemanc292b5f2016-12-20 12:04:29 -05002853 return create_from_CTFontRef(std::move(ct), std::move(cg), true);
bungeman41868fe2015-05-20 09:21:04 -07002854 }
2855
Mike Reed59227392017-09-26 09:46:08 -04002856 sk_sp<SkTypeface> onMakeFromFile(const char path[], int ttcIndex) const override {
bungemanc292b5f2016-12-20 12:04:29 -05002857 UniqueCFRef<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2858 if (!pr) {
halcanary96fcdcc2015-08-27 07:41:13 -07002859 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002860 }
Ben Wagnerfc497342017-02-24 11:15:26 -05002861 return create_from_dataProvider(std::move(pr), ttcIndex);
reed@google.com95625db2013-03-25 20:44:02 +00002862 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002863
Mike Reed59227392017-09-26 09:46:08 -04002864 sk_sp<SkTypeface> onLegacyMakeTypeface(const char familyName[], SkFontStyle style) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002865 if (familyName) {
2866 familyName = map_css_names(familyName);
2867 }
2868
Mike Reed59227392017-09-26 09:46:08 -04002869 sk_sp<SkTypeface> face = create_from_name(familyName, style);
bungeman53d5c6e2016-04-08 07:22:29 -07002870 if (face) {
2871 return face;
bungemana4c4a2d2014-10-20 13:33:19 -07002872 }
bungeman53d5c6e2016-04-08 07:22:29 -07002873
bungemanc292b5f2016-12-20 12:04:29 -05002874 static SkTypeface* gDefaultFace;
2875 static SkOnce lookupDefault;
bungeman83f1f442017-02-06 13:21:33 -05002876 static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
bungemanc292b5f2016-12-20 12:04:29 -05002877 lookupDefault([]{
Mike Reed59227392017-09-26 09:46:08 -04002878 gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle()).release();
bungemanc292b5f2016-12-20 12:04:29 -05002879 });
Mike Reed59227392017-09-26 09:46:08 -04002880 return sk_ref_sp(gDefaultFace);
reed@google.com7fdcd442013-07-30 21:25:49 +00002881 }
reed@google.com95625db2013-03-25 20:44:02 +00002882};
2883
reed@google.com7fdcd442013-07-30 21:25:49 +00002884///////////////////////////////////////////////////////////////////////////////
2885
Ben Wagner3546ff12017-01-03 13:32:36 -05002886sk_sp<SkFontMgr> SkFontMgr::Factory() { return sk_make_sp<SkFontMgr_Mac>(); }
mtklein1ee76512015-11-02 10:20:27 -08002887
2888#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)