blob: a9f6bb0a6eb79c7907b2f7e92aea2b367755d0ee [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"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000023#include "SkCGUtils.h"
24#include "SkColorPriv.h"
25#include "SkDescriptor.h"
26#include "SkEndian.h"
jvanverth02802f62015-07-02 06:42:49 -070027#include "SkFloatingPoint.h"
mtklein1b249332015-07-07 12:21:21 -070028#include "SkFontDescriptor.h"
29#include "SkFontMgr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000030#include "SkGlyph.h"
bungemanf93d7112016-09-16 06:24:20 -070031#include "SkMakeUnique.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000032#include "SkMaskGamma.h"
halcanary4dbbd042016-06-07 17:21:10 -070033#include "SkMathPriv.h"
mtklein1b249332015-07-07 12:21:21 -070034#include "SkMutex.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000035#include "SkOTTable_glyf.h"
36#include "SkOTTable_head.h"
37#include "SkOTTable_hhea.h"
38#include "SkOTTable_loca.h"
39#include "SkOTUtils.h"
mtkleinffa4a922016-05-05 16:05:56 -070040#include "SkOnce.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000041#include "SkPaint.h"
42#include "SkPath.h"
mtklein1b249332015-07-07 12:21:21 -070043#include "SkSFNTHeader.h"
jvanverth02802f62015-07-02 06:42:49 -070044#include "SkStream.h"
mtklein1b249332015-07-07 12:21:21 -070045#include "SkString.h"
bungemane280d062016-03-24 11:27:05 -070046#include "SkTemplates.h"
mtklein1b249332015-07-07 12:21:21 -070047#include "SkTypefaceCache.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000048#include "SkTypeface_mac.h"
49#include "SkUtils.h"
reed@google.combcb42ae2013-07-02 13:56:39 +000050#include "SkUtils.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000051
bungeman34902632014-12-10 21:43:27 -080052#include <dlfcn.h>
reed@google.comf77b35d2013-05-02 20:39:44 +000053
reedd0f41732015-07-10 12:08:38 -070054// Experimental code to use a global lock whenever we access CG, to see if this reduces
55// crashes in Chrome
56#define USE_GLOBAL_MUTEX_FOR_CG_ACCESS
57
58#ifdef USE_GLOBAL_MUTEX_FOR_CG_ACCESS
reed086eea92016-05-04 17:12:46 -070059 SK_DECLARE_STATIC_MUTEX(gCGMutex);
reedd0f41732015-07-10 12:08:38 -070060 #define AUTO_CG_LOCK() SkAutoMutexAcquire amc(gCGMutex)
61#else
62 #define AUTO_CG_LOCK()
63#endif
64
bungeman3b4b66c2015-01-08 08:33:44 -080065// Set to make glyph bounding boxes visible.
66#define SK_SHOW_TEXT_BLIT_COVERAGE 0
67
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000068class SkScalerContext_Mac;
69
reed@google.com3dcbd462013-03-27 13:56:34 +000070// CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
71// provide a wrapper here that will return an empty array if need be.
72static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() {
73#ifdef SK_BUILD_FOR_IOS
halcanary96fcdcc2015-08-27 07:41:13 -070074 return CFArrayCreate(nullptr, nullptr, 0, nullptr);
reed@google.com3dcbd462013-03-27 13:56:34 +000075#else
76 return CTFontManagerCopyAvailableFontFamilyNames();
77#endif
78}
79
80
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000081// Being templated and taking const T* prevents calling
82// CFSafeRelease(autoCFRelease) through implicit conversion.
83template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
84 if (cfTypeRef) {
85 CFRelease(cfTypeRef);
86 }
87}
88
89// Being templated and taking const T* prevents calling
90// CFSafeRetain(autoCFRelease) through implicit conversion.
91template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
92 if (cfTypeRef) {
93 CFRetain(cfTypeRef);
94 }
95}
96
97/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
98template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
99public:
halcanary96fcdcc2015-08-27 07:41:13 -0700100 explicit AutoCFRelease(CFRef cfRef = nullptr) : fCFRef(cfRef) { }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000101 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
102
halcanary96fcdcc2015-08-27 07:41:13 -0700103 void reset(CFRef that = nullptr) {
ljagielskie9d2d092014-07-15 20:02:04 -0700104 if (that != fCFRef) {
105 CFSafeRelease(fCFRef);
106 fCFRef = that;
107 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000108 }
109
mtklein18300a32016-03-16 13:53:35 -0700110 CFRef release() {
bungeman64fb51d2015-05-04 12:03:50 -0700111 CFRef self = fCFRef;
halcanary96fcdcc2015-08-27 07:41:13 -0700112 fCFRef = nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700113 return self;
114 }
115
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000116 operator CFRef() const { return fCFRef; }
117 CFRef get() const { return fCFRef; }
118
halcanary96fcdcc2015-08-27 07:41:13 -0700119 CFRef* operator&() { SkASSERT(fCFRef == nullptr); return &fCFRef; }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000120private:
121 CFRef fCFRef;
122};
123
reed@google.com964988f2013-03-29 14:57:22 +0000124static CFStringRef make_CFString(const char str[]) {
halcanary96fcdcc2015-08-27 07:41:13 -0700125 return CFStringCreateWithCString(nullptr, str, kCFStringEncodingUTF8);
reed@google.com964988f2013-03-29 14:57:22 +0000126}
127
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000128template<typename T> class AutoCGTable : SkNoncopyable {
129public:
130 AutoCGTable(CGFontRef font)
131 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
132 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
halcanary96fcdcc2015-08-27 07:41:13 -0700133 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : nullptr)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000134 { }
135
136 const T* operator->() const { return fData; }
137
138private:
139 AutoCFRelease<CFDataRef> fCFData;
140public:
141 const T* fData;
142};
143
144// inline versions of these rect helpers
145
146static bool CGRectIsEmpty_inline(const CGRect& rect) {
147 return rect.size.width <= 0 || rect.size.height <= 0;
148}
149
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000150static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
151 return rect.origin.x;
152}
153
154static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
155 return rect.origin.x + rect.size.width;
156}
157
158static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
159 return rect.origin.y;
160}
161
162static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
163 return rect.origin.y + rect.size.height;
164}
165
166static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
167 return rect.size.width;
168}
169
170///////////////////////////////////////////////////////////////////////////////
171
172static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
reed@google.com7fa2a652014-01-27 13:42:58 +0000173 int width, int height, size_t rowBytes) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000174 SkASSERT(width);
175 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
176
177 if (width >= 32) {
178 while (height) {
179 sk_memset32(ptr, value, width);
180 ptr = (uint32_t*)((char*)ptr + rowBytes);
181 height -= 1;
182 }
183 return;
184 }
185
186 rowBytes -= width * sizeof(uint32_t);
187
188 if (width >= 8) {
189 while (height) {
190 int w = width;
191 do {
192 *ptr++ = value; *ptr++ = value;
193 *ptr++ = value; *ptr++ = value;
194 *ptr++ = value; *ptr++ = value;
195 *ptr++ = value; *ptr++ = value;
196 w -= 8;
197 } while (w >= 8);
198 while (--w >= 0) {
199 *ptr++ = value;
200 }
201 ptr = (uint32_t*)((char*)ptr + rowBytes);
202 height -= 1;
203 }
204 } else {
205 while (height) {
206 int w = width;
207 do {
208 *ptr++ = value;
209 } while (--w > 0);
210 ptr = (uint32_t*)((char*)ptr + rowBytes);
211 height -= 1;
212 }
213 }
214}
215
216#include <sys/utsname.h>
217
218typedef uint32_t CGRGBPixel;
219
220static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
221 return pixel & 0xFF;
222}
223
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000224static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
225
226// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
227static int readVersion() {
228 struct utsname info;
229 if (uname(&info) != 0) {
230 SkDebugf("uname failed\n");
231 return 0;
232 }
233 if (strcmp(info.sysname, "Darwin") != 0) {
234 SkDebugf("unexpected uname sysname %s\n", info.sysname);
235 return 0;
236 }
237 char* dot = strchr(info.release, '.');
238 if (!dot) {
239 SkDebugf("expected dot in uname release %s\n", info.release);
240 return 0;
241 }
242 int version = atoi(info.release);
243 if (version == 0) {
244 SkDebugf("could not parse uname release %s\n", info.release);
245 }
246 return version;
247}
248
249static int darwinVersion() {
250 static int darwin_version = readVersion();
251 return darwin_version;
252}
253
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000254static bool isSnowLeopard() {
255 return darwinVersion() == 10;
256}
257
258static bool isLion() {
259 return darwinVersion() == 11;
260}
261
262static bool isMountainLion() {
263 return darwinVersion() == 12;
264}
265
266static bool isLCDFormat(unsigned format) {
reedd54d3fc2014-11-13 14:39:58 -0800267 return SkMask::kLCD16_Format == format;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000268}
269
270static CGFloat ScalarToCG(SkScalar scalar) {
271 if (sizeof(CGFloat) == sizeof(float)) {
272 return SkScalarToFloat(scalar);
273 } else {
274 SkASSERT(sizeof(CGFloat) == sizeof(double));
275 return (CGFloat) SkScalarToDouble(scalar);
276 }
277}
278
279static SkScalar CGToScalar(CGFloat cgFloat) {
280 if (sizeof(CGFloat) == sizeof(float)) {
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700281 return SkFloatToScalar(cgFloat);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000282 } else {
283 SkASSERT(sizeof(CGFloat) == sizeof(double));
284 return SkDoubleToScalar(cgFloat);
285 }
286}
287
benjaminwagner6b3eacb2016-03-24 19:07:58 -0700288static float CGToFloat(CGFloat cgFloat) {
289 if (sizeof(CGFloat) == sizeof(float)) {
290 return cgFloat;
291 } else {
292 SkASSERT(sizeof(CGFloat) == sizeof(double));
293 return static_cast<float>(cgFloat);
294 }
295}
296
bungeman4de8c3a2015-09-18 23:03:24 -0700297static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
298 SkScalar sx = SK_Scalar1,
299 SkScalar sy = SK_Scalar1) {
300 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
301 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
302 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
303 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
304 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
305 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000306}
307
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000308///////////////////////////////////////////////////////////////////////////////
309
310#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000311
312/**
313 * There does not appear to be a publicly accessable API for determining if lcd
314 * font smoothing will be applied if we request it. The main issue is that if
315 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
316 */
317static bool supports_LCD() {
318 static int gSupportsLCD = -1;
319 if (gSupportsLCD >= 0) {
320 return (bool) gSupportsLCD;
321 }
322 uint32_t rgb = 0;
323 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
324 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
325 colorspace, BITMAP_INFO_RGB));
ccameronf8ee5b42016-10-04 15:02:02 -0700326 AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithName(CFSTR("Helvetica"), 16, nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000327 CGContextSetShouldSmoothFonts(cgContext, true);
328 CGContextSetShouldAntialias(cgContext, true);
329 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
330 CGContextSetGrayFillColor(cgContext, 1, 1);
ccameronf8ee5b42016-10-04 15:02:02 -0700331 CGPoint point = CGPointMake(-1, 0);
332 static const UniChar pipeChar = '|';
333 CGGlyph pipeGlyph;
334 CTFontGetGlyphsForCharacters(ctFont, &pipeChar, &pipeGlyph, 1);
335 CTFontDrawGlyphs(ctFont, &pipeGlyph, &point, 1, cgContext);
336
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000337 uint32_t r = (rgb >> 16) & 0xFF;
338 uint32_t g = (rgb >> 8) & 0xFF;
339 uint32_t b = (rgb >> 0) & 0xFF;
340 gSupportsLCD = (r != g || r != b);
341 return (bool) gSupportsLCD;
342}
343
344class Offscreen {
345public:
bungeman34902632014-12-10 21:43:27 -0800346 Offscreen()
halcanary96fcdcc2015-08-27 07:41:13 -0700347 : fRGBSpace(nullptr)
348 , fCG(nullptr)
bungeman34902632014-12-10 21:43:27 -0800349 , fDoAA(false)
350 , fDoLCD(false)
351 {
352 fSize.set(0, 0);
353 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000354
355 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
356 CGGlyph glyphID, size_t* rowBytesPtr,
357 bool generateA8FromLCD);
358
359private:
360 enum {
361 kSize = 32 * 32 * sizeof(CGRGBPixel)
362 };
363 SkAutoSMalloc<kSize> fImageStorage;
364 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
365
366 // cached state
367 AutoCFRelease<CGContextRef> fCG;
368 SkISize fSize;
369 bool fDoAA;
370 bool fDoLCD;
371
372 static int RoundSize(int dimension) {
373 return SkNextPow2(dimension);
374 }
375};
376
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000377///////////////////////////////////////////////////////////////////////////////
378
bungeman6e45bda2016-07-25 15:11:49 -0700379static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
bungemana4c4a2d2014-10-20 13:33:19 -0700380 CFNumberRef num;
381 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
382 && CFNumberIsFloatType(num)
bungeman6e45bda2016-07-25 15:11:49 -0700383 && CFNumberGetValue(num, kCFNumberCGFloatType, value);
bungemana4c4a2d2014-10-20 13:33:19 -0700384}
385
bungeman6e45bda2016-07-25 15:11:49 -0700386template <typename S, typename D, typename C> struct LinearInterpolater {
387 struct Mapping {
388 S src_val;
389 D dst_val;
390 };
391 constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
392 : fMapping(mapping), fMappingCount(mappingCount) {}
393
394 static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
395 SkASSERT(src_min < src_max);
396 SkASSERT(dst_min <= dst_max);
397 return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000398 }
bungeman6e45bda2016-07-25 15:11:49 -0700399
400 int map(S val) const {
401 // -Inf to [0]
402 if (val < fMapping[0].src_val) {
403 return fMapping[0].dst_val;
404 }
405
406 // Linear from [i] to [i+1]
407 for (int i = 0; i < fMappingCount - 1; ++i) {
408 if (val < fMapping[i+1].src_val) {
409 return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
410 fMapping[i].dst_val, fMapping[i+1].dst_val);
411 }
412 }
413
414 // From [n] to +Inf
415 // if (fcweight < Inf)
416 return fMapping[fMappingCount - 1].dst_val;
417 }
418
419 Mapping const * fMapping;
420 int fMappingCount;
421};
422
423struct RoundCGFloatToInt {
424 int operator()(CGFloat s) { return s + 0.5; }
425};
426
427static int ct_weight_to_fontstyle(CGFloat cgWeight) {
428 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
429
430 // Values determined by creating font data with every weight, creating a CTFont,
431 // and asking the CTFont for its weight. See TypefaceStyle test for basics.
432
433 // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
434 // However, on this end we can't tell.
435 static constexpr Interpolator::Mapping weightMappings[] = {
436 { -1.00, 0 },
437 { -0.70, 100 },
438 { -0.50, 200 },
439 { -0.23, 300 },
440 { 0.00, 400 },
441 { 0.20, 500 },
442 { 0.30, 600 },
443 { 0.40, 700 },
444 { 0.60, 800 },
445 { 0.80, 900 },
446 { 1.00, 1000 },
447 };
448 static constexpr Interpolator interpolater(weightMappings, SK_ARRAY_COUNT(weightMappings));
449 return interpolater.map(cgWeight);
bungemana4c4a2d2014-10-20 13:33:19 -0700450}
451
bungeman6e45bda2016-07-25 15:11:49 -0700452static int ct_width_to_fontstyle(CGFloat cgWidth) {
453 using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
454
455 // Values determined by creating font data with every width, creating a CTFont,
456 // and asking the CTFont for its width. See TypefaceStyle test for basics.
457 static constexpr Interpolator::Mapping widthMappings[] = {
458 { -0.5, 0 },
459 { 0.5, 10 },
460 };
461 static constexpr Interpolator interpolater(widthMappings, SK_ARRAY_COUNT(widthMappings));
462 return interpolater.map(cgWidth);
bungemana4c4a2d2014-10-20 13:33:19 -0700463}
464
bungeman336fdf22014-11-10 07:48:55 -0800465static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc) {
bungemana4c4a2d2014-10-20 13:33:19 -0700466 AutoCFRelease<CFDictionaryRef> dict(
467 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
halcanary96fcdcc2015-08-27 07:41:13 -0700468 if (nullptr == dict.get()) {
bungemana4c4a2d2014-10-20 13:33:19 -0700469 return SkFontStyle();
470 }
471
bungeman6e45bda2016-07-25 15:11:49 -0700472 CGFloat weight, width, slant;
473 if (!find_dict_CGFloat(dict, kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800474 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700475 }
bungeman6e45bda2016-07-25 15:11:49 -0700476 if (!find_dict_CGFloat(dict, kCTFontWidthTrait, &width)) {
bungemana4c4a2d2014-10-20 13:33:19 -0700477 width = 0;
478 }
bungeman6e45bda2016-07-25 15:11:49 -0700479 if (!find_dict_CGFloat(dict, kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800480 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700481 }
482
bungeman6e45bda2016-07-25 15:11:49 -0700483 return SkFontStyle(ct_weight_to_fontstyle(weight),
484 ct_width_to_fontstyle(width),
bungemana4c4a2d2014-10-20 13:33:19 -0700485 slant ? SkFontStyle::kItalic_Slant
bungemanb4bb7d82016-04-27 10:21:04 -0700486 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000487}
488
reed@google.comce8b3de2013-03-26 19:30:16 +0000489#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
490
bungeman994e8182015-02-23 16:17:43 -0800491// kCTFontColorGlyphsTrait was added in the Mac 10.7 and iPhone 4.3 SDKs.
492// Being an enum value it is not guarded by version macros, but old SDKs must still be supported.
493#if defined(__MAC_10_7) || defined(__IPHONE_4_3)
494static const uint32_t SkCTFontColorGlyphsTrait = kCTFontColorGlyphsTrait;
495#else
496static const uint32_t SkCTFontColorGlyphsTrait = (1 << 13);
497#endif
498
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000499class SkTypeface_Mac : public SkTypeface {
500public:
bungeman78884012015-06-08 13:39:12 -0700501 SkTypeface_Mac(CTFontRef fontRef, CFTypeRef resourceRef,
502 const SkFontStyle& fs, bool isFixedPitch,
bungeman53d5c6e2016-04-08 07:22:29 -0700503 bool isLocalStream)
bungemane3aea102016-07-13 05:16:58 -0700504 : SkTypeface(fs, isFixedPitch)
mtklein802ad832014-10-20 12:54:31 -0700505 , fFontRef(fontRef) // caller has already called CFRetain for us
bungeman78884012015-06-08 13:39:12 -0700506 , fOriginatingCFTypeRef(resourceRef) // caller has already called CFRetain for us
bungemane3bea5c2015-04-07 07:34:36 -0700507 , fHasColorGlyphs(SkToBool(CTFontGetSymbolicTraits(fFontRef) & SkCTFontColorGlyphsTrait))
caseq26337e92014-06-30 12:14:52 -0700508 , fIsLocalStream(isLocalStream)
reed@google.comce8b3de2013-03-26 19:30:16 +0000509 {
510 SkASSERT(fontRef);
511 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000512
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000513 AutoCFRelease<CTFontRef> fFontRef;
bungeman78884012015-06-08 13:39:12 -0700514 AutoCFRelease<CFTypeRef> fOriginatingCFTypeRef;
bungemane3bea5c2015-04-07 07:34:36 -0700515 const bool fHasColorGlyphs;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000516
517protected:
mtklein36352bf2015-03-25 18:17:31 -0700518 int onGetUPEM() const override;
519 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
bungemanf93d7112016-09-16 06:24:20 -0700520 std::unique_ptr<SkFontData> onMakeFontData() const override;
mtklein36352bf2015-03-25 18:17:31 -0700521 void onGetFamilyName(SkString* familyName) const override;
522 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
523 int onGetTableTags(SkFontTableTag tags[]) const override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000524 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
mtklein36352bf2015-03-25 18:17:31 -0700525 size_t length, void* data) const override;
reeda9322c22016-04-12 06:47:05 -0700526 SkScalerContext* onCreateScalerContext(const SkScalerContextEffects&,
527 const SkDescriptor*) const override;
mtklein36352bf2015-03-25 18:17:31 -0700528 void onFilterRec(SkScalerContextRec*) const override;
529 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
reed@google.com2689f612013-03-20 20:01:47 +0000530 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -0700531 PerGlyphInfo,
mtklein36352bf2015-03-25 18:17:31 -0700532 const uint32_t*, uint32_t) const override;
reed@google.combcb42ae2013-07-02 13:56:39 +0000533 virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
mtklein36352bf2015-03-25 18:17:31 -0700534 int glyphCount) const override;
535 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000536
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000537private:
caseq26337e92014-06-30 12:14:52 -0700538 bool fIsLocalStream;
reed@google.comce8b3de2013-03-26 19:30:16 +0000539
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000540 typedef SkTypeface INHERITED;
541};
542
bungeman82a455f2016-04-14 08:04:45 -0700543static bool find_by_CTFontRef(SkTypeface* cached, void* context) {
bungeman64fb51d2015-05-04 12:03:50 -0700544 CTFontRef self = (CTFontRef)context;
545 CTFontRef other = ((SkTypeface_Mac*)cached)->fFontRef;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000546
bungeman64fb51d2015-05-04 12:03:50 -0700547 return CFEqual(self, other);
548}
549
bungemanbea97482016-08-24 08:29:50 -0700550/** Creates a typeface, searching the cache if isLocalStream is false.
551 * Takes ownership of the CTFontRef and CFTypeRef.
552 */
553static SkTypeface* create_from_CTFontRef(CTFontRef f, CFTypeRef r, bool isLocalStream) {
554 SkASSERT(f);
555 AutoCFRelease<CTFontRef> font(f);
556 AutoCFRelease<CFTypeRef> resource(r);
557
558 if (!isLocalStream) {
559 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)font.get());
560 if (face) {
561 return face;
562 }
563 }
564
565 AutoCFRelease<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(font));
566 SkFontStyle style = fontstyle_from_descriptor(desc);
567 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
568 bool isFixedPitch = SkToBool(traits & kCTFontMonoSpaceTrait);
569
570 SkTypeface* face = new SkTypeface_Mac(font.release(), resource.release(),
571 style, isFixedPitch, isLocalStream);
572 if (!isLocalStream) {
573 SkTypefaceCache::Add(face);
574 }
575 return face;
576}
577
578/** Creates a typeface from a descriptor, searching the cache. */
579static SkTypeface* create_from_desc(CTFontDescriptorRef desc) {
580 AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
581 if (!ctFont) {
582 return nullptr;
583 }
584
585 return create_from_CTFontRef(ctFont.release(), nullptr, false);
586}
587
588static CTFontDescriptorRef create_descriptor(const char familyName[], const SkFontStyle& style) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000589 CTFontSymbolicTraits ctFontTraits = 0;
bungemanbea97482016-08-24 08:29:50 -0700590 if (style.weight() >= SkFontStyle::kBold_Weight) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000591 ctFontTraits |= kCTFontBoldTrait;
592 }
bungemanbea97482016-08-24 08:29:50 -0700593 if (style.slant() != SkFontStyle::kUpright_Slant) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000594 ctFontTraits |= kCTFontItalicTrait;
595 }
596
bungemana4c4a2d2014-10-20 13:33:19 -0700597 //TODO: add weight width slant
598
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000599 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000600 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000601
602 AutoCFRelease<CFNumberRef> cfFontTraits(
603 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
604
605 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
606 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
607 &kCFTypeDictionaryKeyCallBacks,
608 &kCFTypeDictionaryValueCallBacks));
609
610 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
611 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
612 &kCFTypeDictionaryKeyCallBacks,
613 &kCFTypeDictionaryValueCallBacks));
614
bungeman64fb51d2015-05-04 12:03:50 -0700615 if (!cfFontName || !cfFontTraits || !cfAttributes || !cfTraits) {
halcanary96fcdcc2015-08-27 07:41:13 -0700616 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000617 }
618
bungeman64fb51d2015-05-04 12:03:50 -0700619 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
620
621 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
622 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
623
bungemanbea97482016-08-24 08:29:50 -0700624 return CTFontDescriptorCreateWithAttributes(cfAttributes);
625}
626
627/** Creates a typeface from a name, searching the cache. */
628static SkTypeface* create_from_name(const char familyName[], const SkFontStyle& style) {
629 AutoCFRelease<CTFontDescriptorRef> desc(create_descriptor(familyName, style));
630 if (!desc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700631 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700632 }
bungemanbea97482016-08-24 08:29:50 -0700633 return create_from_desc(desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000634}
635
reed086eea92016-05-04 17:12:46 -0700636SK_DECLARE_STATIC_MUTEX(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000637static SkTypeface* GetDefaultFace() {
bungemand6aeb6d2014-07-25 11:52:47 -0700638 SkAutoMutexAcquire ma(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000639
640 static SkTypeface* gDefaultFace;
641
halcanary96fcdcc2015-08-27 07:41:13 -0700642 if (nullptr == gDefaultFace) {
bungemanbea97482016-08-24 08:29:50 -0700643 gDefaultFace = create_from_name(FONT_DEFAULT_NAME, SkFontStyle());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000644 }
645 return gDefaultFace;
646}
647
648///////////////////////////////////////////////////////////////////////////////
649
650extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
651CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
652 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
halcanary96fcdcc2015-08-27 07:41:13 -0700653 return macface ? macface->fFontRef.get() : nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000654}
655
656/* This function is visible on the outside. It first searches the cache, and if
657 * not found, returns a new entry (after adding it to the cache).
658 */
bungeman78884012015-06-08 13:39:12 -0700659SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef, CFTypeRef resourceRef) {
bungeman53d5c6e2016-04-08 07:22:29 -0700660 CFRetain(fontRef);
661 if (resourceRef) {
662 CFRetain(resourceRef);
663 }
bungemanbea97482016-08-24 08:29:50 -0700664 return create_from_CTFontRef(fontRef, resourceRef, false);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000665}
666
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000667static const char* map_css_names(const char* name) {
668 static const struct {
669 const char* fFrom; // name the caller specified
670 const char* fTo; // "canonical" name we map to
671 } gPairs[] = {
672 { "sans-serif", "Helvetica" },
673 { "serif", "Times" },
674 { "monospace", "Courier" }
675 };
676
677 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
678 if (strcmp(name, gPairs[i].fFrom) == 0) {
679 return gPairs[i].fTo;
680 }
681 }
682 return name; // no change
683}
684
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000685///////////////////////////////////////////////////////////////////////////////
686
bungeman@google.comcefd9812013-05-15 15:07:32 +0000687/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000688struct GlyphRect {
689 int16_t fMinX;
690 int16_t fMinY;
691 int16_t fMaxX;
692 int16_t fMaxY;
693};
694
695class SkScalerContext_Mac : public SkScalerContext {
696public:
bungeman7cfd46a2016-10-20 16:06:52 -0400697 SkScalerContext_Mac(sk_sp<SkTypeface_Mac>, const SkScalerContextEffects&, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000698
699protected:
mtklein36352bf2015-03-25 18:17:31 -0700700 unsigned generateGlyphCount(void) override;
701 uint16_t generateCharToGlyph(SkUnichar uni) override;
702 void generateAdvance(SkGlyph* glyph) override;
703 void generateMetrics(SkGlyph* glyph) override;
704 void generateImage(const SkGlyph& glyph) override;
Ben Wagner6e9ac122016-11-11 14:31:06 -0500705 void generatePath(SkGlyphID glyph, SkPath* path) override;
mtklein36352bf2015-03-25 18:17:31 -0700706 void generateFontMetrics(SkPaint::FontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000707
708private:
709 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000710
bungeman@google.comcefd9812013-05-15 15:07:32 +0000711 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
712 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000713
714 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
715 *
716 * For use with (and must be called before) generateBBoxes.
717 */
718 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000719
bungeman@google.comcefd9812013-05-15 15:07:32 +0000720 /** Initializes fFBoundingBoxes and returns true on success.
721 *
722 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
723 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
724 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
725 * font directly.
726 *
727 * This routine initializes fFBoundingBoxes to an array of
728 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
729 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
730 *
731 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
732 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
733 *
734 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
735 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000736 bool generateBBoxes();
737
bungeman@google.comcefd9812013-05-15 15:07:32 +0000738 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
739 *
740 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
741 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
742 */
743 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000744
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000745 Offscreen fOffscreen;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000746
bungeman3b4b66c2015-01-08 08:33:44 -0800747 /** Unrotated variant of fCTFont.
748 *
749 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
750 * advances, but always sets the height to 0. This font is used to get the advances of the
751 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000752 *
753 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
754 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
755 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
bungeman3b4b66c2015-01-08 08:33:44 -0800756 * With kCTFontVerticalOrientation the advances must be unrotated.
bungemanef27ce32015-10-29 09:30:32 -0700757 *
758 * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
759 * different underlying font data. As a result, avoid ever creating more than one CTFont per
760 * SkScalerContext to ensure that only one CTFont is used.
761 *
762 * As a result of the above (and other constraints) this font contains the size, but not the
763 * transform. The transform must always be applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000764 */
bungemanef27ce32015-10-29 09:30:32 -0700765 AutoCFRelease<CTFontRef> fCTFont;
766
767 /** The transform without the font size. */
768 CGAffineTransform fTransform;
769 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000770
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000771 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000772 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000773 uint16_t fFBoundingBoxesGlyphOffset;
774 uint16_t fGlyphCount;
775 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000776 const bool fDoSubPosition;
777 const bool fVertical;
778
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000779 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000780
781 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000782};
783
bungeman7cbeaae2015-09-22 09:54:56 -0700784// CTFontCreateCopyWithAttributes or CTFontCreateCopyWithSymbolicTraits cannot be used on 10.10
bungeman05846312015-09-23 12:51:28 -0700785// and later, as they will return different underlying fonts depending on the size requested.
786// It is not possible to use descriptors with CTFontCreateWithFontDescriptor, since that does not
787// work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
788// CGFont.
bungeman7cbeaae2015-09-22 09:54:56 -0700789static CTFontRef ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
bungeman05846312015-09-23 12:51:28 -0700790 const CGAffineTransform* transform)
bungeman7cbeaae2015-09-22 09:54:56 -0700791{
bungeman05846312015-09-23 12:51:28 -0700792 AutoCFRelease<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700793
bungeman05846312015-09-23 12:51:28 -0700794 // The last parameter (CTFontDescriptorRef attributes) *must* be nullptr.
795 // If non-nullptr then with fonts with variation axes, the copy will fail in
796 // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
797 // which it quite obviously is not.
bungeman7cbeaae2015-09-22 09:54:56 -0700798
bungeman05846312015-09-23 12:51:28 -0700799 // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
800 // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
801 // the scaler context, since they aren't 'normal'.
802 return CTFontCreateWithGraphicsFont(baseCGFont, textSize, transform, nullptr);
bungeman7cbeaae2015-09-22 09:54:56 -0700803}
804
bungeman7cfd46a2016-10-20 16:06:52 -0400805SkScalerContext_Mac::SkScalerContext_Mac(sk_sp<SkTypeface_Mac> typeface,
reeda9322c22016-04-12 06:47:05 -0700806 const SkScalerContextEffects& effects,
reed@google.com0da48612013-03-19 16:06:52 +0000807 const SkDescriptor* desc)
bungeman7cfd46a2016-10-20 16:06:52 -0400808 : INHERITED(std::move(typeface), effects, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000809 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000810 , fFBoundingBoxesGlyphOffset(0)
811 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000812 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
813 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
814
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000815{
reedd0f41732015-07-10 12:08:38 -0700816 AUTO_CG_LOCK();
817
bungeman7cfd46a2016-10-20 16:06:52 -0400818 CTFontRef ctFont = static_cast<SkTypeface_Mac*>(this->getTypeface())->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000819 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000820 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
821 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000822
bungeman34902632014-12-10 21:43:27 -0800823 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
824 // As a result, it is necessary to know the actual device size and request that.
825 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800826 SkMatrix skTransform;
bungemane55131c2016-08-24 12:01:31 -0700827 bool invertible = fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
bungeman1f0e78d2016-08-23 13:19:01 -0700828 &scale, &skTransform, nullptr, nullptr, &fFUnitMatrix);
bungemanaae30912015-03-02 13:43:26 -0800829 fTransform = MatrixToCGAffineTransform(skTransform);
bungeman1f0e78d2016-08-23 13:19:01 -0700830 // CGAffineTransformInvert documents that if the transform is non-invertible it will return the
831 // passed transform unchanged. It does so, but then also prints a message to stdout. Avoid this.
bungemane55131c2016-08-24 12:01:31 -0700832 if (invertible) {
bungeman1f0e78d2016-08-23 13:19:01 -0700833 fInvTransform = CGAffineTransformInvert(fTransform);
834 } else {
835 fInvTransform = fTransform;
836 }
bungeman@google.comcefd9812013-05-15 15:07:32 +0000837
bungemanbe2284d2014-11-25 08:08:09 -0800838 // The transform contains everything except the requested text size.
839 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -0800840 CGFloat textSize = ScalarToCG(scale.y());
bungemanef27ce32015-10-29 09:30:32 -0700841 fCTFont.reset(ctfont_create_exact_copy(ctFont, textSize, nullptr));
halcanary96fcdcc2015-08-27 07:41:13 -0700842 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont, nullptr));
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000843
bungemanbe2284d2014-11-25 08:08:09 -0800844 // The fUnitMatrix includes the text size (and em) as it is used to scale the raw font data.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000845 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
846 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000847}
848
849CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
850 CGGlyph glyphID, size_t* rowBytesPtr,
mtkleinbbd40182015-09-08 08:19:33 -0700851 bool generateA8FromLCD) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000852 if (!fRGBSpace) {
853 //It doesn't appear to matter what color space is specified.
854 //Regular blends and antialiased text are always (s*a + d*(1-a))
855 //and smoothed text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -0700856 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000857 }
858
859 // default to kBW_Format
860 bool doAA = false;
861 bool doLCD = false;
862
863 if (SkMask::kBW_Format != glyph.fMaskFormat) {
864 doLCD = true;
865 doAA = true;
866 }
867
868 // FIXME: lcd smoothed un-hinted rasterization unsupported.
869 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
870 doLCD = false;
871 doAA = true;
872 }
873
bungeman34902632014-12-10 21:43:27 -0800874 // If this font might have color glyphs, disable LCD as there's no way to support it.
875 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
bungemane280d062016-03-24 11:27:05 -0700876 // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
bungeman34902632014-12-10 21:43:27 -0800877 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
878 doLCD = false;
879 }
880
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000881 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
882 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
883 if (fSize.fWidth < glyph.fWidth) {
884 fSize.fWidth = RoundSize(glyph.fWidth);
885 }
886 if (fSize.fHeight < glyph.fHeight) {
887 fSize.fHeight = RoundSize(glyph.fHeight);
888 }
889
890 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
891 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -0800892 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
893 ? kCGImageAlphaPremultipliedFirst
894 : kCGImageAlphaNoneSkipFirst;
895 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -0700896 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungeman34902632014-12-10 21:43:27 -0800897 rowBytes, fRGBSpace, bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000898
bungeman34902632014-12-10 21:43:27 -0800899 // Skia handles quantization and subpixel positioning,
900 // so disable quantization and enabe subpixel positioning in CG.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000901 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
902 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
903
bungeman@google.comcefd9812013-05-15 15:07:32 +0000904 // Because CG always draws from the horizontal baseline,
905 // if there is a non-integral translation from the horizontal origin to the vertical origin,
906 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungeman34902632014-12-10 21:43:27 -0800907 CGContextSetAllowsFontSubpixelPositioning(fCG, true);
908 CGContextSetShouldSubpixelPositionFonts(fCG, true);
909
910 CGContextSetTextDrawingMode(fCG, kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000911
bungemane280d062016-03-24 11:27:05 -0700912 // Draw black on white to create mask. (Special path exists to speed this up in CG.)
913 CGContextSetGrayFillColor(fCG, 0.0f, 1.0f);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000914
915 // force our checks below to happen
916 fDoAA = !doAA;
917 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -0800918
bungemanaae30912015-03-02 13:43:26 -0800919 CGContextSetTextMatrix(fCG, context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000920 }
921
922 if (fDoAA != doAA) {
923 CGContextSetShouldAntialias(fCG, doAA);
924 fDoAA = doAA;
925 }
926 if (fDoLCD != doLCD) {
927 CGContextSetShouldSmoothFonts(fCG, doLCD);
928 fDoLCD = doLCD;
929 }
930
931 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
932 // skip rows based on the glyph's height
933 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
934
bungemane280d062016-03-24 11:27:05 -0700935 // Erase to white (or transparent black if it's a color glyph, to not composite against white).
936 uint32_t bgColor = (SkMask::kARGB32_Format != glyph.fMaskFormat) ? 0xFFFFFFFF : 0x00000000;
937 sk_memset_rect32(image, bgColor, glyph.fWidth, glyph.fHeight, rowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000938
939 float subX = 0;
940 float subY = 0;
941 if (context.fDoSubPosition) {
942 subX = SkFixedToFloat(glyph.getSubXFixed());
943 subY = SkFixedToFloat(glyph.getSubYFixed());
944 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000945
bungeman34902632014-12-10 21:43:27 -0800946 // CoreText and CoreGraphics always draw using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000947 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000948 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000949 context.getVerticalOffset(glyphID, &offset);
950 subX += offset.fX;
951 subY += offset.fY;
952 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000953
bungeman34902632014-12-10 21:43:27 -0800954 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -0800955 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
956 // 'positions' which are in text space. The glyph location (in device space) must be
957 // mapped into text space, so that CG can convert it back into device space.
958 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
bungeman1b5f25c2015-06-08 14:32:24 -0700959 //
bungemanaae30912015-03-02 13:43:26 -0800960 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
961 // So always make the font transform identity and place the transform on the context.
962 point = CGPointApplyAffineTransform(point, context.fInvTransform);
963
ccameronf8ee5b42016-10-04 15:02:02 -0700964 CTFontDrawGlyphs(context.fCTFont, &glyphID, &point, 1, fCG);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000965
966 SkASSERT(rowBytesPtr);
967 *rowBytesPtr = rowBytes;
968 return image;
969}
970
bungeman@google.comcefd9812013-05-15 15:07:32 +0000971void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
972 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
973 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
974 CGSize cgVertOffset;
975 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000976 if (isSnowLeopard()) {
bungemanef27ce32015-10-29 09:30:32 -0700977 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
bungeman@google.comcefd9812013-05-15 15:07:32 +0000978 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
979 fFUnitMatrix.mapPoints(&skVertOffset, 1);
bungemanef27ce32015-10-29 09:30:32 -0700980 *offset = skVertOffset;
981 return;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000982 }
bungemanef27ce32015-10-29 09:30:32 -0700983 cgVertOffset = CGSizeApplyAffineTransform(cgVertOffset, fTransform);
984 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
985 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
986 skVertOffset.fY = -skVertOffset.fY;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000987 *offset = skVertOffset;
988}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000989
990uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
991 if (fFBoundingBoxesGlyphOffset) {
992 return fFBoundingBoxesGlyphOffset;
993 }
994 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
995 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
996 if (hheaTable.fData) {
997 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
998 }
999 return fFBoundingBoxesGlyphOffset;
1000}
reed@android.com0680d6c2008-12-19 19:46:15 +00001001
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001002bool SkScalerContext_Mac::generateBBoxes() {
1003 if (fGeneratedFBoundingBoxes) {
bsalomon49f085d2014-09-05 13:34:00 -07001004 return SkToBool(fFBoundingBoxes.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001005 }
1006 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +00001007
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001008 AutoCGTable<SkOTTableHead> headTable(fCGFont);
1009 if (!headTable.fData) {
1010 return false;
1011 }
1012
1013 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
1014 if (!locaTable.fData) {
1015 return false;
1016 }
1017
1018 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
1019 if (!glyfTable.fData) {
1020 return false;
1021 }
1022
1023 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +00001024 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001025
1026 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
1027 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
1028 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
1029 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
1030 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
1031 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
1032 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
1033 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
1034 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
1035 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
1036 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +00001037
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001038 return true;
1039}
1040
1041unsigned SkScalerContext_Mac::generateGlyphCount(void) {
1042 return fGlyphCount;
1043}
1044
1045uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
reedd0f41732015-07-10 12:08:38 -07001046 AUTO_CG_LOCK();
1047
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001048 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001049 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001050
1051 // Get the glyph
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001052 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar);
1053 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001054
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001055 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
1056 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1057 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
1058 CTFontGetGlyphsForCharacters(fCTFont, theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001059 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001060}
1061
1062void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
1063 this->generateMetrics(glyph);
1064}
1065
bungeman@google.comcefd9812013-05-15 15:07:32 +00001066void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
reedd0f41732015-07-10 12:08:38 -07001067 AUTO_CG_LOCK();
1068
djsollen1b277042014-08-06 06:58:06 -07001069 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +00001070 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001071
bungeman@google.comcefd9812013-05-15 15:07:32 +00001072 // The following block produces cgAdvance in CG units (pixels, y up).
1073 CGSize cgAdvance;
1074 if (fVertical) {
bungemanef27ce32015-10-29 09:30:32 -07001075 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontVerticalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001076 &cgGlyph, &cgAdvance, 1);
bungeman3b4b66c2015-01-08 08:33:44 -08001077 // Vertical advances are returned as widths instead of heights.
1078 SkTSwap(cgAdvance.height, cgAdvance.width);
1079 cgAdvance.height = -cgAdvance.height;
bungeman@google.comcefd9812013-05-15 15:07:32 +00001080 } else {
bungemanef27ce32015-10-29 09:30:32 -07001081 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001082 &cgGlyph, &cgAdvance, 1);
1083 }
bungemanef27ce32015-10-29 09:30:32 -07001084 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
benjaminwagner6b3eacb2016-03-24 19:07:58 -07001085 glyph->fAdvanceX = CGToFloat(cgAdvance.width);
1086 glyph->fAdvanceY = -CGToFloat(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001087
bungeman@google.comcefd9812013-05-15 15:07:32 +00001088 // The following produces skBounds in SkGlyph units (pixels, y down),
1089 // or returns early if skBounds would be empty.
1090 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001091
bungeman@google.comcefd9812013-05-15 15:07:32 +00001092 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
1093 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
1094 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
1095 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
1096 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
1097 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
1098 // to center the glyph along the vertical baseline and also perform some mysterious shift
1099 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
1100 // these steps.
1101 //
1102 // It is not known which is correct (or if either is correct). However, we must always draw
1103 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
1104 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001105
bungeman@google.comcefd9812013-05-15 15:07:32 +00001106 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
1107 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001108
bungeman@google.comcefd9812013-05-15 15:07:32 +00001109 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
1110 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
1111 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
1112 // font directly.
1113 if ((isLion() || isMountainLion()) &&
1114 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1115 {
1116 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1117 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1118 return;
1119 }
1120 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1121 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1122 fFUnitMatrix.mapRect(&skBounds);
1123
1124 } else {
1125 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1126 CGRect cgBounds;
1127 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1128 &cgGlyph, &cgBounds, 1);
bungemanef27ce32015-10-29 09:30:32 -07001129 cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001130
bungeman@google.comcefd9812013-05-15 15:07:32 +00001131 // BUG?
1132 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1133 // it should be empty. So, if we see a zero-advance, we check if it has an
1134 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1135 // is rare, so we won't incur a big performance cost for this extra check.
1136 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
halcanary96fcdcc2015-08-27 07:41:13 -07001137 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, nullptr));
1138 if (nullptr == path || CGPathIsEmpty(path)) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001139 return;
1140 }
1141 }
1142
1143 if (CGRectIsEmpty_inline(cgBounds)) {
1144 return;
1145 }
1146
1147 // Convert cgBounds to SkGlyph units (pixels, y down).
1148 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1149 cgBounds.size.width, cgBounds.size.height);
1150 }
1151
1152 if (fVertical) {
1153 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1154 // Convert these horizontal bounds into vertical bounds.
1155 SkPoint offset;
1156 getVerticalOffset(cgGlyph, &offset);
1157 skBounds.offset(offset);
1158 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001159
bungeman@google.comcefd9812013-05-15 15:07:32 +00001160 // Currently the bounds are based on being rendered at (0,0).
1161 // The top left must not move, since that is the base from which subpixel positioning is offset.
1162 if (fDoSubPosition) {
1163 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1164 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1165 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001166
bungeman@google.comcefd9812013-05-15 15:07:32 +00001167 SkIRect skIBounds;
1168 skBounds.roundOut(&skIBounds);
1169 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1170 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1171 // is not currently known, as CG dilates the outlines by some percentage.
1172 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1173 skIBounds.outset(1, 1);
1174 glyph->fLeft = SkToS16(skIBounds.fLeft);
1175 glyph->fTop = SkToS16(skIBounds.fTop);
1176 glyph->fWidth = SkToU16(skIBounds.width());
1177 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001178}
1179
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001180#include "SkColorPriv.h"
1181
bungemane280d062016-03-24 11:27:05 -07001182static void build_power_table(uint8_t table[]) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001183 for (int i = 0; i < 256; i++) {
1184 float x = i / 255.f;
bungemane280d062016-03-24 11:27:05 -07001185 int xx = SkScalarRoundToInt(x * x * 255);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001186 table[i] = SkToU8(xx);
1187 }
1188}
1189
1190/**
1191 * This will invert the gamma applied by CoreGraphics, so we can get linear
1192 * values.
1193 *
1194 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1195 * The color space used does not appear to affect this choice.
1196 */
1197static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1198 static bool gInited;
1199 static uint8_t gTableCoreGraphicsSmoothing[256];
1200 if (!gInited) {
bungemane280d062016-03-24 11:27:05 -07001201 build_power_table(gTableCoreGraphicsSmoothing);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001202 gInited = true;
1203 }
1204 return gTableCoreGraphicsSmoothing;
1205}
1206
1207static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1208 while (count > 0) {
1209 uint8_t mask = 0;
1210 for (int i = 7; i >= 0; --i) {
bungemane280d062016-03-24 11:27:05 -07001211 mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001212 if (0 == --count) {
1213 break;
1214 }
1215 }
1216 *dst++ = mask;
1217 }
1218}
1219
1220template<bool APPLY_PREBLEND>
1221static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
bungemane280d062016-03-24 11:27:05 -07001222 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
1223 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
1224 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
bungeman3b4b66c2015-01-08 08:33:44 -08001225 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1226#if SK_SHOW_TEXT_BLIT_COVERAGE
1227 lum = SkTMax(lum, (U8CPU)0x30);
1228#endif
1229 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001230}
1231template<bool APPLY_PREBLEND>
1232static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1233 const SkGlyph& glyph, const uint8_t* table8) {
1234 const int width = glyph.fWidth;
1235 size_t dstRB = glyph.rowBytes();
1236 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1237
1238 for (int y = 0; y < glyph.fHeight; y++) {
1239 for (int i = 0; i < width; ++i) {
1240 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1241 }
1242 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1243 dst += dstRB;
1244 }
1245}
1246
1247template<bool APPLY_PREBLEND>
1248static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1249 const uint8_t* tableG,
1250 const uint8_t* tableB) {
bungemane280d062016-03-24 11:27:05 -07001251 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
1252 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
1253 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001254#if SK_SHOW_TEXT_BLIT_COVERAGE
1255 r = SkTMax(r, (U8CPU)0x30);
1256 g = SkTMax(g, (U8CPU)0x30);
1257 b = SkTMax(b, (U8CPU)0x30);
1258#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001259 return SkPack888ToRGB16(r, g, b);
1260}
1261template<bool APPLY_PREBLEND>
1262static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1263 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1264 const int width = glyph.fWidth;
1265 size_t dstRB = glyph.rowBytes();
1266 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1267
1268 for (int y = 0; y < glyph.fHeight; y++) {
1269 for (int i = 0; i < width; i++) {
1270 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1271 }
1272 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1273 dst = (uint16_t*)((char*)dst + dstRB);
1274 }
1275}
1276
bungeman34902632014-12-10 21:43:27 -08001277static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1278 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001279 U8CPU r = (rgb >> 16) & 0xFF;
1280 U8CPU g = (rgb >> 8) & 0xFF;
1281 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001282#if SK_SHOW_TEXT_BLIT_COVERAGE
1283 a = SkTMax(a, (U8CPU)0x30);
1284#endif
bungeman34902632014-12-10 21:43:27 -08001285 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001286}
reed@google.comf77b35d2013-05-02 20:39:44 +00001287
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001288void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
djsollen1b277042014-08-06 06:58:06 -07001289 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001290
1291 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1292 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1293
1294 // Draw the glyph
1295 size_t cgRowBytes;
1296 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
halcanary96fcdcc2015-08-27 07:41:13 -07001297 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001298 return;
1299 }
1300
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001301 // Fix the glyph
1302 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1303 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1304 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1305
1306 //Note that the following cannot really be integrated into the
1307 //pre-blend, since we may not be applying the pre-blend; when we aren't
1308 //applying the pre-blend it means that a filter wants linear anyway.
1309 //Other code may also be applying the pre-blend, so we'd need another
1310 //one with this and one without.
1311 CGRGBPixel* addr = cgPixels;
1312 for (int y = 0; y < glyph.fHeight; ++y) {
1313 for (int x = 0; x < glyph.fWidth; ++x) {
1314 int r = (addr[x] >> 16) & 0xFF;
1315 int g = (addr[x] >> 8) & 0xFF;
1316 int b = (addr[x] >> 0) & 0xFF;
1317 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1318 }
bungemane280d062016-03-24 11:27:05 -07001319 addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001320 }
1321 }
1322
1323 // Convert glyph to mask
1324 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001325 case SkMask::kLCD16_Format: {
1326 if (fPreBlend.isApplicable()) {
1327 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1328 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1329 } else {
1330 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1331 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1332 }
1333 } break;
1334 case SkMask::kA8_Format: {
1335 if (fPreBlend.isApplicable()) {
1336 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1337 } else {
1338 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1339 }
1340 } break;
1341 case SkMask::kBW_Format: {
1342 const int width = glyph.fWidth;
1343 size_t dstRB = glyph.rowBytes();
1344 uint8_t* dst = (uint8_t*)glyph.fImage;
1345 for (int y = 0; y < glyph.fHeight; y++) {
1346 cgpixels_to_bits(dst, cgPixels, width);
1347 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1348 dst += dstRB;
1349 }
1350 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001351 case SkMask::kARGB32_Format: {
1352 const int width = glyph.fWidth;
1353 size_t dstRB = glyph.rowBytes();
1354 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1355 for (int y = 0; y < glyph.fHeight; y++) {
1356 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001357 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001358 }
1359 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1360 dst = (SkPMColor*)((char*)dst + dstRB);
1361 }
1362 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001363 default:
1364 SkDEBUGFAIL("unexpected mask format");
1365 break;
1366 }
1367}
1368
1369/*
1370 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1371 * seems sufficient, and possibly even correct, to allow the hinted outline
1372 * to be subpixel positioned.
1373 */
1374#define kScaleForSubPixelPositionHinting (4.0f)
1375
Ben Wagner6e9ac122016-11-11 14:31:06 -05001376void SkScalerContext_Mac::generatePath(SkGlyphID glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001377 AUTO_CG_LOCK();
1378
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001379 SkScalar scaleX = SK_Scalar1;
1380 SkScalar scaleY = SK_Scalar1;
1381
bungemanef27ce32015-10-29 09:30:32 -07001382 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001383 /*
1384 * For subpixel positioning, we want to return an unhinted outline, so it
1385 * can be positioned nicely at fractional offsets. However, we special-case
1386 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1387 * we want to retain hinting in the direction orthogonal to the baseline.
1388 * e.g. for horizontal baseline, we want to retain hinting in Y.
1389 * The way we remove hinting is to scale the font by some value (4) in that
1390 * direction, ask for the path, and then scale the path back down.
1391 */
bungeman7cbeaae2015-09-22 09:54:56 -07001392 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001393 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001394 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001395 // now see if we need to restore hinting for axis-aligned baselines
bungeman27876bc2016-02-29 11:22:55 -08001396 switch (this->computeAxisAlignmentForHText()) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001397 case kX_SkAxisAlignment:
1398 scaleY = SK_Scalar1; // want hinting in the Y direction
1399 break;
1400 case kY_SkAxisAlignment:
1401 scaleX = SK_Scalar1; // want hinting in the X direction
1402 break;
1403 default:
1404 break;
1405 }
1406
bungemanef27ce32015-10-29 09:30:32 -07001407 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1408 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001409 }
1410
Ben Wagner6e9ac122016-11-11 14:31:06 -05001411 CGGlyph cgGlyph = SkTo<CGGlyph>(glyph);
bungemanef27ce32015-10-29 09:30:32 -07001412 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont, cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001413
1414 path->reset();
halcanary96fcdcc2015-08-27 07:41:13 -07001415 if (cgPath != nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001416 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1417 }
1418
bungeman@google.comcefd9812013-05-15 15:07:32 +00001419 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001420 SkMatrix m;
1421 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1422 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001423 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001424 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001425 SkPoint offset;
1426 getVerticalOffset(cgGlyph, &offset);
1427 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001428 }
1429}
1430
bungeman41078062014-07-07 08:16:37 -07001431void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001432 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001433 return;
1434 }
1435
reedd0f41732015-07-10 12:08:38 -07001436 AUTO_CG_LOCK();
1437
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001438 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1439
bungeman41078062014-07-07 08:16:37 -07001440 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1441 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1442 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1443 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1444 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1445 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1446 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1447 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001448 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungeman41078062014-07-07 08:16:37 -07001449 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
bungeman6bd8d1c2015-06-09 08:40:51 -07001450 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont));
bungeman41078062014-07-07 08:16:37 -07001451 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont));
1452 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001453
bungeman41078062014-07-07 08:16:37 -07001454 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
1455 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001456}
1457
1458void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1459 SkPath* skPath = (SkPath*)info;
1460
1461 // Process the path element
1462 switch (element->type) {
1463 case kCGPathElementMoveToPoint:
1464 skPath->moveTo(element->points[0].x, -element->points[0].y);
1465 break;
1466
1467 case kCGPathElementAddLineToPoint:
1468 skPath->lineTo(element->points[0].x, -element->points[0].y);
1469 break;
1470
1471 case kCGPathElementAddQuadCurveToPoint:
1472 skPath->quadTo(element->points[0].x, -element->points[0].y,
1473 element->points[1].x, -element->points[1].y);
1474 break;
1475
1476 case kCGPathElementAddCurveToPoint:
1477 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1478 element->points[1].x, -element->points[1].y,
1479 element->points[2].x, -element->points[2].y);
1480 break;
1481
1482 case kCGPathElementCloseSubpath:
1483 skPath->close();
1484 break;
1485
1486 default:
1487 SkDEBUGFAIL("Unknown path element!");
1488 break;
1489 }
1490}
1491
1492
1493///////////////////////////////////////////////////////////////////////////////
1494
halcanary96fcdcc2015-08-27 07:41:13 -07001495// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001496// Call must still manage its ownership of provider
1497static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1498 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
halcanary96fcdcc2015-08-27 07:41:13 -07001499 if (nullptr == cg) {
1500 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001501 }
halcanary96fcdcc2015-08-27 07:41:13 -07001502 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr);
bungemanbea97482016-08-24 08:29:50 -07001503 return ct ? create_from_CTFontRef(ct, nullptr, true) : nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001504}
1505
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001506// Web fonts added to the the CTFont registry do not return their character set.
1507// Iterate through the font in this case. The existing caller caches the result,
1508// so the performance impact isn't too bad.
1509static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1510 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001511 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001512 SkUnichar* out = glyphToUnicode->begin();
1513 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1514 UniChar unichar = 0;
1515 while (glyphCount > 0) {
1516 CGGlyph glyph;
1517 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1518 out[glyph] = unichar;
1519 --glyphCount;
1520 }
1521 if (++unichar == 0) {
1522 break;
1523 }
1524 }
1525}
1526
1527// Construct Glyph to Unicode table.
1528// Unicode code points that require conjugate pairs in utf16 are not
1529// supported.
1530static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1531 SkTDArray<SkUnichar>* glyphToUnicode) {
1532 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1533 if (!charSet) {
1534 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1535 return;
1536 }
1537
1538 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1539 charSet));
1540 if (!bitmap) {
1541 return;
1542 }
1543 CFIndex length = CFDataGetLength(bitmap);
1544 if (!length) {
1545 return;
1546 }
1547 if (length > 8192) {
1548 // TODO: Add support for Unicode above 0xFFFF
1549 // Consider only the BMP portion of the Unicode character points.
1550 // The bitmap may contain other planes, up to plane 16.
1551 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1552 length = 8192;
1553 }
1554 const UInt8* bits = CFDataGetBytePtr(bitmap);
reed@google.com7fa2a652014-01-27 13:42:58 +00001555 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001556 SkUnichar* out = glyphToUnicode->begin();
1557 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1558 for (int i = 0; i < length; i++) {
1559 int mask = bits[i];
1560 if (!mask) {
1561 continue;
1562 }
1563 for (int j = 0; j < 8; j++) {
1564 CGGlyph glyph;
1565 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1566 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1567 out[glyph] = unichar;
1568 }
1569 }
1570 }
1571}
1572
halcanary96fcdcc2015-08-27 07:41:13 -07001573/** Assumes src and dst are not nullptr. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001574static void CFStringToSkString(CFStringRef src, SkString* dst) {
1575 // Reserve enough room for the worst-case string,
1576 // plus 1 byte for the trailing null.
1577 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1578 kCFStringEncodingUTF8) + 1;
1579 dst->resize(length);
1580 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1581 // Resize to the actual UTF-8 length used, stripping the null character.
1582 dst->resize(strlen(dst->c_str()));
1583}
1584
reed@google.com2689f612013-03-20 20:01:47 +00001585SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -07001586 PerGlyphInfo perGlyphInfo,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001587 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001588 uint32_t glyphIDsCount) const {
1589
reedd0f41732015-07-10 12:08:38 -07001590 AUTO_CG_LOCK();
1591
reed@google.com2689f612013-03-20 20:01:47 +00001592 CTFontRef originalCTFont = fFontRef.get();
bungeman7cbeaae2015-09-22 09:54:56 -07001593 AutoCFRelease<CTFontRef> ctFont(ctfont_create_exact_copy(
bungeman05846312015-09-23 12:51:28 -07001594 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -07001595
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001596 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1597
1598 {
1599 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
bungeman256b3512014-07-02 07:57:59 -07001600 if (fontName.get()) {
1601 CFStringToSkString(fontName, &info->fFontName);
1602 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001603 }
1604
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001605 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1606 info->fLastGlyphID = SkToU16(glyphCount - 1);
1607 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1608
reed39a9a502015-05-12 09:50:04 -07001609 if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001610 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1611 }
1612
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001613 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1614 // fonts always have both glyf and loca tables. At the least, this is what
1615 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1616 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001617 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001618 return info;
1619 }
1620
1621 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1622 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1623 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1624 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1625 }
1626 if (symbolicTraits & kCTFontItalicTrait) {
1627 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1628 }
1629 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001630 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1631 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1632 } else if (stylisticClass & kCTFontScriptsClass) {
1633 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1634 }
1635 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1636 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1637 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1638 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1639 CGRect bbox = CTFontGetBoundingBox(ctFont);
1640
1641 SkRect r;
1642 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1643 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1644 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1645 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1646
1647 r.roundOut(&(info->fBBox));
1648
1649 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1650 // This probably isn't very good with an italic font.
1651 int16_t min_width = SHRT_MAX;
1652 info->fStemV = 0;
1653 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1654 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1655 CGGlyph glyphs[count];
1656 CGRect boundingRects[count];
1657 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1658 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1659 glyphs, boundingRects, count);
1660 for (size_t i = 0; i < count; i++) {
1661 int16_t width = (int16_t) boundingRects[i].size.width;
1662 if (width > 0 && width < min_width) {
1663 min_width = width;
1664 info->fStemV = min_width;
1665 }
1666 }
1667 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001668 return info;
1669}
1670
1671///////////////////////////////////////////////////////////////////////////////
1672
reed@google.comcc9aad52013-03-21 19:28:10 +00001673static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1674 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001675 AutoCFRelease<CFNumberRef> fontFormatRef(
1676 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1677 if (!fontFormatRef) {
1678 return 0;
1679 }
1680
1681 SInt32 fontFormatValue;
1682 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1683 return 0;
1684 }
1685
1686 switch (fontFormatValue) {
1687 case kCTFontFormatOpenTypePostScript:
1688 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1689 case kCTFontFormatOpenTypeTrueType:
1690 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1691 case kCTFontFormatTrueType:
1692 return SkSFNTHeader::fontType_MacTrueType::TAG;
1693 case kCTFontFormatPostScript:
1694 return SkSFNTHeader::fontType_PostScript::TAG;
1695 case kCTFontFormatBitmap:
1696 return SkSFNTHeader::fontType_MacTrueType::TAG;
1697 case kCTFontFormatUnrecognized:
1698 default:
1699 //CT seems to be unreliable in being able to obtain the type,
1700 //even if all we want is the first four bytes of the font resource.
1701 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1702 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1703 }
1704}
1705
bungeman5f213d92015-01-27 05:39:10 -08001706SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
reed@google.comcc9aad52013-03-21 19:28:10 +00001707 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001708 if (0 == fontType) {
halcanary96fcdcc2015-08-27 07:41:13 -07001709 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001710 }
1711
1712 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001713 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001714 SkTDArray<SkFontTableTag> tableTags;
1715 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001716 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001717
1718 // calc total size for font, save sizes
1719 SkTDArray<size_t> tableSizes;
1720 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1721 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001722 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001723 totalSize += (tableSize + 3) & ~3;
1724 *tableSizes.append() = tableSize;
1725 }
1726
1727 // reserve memory for stream, and zero it (tables must be zero padded)
1728 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1729 char* dataStart = (char*)stream->getMemoryBase();
1730 sk_bzero(dataStart, totalSize);
1731 char* dataPtr = dataStart;
1732
1733 // compute font header entries
1734 uint16_t entrySelector = 0;
1735 uint16_t searchRange = 1;
1736 while (searchRange < numTables >> 1) {
1737 entrySelector++;
1738 searchRange <<= 1;
1739 }
1740 searchRange <<= 4;
1741 uint16_t rangeShift = (numTables << 4) - searchRange;
1742
1743 // write font header
1744 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1745 header->fontType = fontType;
1746 header->numTables = SkEndian_SwapBE16(numTables);
1747 header->searchRange = SkEndian_SwapBE16(searchRange);
1748 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1749 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1750 dataPtr += sizeof(SkSFNTHeader);
1751
1752 // write tables
1753 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1754 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1755 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1756 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001757 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001758 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1759 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1760 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001761 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1762 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001763
1764 dataPtr += (tableSize + 3) & ~3;
1765 ++entry;
1766 }
1767
bungemanb3310c22015-03-02 09:05:36 -08001768 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001769 return stream;
1770}
1771
bungeman41868fe2015-05-20 09:21:04 -07001772struct NonDefaultAxesContext {
1773 SkFixed* axisValue;
1774 CFArrayRef cgAxes;
1775};
1776static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1777 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1778
1779 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1780 return;
1781 }
1782
1783 // The key is a CFString which is a string from the 'name' table.
1784 // Search the cgAxes for an axis with this name, and use its index to store the value.
1785 CFIndex keyIndex = -1;
1786 CFStringRef keyString = static_cast<CFStringRef>(key);
1787 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1788 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1789 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1790 continue;
1791 }
1792
1793 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1794 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1795 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1796 continue;
1797 }
1798 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1799 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1800 keyIndex = i;
1801 break;
1802 }
1803 }
1804 if (keyIndex == -1) {
1805 return;
1806 }
1807
1808 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1809 double valueDouble;
1810 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1811 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1812 {
1813 return;
1814 }
1815 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1816}
1817static bool get_variations(CTFontRef fFontRef, CFIndex* cgAxisCount,
1818 SkAutoSTMalloc<4, SkFixed>* axisValues)
1819{
1820 // CTFontCopyVariationAxes and CTFontCopyVariation do not work when applied to fonts which
halcanary96fcdcc2015-08-27 07:41:13 -07001821 // started life with CGFontCreateWithDataProvider (they simply always return nullptr).
bungeman41868fe2015-05-20 09:21:04 -07001822 // As a result, we are limited to CGFontCopyVariationAxes and CGFontCopyVariations.
halcanary96fcdcc2015-08-27 07:41:13 -07001823 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07001824
1825 AutoCFRelease<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont));
halcanary96fcdcc2015-08-27 07:41:13 -07001826 // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
bungeman41868fe2015-05-20 09:21:04 -07001827 if (!cgVariations.get()) {
1828 return false;
1829 }
1830
1831 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont));
1832 *cgAxisCount = CFArrayGetCount(cgAxes);
1833 axisValues->reset(*cgAxisCount);
1834
1835 // Set all of the axes to their default values.
1836 // Fail if any default value cannot be determined.
1837 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
1838 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
1839 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1840 return false;
1841 }
1842
1843 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1844 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1845 kCGFontVariationAxisDefaultValue);
1846 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1847 return false;
1848 }
1849 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
1850 double axisDefaultValueDouble;
1851 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
1852 {
1853 return false;
1854 }
1855 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
1856 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
1857 {
1858 return false;
1859 }
1860 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
1861 }
1862
1863 // Override the default values with the given font's stated axis values.
1864 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
1865 CFDictionaryApplyFunction(cgVariations, set_non_default_axes, &c);
1866
1867 return true;
1868}
bungemanf93d7112016-09-16 06:24:20 -07001869std::unique_ptr<SkFontData> SkTypeface_Mac::onMakeFontData() const {
bungeman41868fe2015-05-20 09:21:04 -07001870 int index;
bungemanf93d7112016-09-16 06:24:20 -07001871 std::unique_ptr<SkStreamAsset> stream(this->onOpenStream(&index));
bungeman41868fe2015-05-20 09:21:04 -07001872
1873 CFIndex cgAxisCount;
1874 SkAutoSTMalloc<4, SkFixed> axisValues;
1875 if (get_variations(fFontRef, &cgAxisCount, &axisValues)) {
bungemanf93d7112016-09-16 06:24:20 -07001876 return skstd::make_unique<SkFontData>(std::move(stream), index,
1877 axisValues.get(), cgAxisCount);
bungeman41868fe2015-05-20 09:21:04 -07001878 }
bungemanf93d7112016-09-16 06:24:20 -07001879 return skstd::make_unique<SkFontData>(std::move(stream), index, nullptr, 0);
bungeman41868fe2015-05-20 09:21:04 -07001880}
1881
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001882///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001883///////////////////////////////////////////////////////////////////////////////
1884
1885int SkTypeface_Mac::onGetUPEM() const {
halcanary96fcdcc2015-08-27 07:41:13 -07001886 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001887 return CGFontGetUnitsPerEm(cgFont);
1888}
1889
bungeman@google.com839702b2013-08-07 17:09:22 +00001890SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001891 SkTypeface::LocalizedStrings* nameIter =
1892 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07001893 if (nullptr == nameIter) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001894 AutoCFRelease<CFStringRef> cfLanguage;
1895 AutoCFRelease<CFStringRef> cfFamilyName(
1896 CTFontCopyLocalizedName(fFontRef, kCTFontFamilyNameKey, &cfLanguage));
1897
1898 SkString skLanguage;
1899 SkString skFamilyName;
1900 if (cfLanguage.get()) {
1901 CFStringToSkString(cfLanguage.get(), &skLanguage);
1902 } else {
1903 skLanguage = "und"; //undetermined
1904 }
1905 if (cfFamilyName.get()) {
1906 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1907 }
1908
1909 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1910 }
1911 return nameIter;
1912}
1913
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001914// If, as is the case with web fonts, the CTFont data isn't available,
1915// the CGFont data may work. While the CGFont may always provide the
1916// right result, leave the CTFont code path to minimize disruption.
1917static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1918 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1919 kCTFontTableOptionNoOptions);
halcanary96fcdcc2015-08-27 07:41:13 -07001920 if (nullptr == data) {
1921 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001922 data = CGFontCopyTableForTag(cgFont, tag);
1923 }
1924 return data;
1925}
1926
1927int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1928 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1929 kCTFontTableOptionNoOptions));
halcanary96fcdcc2015-08-27 07:41:13 -07001930 if (nullptr == cfArray) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001931 return 0;
1932 }
reed@google.com7fa2a652014-01-27 13:42:58 +00001933 int count = SkToInt(CFArrayGetCount(cfArray));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001934 if (tags) {
1935 for (int i = 0; i < count; ++i) {
1936 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1937 tags[i] = static_cast<SkFontTableTag>(fontTag);
1938 }
1939 }
1940 return count;
1941}
1942
1943size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1944 size_t length, void* dstData) const {
1945 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
halcanary96fcdcc2015-08-27 07:41:13 -07001946 if (nullptr == srcData) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001947 return 0;
1948 }
1949
1950 size_t srcSize = CFDataGetLength(srcData);
1951 if (offset >= srcSize) {
1952 return 0;
1953 }
1954 if (length > srcSize - offset) {
1955 length = srcSize - offset;
1956 }
1957 if (dstData) {
1958 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1959 }
1960 return length;
1961}
1962
reeda9322c22016-04-12 06:47:05 -07001963SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkScalerContextEffects& effects,
1964 const SkDescriptor* desc) const {
bungeman7cfd46a2016-10-20 16:06:52 -04001965 return new SkScalerContext_Mac(sk_ref_sp(const_cast<SkTypeface_Mac*>(this)), effects, desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001966}
1967
1968void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001969 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1970 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1971 {
1972 rec->fMaskFormat = SkMask::kA8_Format;
1973 // Render the glyphs as close as possible to what was requested.
1974 // The above turns off subpixel rendering, but the user requested it.
1975 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1976 // See comments below for more details.
1977 rec->setHinting(SkPaint::kNormal_Hinting);
1978 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001979
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001980 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00001981 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001982 SkScalerContext::kLCD_BGROrder_Flag |
1983 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001984
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001985 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001986
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001987 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001988
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001989 // Only two levels of hinting are supported.
1990 // kNo_Hinting means avoid CoreGraphics outline dilation.
1991 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1992 // If there is no lcd support, hinting (dilation) cannot be supported.
1993 SkPaint::Hinting hinting = rec->getHinting();
1994 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1995 hinting = SkPaint::kNo_Hinting;
1996 } else if (SkPaint::kFull_Hinting == hinting) {
1997 hinting = SkPaint::kNormal_Hinting;
1998 }
1999 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002000
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002001 // FIXME: lcd smoothed un-hinted rasterization unsupported.
2002 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
2003 // There is no current means to honor a request for unhinted lcd,
2004 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002005
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002006 // Hinting and smoothing should be orthogonal, but currently they are not.
2007 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
2008 // output is drawn from auto-dilated outlines (the amount of which is
2009 // determined by AppleFontSmoothing). Its regular anti-aliased output is
2010 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002011
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002012 // The behavior of Skia is as follows:
2013 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
2014 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
2015 // channel. This matches [LCD][yes-hint] in weight.
2016 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
2017 // Currenly side with LCD, effectively ignoring the hinting setting.
2018 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002019
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002020 if (isLCDFormat(rec->fMaskFormat)) {
2021 if (lcdSupport) {
2022 //CoreGraphics creates 555 masks for smoothed text anyway.
2023 rec->fMaskFormat = SkMask::kLCD16_Format;
2024 rec->setHinting(SkPaint::kNormal_Hinting);
2025 } else {
2026 rec->fMaskFormat = SkMask::kA8_Format;
2027 }
2028 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002029
bungeman34902632014-12-10 21:43:27 -08002030 // CoreText provides no information as to whether a glyph will be color or not.
2031 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
2032 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08002033 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08002034 rec->fMaskFormat = SkMask::kARGB32_Format;
2035 }
2036
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002037 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2038 // All other masks can use regular gamma.
2039 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
2040#ifndef SK_GAMMA_APPLY_TO_A8
brianosmana1e8f8d2016-04-08 06:47:54 -07002041 // SRGBTODO: Is this correct? Do we want contrast boost?
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002042 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002043#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002044 } else {
2045 //CoreGraphics dialates smoothed text as needed.
2046 rec->setContrast(0);
2047 }
2048}
2049
2050// we take ownership of the ref
2051static const char* get_str(CFStringRef ref, SkString* str) {
halcanary96fcdcc2015-08-27 07:41:13 -07002052 if (nullptr == ref) {
2053 return nullptr;
bungeman256b3512014-07-02 07:57:59 -07002054 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002055 CFStringToSkString(ref, str);
2056 CFSafeRelease(ref);
2057 return str->c_str();
2058}
2059
bungemanb374d6a2014-09-17 07:48:59 -07002060void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
2061 get_str(CTFontCopyFamilyName(fFontRef), familyName);
2062}
2063
reed@google.com5526ede2013-03-25 13:03:37 +00002064void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2065 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002066 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002067
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002068 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
2069 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
2070 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
bungemanb8113782016-07-25 16:54:59 -07002071 desc->setStyle(this->fontStyle());
caseq26337e92014-06-30 12:14:52 -07002072 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002073}
reed@google.com5526ede2013-03-25 13:03:37 +00002074
reed@google.combcb42ae2013-07-02 13:56:39 +00002075int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002076 uint16_t glyphs[], int glyphCount) const
2077{
2078 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
2079 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
2080 // 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 +00002081
reed@google.combcb42ae2013-07-02 13:56:39 +00002082 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002083 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
2084 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00002085 switch (encoding) {
2086 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002087 const char* utf8 = reinterpret_cast<const char*>(chars);
2088 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2089 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00002090 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002091 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
2092 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00002093 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002094 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002095 break;
2096 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002097 case kUTF16_Encoding: {
2098 src = reinterpret_cast<const UniChar*>(chars);
2099 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00002100 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002101 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2102 ++extra;
2103 }
reed@google.combcb42ae2013-07-02 13:56:39 +00002104 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002105 srcCount = glyphCount + extra;
2106 break;
2107 }
2108 case kUTF32_Encoding: {
2109 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
2110 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2111 src = utf16;
2112 for (int i = 0; i < glyphCount; ++i) {
2113 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
2114 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002115 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002116 break;
2117 }
2118 }
2119
halcanary96fcdcc2015-08-27 07:41:13 -07002120 // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002121 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002122 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2123 uint16_t* macGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002124 if (nullptr == macGlyphs || srcCount > glyphCount) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002125 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002126 }
2127
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002128 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, srcCount);
2129
2130 // If there were any non-bmp, then copy and compact.
halcanary96fcdcc2015-08-27 07:41:13 -07002131 // If 'glyphs' is nullptr, then compact glyphStorage in-place.
2132 // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
2133 // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002134 uint16_t* compactedGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002135 if (nullptr == compactedGlyphs) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002136 compactedGlyphs = macGlyphs;
2137 }
2138 if (srcCount > glyphCount) {
2139 int extra = 0;
2140 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002141 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002142 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2143 ++extra;
2144 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002145 }
2146 }
2147
2148 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002149 return glyphCount;
2150 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002151
2152 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002153 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002154 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002155 return i;
2156 }
2157 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002158 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002159 return glyphCount;
2160}
2161
2162int SkTypeface_Mac::onCountGlyphs() const {
reed@google.com7fa2a652014-01-27 13:42:58 +00002163 return SkToInt(CTFontGetGlyphCount(fFontRef));
reed@google.combcb42ae2013-07-02 13:56:39 +00002164}
2165
reed@google.com95625db2013-03-25 20:44:02 +00002166///////////////////////////////////////////////////////////////////////////////
2167///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002168
2169static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
2170 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
halcanary96fcdcc2015-08-27 07:41:13 -07002171 if (nullptr == ref.get()) {
reed@google.com83787c52013-03-26 17:19:15 +00002172 return false;
2173 }
2174 CFStringToSkString(ref, value);
2175 return true;
2176}
2177
reed@google.com95625db2013-03-25 20:44:02 +00002178#include "SkFontMgr.h"
2179
reed@google.com964988f2013-03-29 14:57:22 +00002180static inline int sqr(int value) {
2181 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2182 return value * value;
2183}
2184
2185// We normalize each axis (weight, width, italic) to be base-900
2186static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2187 return sqr(a.weight() - b.weight()) +
2188 sqr((a.width() - b.width()) * 100) +
bungemanb4bb7d82016-04-27 10:21:04 -07002189 sqr((a.slant() != b.slant()) * 900);
reed@google.com964988f2013-03-29 14:57:22 +00002190}
2191
reed@google.com83787c52013-03-26 17:19:15 +00002192class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002193public:
bungeman53d5c6e2016-04-08 07:22:29 -07002194 SkFontStyleSet_Mac(CTFontDescriptorRef desc)
halcanary96fcdcc2015-08-27 07:41:13 -07002195 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
reed@google.comdea7ee02013-03-28 14:12:10 +00002196 , fCount(0) {
halcanary96fcdcc2015-08-27 07:41:13 -07002197 if (nullptr == fArray) {
2198 fArray = CFArrayCreate(nullptr, nullptr, 0, nullptr);
reed@google.comdea7ee02013-03-28 14:12:10 +00002199 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002200 fCount = SkToInt(CFArrayGetCount(fArray));
reed@google.com83787c52013-03-26 17:19:15 +00002201 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002202
reed@google.com83787c52013-03-26 17:19:15 +00002203 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002204 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002205 }
2206
mtklein36352bf2015-03-25 18:17:31 -07002207 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002208 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002209 }
2210
mtklein36352bf2015-03-25 18:17:31 -07002211 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002212 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002213 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2214 if (style) {
bungeman336fdf22014-11-10 07:48:55 -08002215 *style = fontstyle_from_descriptor(desc);
reed@google.com83787c52013-03-26 17:19:15 +00002216 }
2217 if (name) {
2218 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2219 name->reset();
2220 }
2221 }
2222 }
2223
mtklein36352bf2015-03-25 18:17:31 -07002224 SkTypeface* createTypeface(int index) override {
reed@google.com83787c52013-03-26 17:19:15 +00002225 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2226 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002227
bungemanbea97482016-08-24 08:29:50 -07002228 return create_from_desc(desc);
reed@google.com83787c52013-03-26 17:19:15 +00002229 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002230
mtklein36352bf2015-03-25 18:17:31 -07002231 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002232 if (0 == fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002233 return nullptr;
reed@google.com964988f2013-03-29 14:57:22 +00002234 }
bungemanbea97482016-08-24 08:29:50 -07002235 return create_from_desc(findMatchingDesc(pattern));
reed@google.com964988f2013-03-29 14:57:22 +00002236 }
2237
reed@google.com83787c52013-03-26 17:19:15 +00002238private:
2239 CFArrayRef fArray;
reed@google.comdea7ee02013-03-28 14:12:10 +00002240 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002241
2242 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2243 int bestMetric = SK_MaxS32;
halcanary96fcdcc2015-08-27 07:41:13 -07002244 CTFontDescriptorRef bestDesc = nullptr;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002245
reed@google.com964988f2013-03-29 14:57:22 +00002246 for (int i = 0; i < fCount; ++i) {
2247 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
bungeman336fdf22014-11-10 07:48:55 -08002248 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc));
reed@google.com964988f2013-03-29 14:57:22 +00002249 if (0 == metric) {
2250 return desc;
2251 }
2252 if (metric < bestMetric) {
2253 bestMetric = metric;
2254 bestDesc = desc;
2255 }
2256 }
2257 SkASSERT(bestDesc);
2258 return bestDesc;
2259 }
reed@google.com83787c52013-03-26 17:19:15 +00002260};
2261
2262class SkFontMgr_Mac : public SkFontMgr {
reed@google.com83787c52013-03-26 17:19:15 +00002263 CFArrayRef fNames;
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002264 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002265
2266 CFStringRef stringAt(int index) const {
2267 SkASSERT((unsigned)index < (unsigned)fCount);
2268 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2269 }
2270
reed@google.com964988f2013-03-29 14:57:22 +00002271 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2272 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2273 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2274 &kCFTypeDictionaryKeyCallBacks,
2275 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002276
reed@google.com964988f2013-03-29 14:57:22 +00002277 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002278
reed@google.com964988f2013-03-29 14:57:22 +00002279 AutoCFRelease<CTFontDescriptorRef> desc(
2280 CTFontDescriptorCreateWithAttributes(cfAttr));
bungeman53d5c6e2016-04-08 07:22:29 -07002281 return new SkFontStyleSet_Mac(desc);
reed@google.com964988f2013-03-29 14:57:22 +00002282 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002283
reed@google.com83787c52013-03-26 17:19:15 +00002284public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002285 SkFontMgr_Mac()
2286 : fNames(SkCTFontManagerCopyAvailableFontFamilyNames())
2287 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames)) : 0) {}
reed@google.com83787c52013-03-26 17:19:15 +00002288
2289 virtual ~SkFontMgr_Mac() {
2290 CFSafeRelease(fNames);
2291 }
reed@google.com95625db2013-03-25 20:44:02 +00002292
2293protected:
mtklein36352bf2015-03-25 18:17:31 -07002294 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002295 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002296 }
2297
mtklein36352bf2015-03-25 18:17:31 -07002298 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002299 if ((unsigned)index < (unsigned)fCount) {
2300 CFStringToSkString(this->stringAt(index), familyName);
2301 } else {
2302 familyName->reset();
2303 }
reed@google.com95625db2013-03-25 20:44:02 +00002304 }
2305
mtklein36352bf2015-03-25 18:17:31 -07002306 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002307 if ((unsigned)index >= (unsigned)fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002308 return nullptr;
reed@google.com83787c52013-03-26 17:19:15 +00002309 }
reed@google.com964988f2013-03-29 14:57:22 +00002310 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002311 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002312
mtklein36352bf2015-03-25 18:17:31 -07002313 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
reed@google.com964988f2013-03-29 14:57:22 +00002314 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2315 return CreateSet(cfName);
2316 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002317
reed@google.com95625db2013-03-25 20:44:02 +00002318 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
bungeman5eff9e72016-01-21 08:18:42 -08002319 const SkFontStyle& fontStyle) const override {
Hal Canary67b39de2016-11-07 11:47:44 -05002320 sk_sp<SkFontStyleSet> sset(this->matchFamily(familyName));
bungeman5eff9e72016-01-21 08:18:42 -08002321 return sset->matchStyle(fontStyle);
reed@google.com95625db2013-03-25 20:44:02 +00002322 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002323
bungemanbea97482016-08-24 08:29:50 -07002324 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[],
2325 const SkFontStyle& style,
djsollen33068c12014-11-14 10:52:53 -08002326 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002327 SkUnichar character) const override {
bungemanbea97482016-08-24 08:29:50 -07002328 AutoCFRelease<CTFontDescriptorRef> desc(create_descriptor(familyName, style));
2329 AutoCFRelease<CTFontRef> currentFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
2330
2331 // kCFStringEncodingUTF32 is BE unless there is a BOM.
2332 // Since there is no machine endian option, explicitly state machine endian.
2333#ifdef SK_CPU_LENDIAN
2334 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32LE;
2335#else
2336 constexpr CFStringEncoding encoding = kCFStringEncodingUTF32BE;
2337#endif
2338 AutoCFRelease<CFStringRef> string(CFStringCreateWithBytes(
2339 kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(&character), sizeof(character),
2340 encoding, false));
2341 CFRange range = CFRangeMake(0, CFStringGetLength(string)); // in UniChar units.
2342 AutoCFRelease<CTFontRef> fallbackFont(CTFontCreateForString(currentFont, string, range));
2343 return create_from_CTFontRef(fallbackFont.release(), nullptr, false);
djsollen33068c12014-11-14 10:52:53 -08002344 }
2345
reed@google.com95625db2013-03-25 20:44:02 +00002346 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002347 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002348 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002349 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002350
mtklein36352bf2015-03-25 18:17:31 -07002351 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
bungemanf93d7112016-09-16 06:24:20 -07002352 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(sk_ref_sp(data)));
halcanary96fcdcc2015-08-27 07:41:13 -07002353 if (nullptr == pr) {
2354 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002355 }
2356 return create_from_dataProvider(pr);
2357 }
2358
bungemanf93d7112016-09-16 06:24:20 -07002359 SkTypeface* onCreateFromStream(SkStreamAsset* bareStream, int ttcIndex) const override {
2360 std::unique_ptr<SkStreamAsset> stream(bareStream);
2361 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(std::move(stream)));
halcanary96fcdcc2015-08-27 07:41:13 -07002362 if (nullptr == pr) {
2363 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002364 }
2365 return create_from_dataProvider(pr);
2366 }
2367
bungemanf6c71072016-01-21 14:17:47 -08002368 static CFNumberRef get_tag_for_name(CFStringRef name, CFArrayRef ctAxes) {
2369 CFIndex ctAxisCount = CFArrayGetCount(ctAxes);
2370 for (int i = 0; i < ctAxisCount; ++i) {
2371 CFTypeRef ctAxisInfo = CFArrayGetValueAtIndex(ctAxes, i);
2372 if (CFDictionaryGetTypeID() != CFGetTypeID(ctAxisInfo)) {
2373 return nullptr;
2374 }
2375 CFDictionaryRef ctAxisInfoDict = static_cast<CFDictionaryRef>(ctAxisInfo);
2376
2377 CFTypeRef ctAxisName = CFDictionaryGetValue(ctAxisInfoDict,
2378 kCTFontVariationAxisNameKey);
2379 if (!ctAxisName || CFGetTypeID(ctAxisName) != CFStringGetTypeID()) {
2380 return nullptr;
2381 }
2382
2383 if (CFEqual(name, ctAxisName)) {
2384 CFTypeRef tag = CFDictionaryGetValue(ctAxisInfoDict,
2385 kCTFontVariationAxisIdentifierKey);
2386 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2387 return nullptr;
2388 }
2389 return static_cast<CFNumberRef>(tag);
2390 }
2391 }
2392 return nullptr;
2393 }
2394 static CFDictionaryRef get_axes(CGFontRef cg, const FontParameters& params) {
2395 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
2396 if (!cgAxes) {
2397 return nullptr;
2398 }
2399 CFIndex axisCount = CFArrayGetCount(cgAxes);
2400
2401 // The CGFont variation data is keyed by name, and lacks the tag.
2402 // The CTFont variation data is keyed by tag, and also has the name.
2403 // We would like to work with CTFont variaitons, but creating a CTFont font with
2404 // CTFont variation dictionary runs into bugs. So use the CTFont variation data
2405 // to match names to tags to create the appropriate CGFont.
2406 AutoCFRelease<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
2407 AutoCFRelease<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
2408 if (!ctAxes || CFArrayGetCount(ctAxes) != axisCount) {
2409 return nullptr;
2410 }
2411
2412 int paramAxisCount;
2413 const FontParameters::Axis* paramAxes = params.getAxes(&paramAxisCount);
2414
2415 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2416 &kCFTypeDictionaryKeyCallBacks,
2417 &kCFTypeDictionaryValueCallBacks);
2418 for (int i = 0; i < axisCount; ++i) {
2419 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i);
2420 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2421 return nullptr;
2422 }
2423 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2424
2425 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2426 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2427 return nullptr;
2428 }
2429
2430 CFNumberRef tagNumber = get_tag_for_name(static_cast<CFStringRef>(axisName), ctAxes);
2431 if (!tagNumber) {
2432 // Could not find a tag to go with the name of this index.
2433 // This would be a bug in CG/CT.
2434 continue;
2435 }
2436 int64_t tagLong;
2437 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2438 return nullptr;
2439 }
2440
2441 // The variation axes can be set to any value, but cg will effectively pin them.
2442 // Pin them here to normalize.
2443 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2444 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2445 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisDefaultValue);
2446 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2447 !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
2448 !def || CFGetTypeID(def) != CFNumberGetTypeID())
2449 {
2450 return nullptr;
2451 }
2452 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2453 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2454 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2455 double minDouble;
2456 double maxDouble;
2457 double defDouble;
2458 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2459 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
2460 !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
2461 {
2462 return nullptr;
2463 }
2464
2465 double value = defDouble;
2466 for (int j = 0; j < paramAxisCount; ++j) {
2467 if (paramAxes[j].fTag == tagLong) {
2468 value = SkTPin(SkScalarToDouble(paramAxes[j].fStyleValue),minDouble,maxDouble);
2469 break;
2470 }
2471 }
2472 CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType,
2473 &value);
2474 CFDictionaryAddValue(dict, axisName, valueNumber);
2475 CFRelease(valueNumber);
2476 }
2477 return dict;
2478 }
bungemanf93d7112016-09-16 06:24:20 -07002479 SkTypeface* onCreateFromStream(SkStreamAsset* bs, const FontParameters& params) const override {
2480 std::unique_ptr<SkStreamAsset> s(bs);
2481 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(std::move(s)));
bungemanf6c71072016-01-21 14:17:47 -08002482 if (nullptr == provider) {
2483 return nullptr;
2484 }
2485 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
2486 if (nullptr == cg) {
2487 return nullptr;
2488 }
2489
2490 AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, params));
2491 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2492 // created from a data provider does not appear to have any ownership of the underlying
2493 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
2494 AutoCFRelease<CGFontRef> cgVariant;
2495 if (cgVariations) {
2496 cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations));
2497 } else {
mtklein18300a32016-03-16 13:53:35 -07002498 cgVariant.reset(cg.release());
bungemanf6c71072016-01-21 14:17:47 -08002499 }
2500
bungemanbea97482016-08-24 08:29:50 -07002501 AutoCFRelease<CTFontRef> ct(CTFontCreateWithGraphicsFont(cgVariant, 0, nullptr, nullptr));
bungemanf6c71072016-01-21 14:17:47 -08002502 if (!ct) {
2503 return nullptr;
2504 }
bungemanbea97482016-08-24 08:29:50 -07002505 return create_from_CTFontRef(ct.release(), cg.release(), true);
bungemanf6c71072016-01-21 14:17:47 -08002506 }
2507
bungeman41868fe2015-05-20 09:21:04 -07002508 static CFDictionaryRef get_axes(CGFontRef cg, SkFontData* fontData) {
2509 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
2510 if (!cgAxes) {
halcanary96fcdcc2015-08-27 07:41:13 -07002511 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002512 }
2513
2514 CFIndex axisCount = CFArrayGetCount(cgAxes);
2515 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002516 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002517 }
2518
2519 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2520 &kCFTypeDictionaryKeyCallBacks,
2521 &kCFTypeDictionaryValueCallBacks);
2522 for (int i = 0; i < fontData->getAxisCount(); ++i) {
2523 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i);
2524 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002525 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002526 }
2527 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2528
2529 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2530 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002531 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002532 }
2533
2534 // The variation axes can be set to any value, but cg will effectively pin them.
2535 // Pin them here to normalize.
2536 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2537 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2538 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2539 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2540 {
halcanary96fcdcc2015-08-27 07:41:13 -07002541 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002542 }
2543 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2544 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2545 double minDouble;
2546 double maxDouble;
2547 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2548 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2549 {
halcanary96fcdcc2015-08-27 07:41:13 -07002550 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002551 }
2552 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
2553 CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType,
2554 &value);
2555
2556 CFDictionaryAddValue(dict, axisName, valueNumber);
2557 CFRelease(valueNumber);
2558 }
2559 return dict;
2560 }
bungemanf93d7112016-09-16 06:24:20 -07002561 SkTypeface* onCreateFromFontData(std::unique_ptr<SkFontData> fontData) const override {
2562 AutoCFRelease<CGDataProviderRef> provider(
2563 SkCreateDataProviderFromStream(fontData->detachStream()));
halcanary96fcdcc2015-08-27 07:41:13 -07002564 if (nullptr == provider) {
2565 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002566 }
2567 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
halcanary96fcdcc2015-08-27 07:41:13 -07002568 if (nullptr == cg) {
2569 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002570 }
2571
bungemanf93d7112016-09-16 06:24:20 -07002572 AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, fontData.get()));
bungeman41868fe2015-05-20 09:21:04 -07002573 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2574 // created from a data provider does not appear to have any ownership of the underlying
2575 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
2576 AutoCFRelease<CGFontRef> cgVariant;
2577 if (cgVariations) {
2578 cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations));
2579 } else {
mtklein18300a32016-03-16 13:53:35 -07002580 cgVariant.reset(cg.release());
bungeman41868fe2015-05-20 09:21:04 -07002581 }
2582
bungemanbea97482016-08-24 08:29:50 -07002583 AutoCFRelease<CTFontRef> ct(CTFontCreateWithGraphicsFont(cgVariant, 0, nullptr, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07002584 if (!ct) {
halcanary96fcdcc2015-08-27 07:41:13 -07002585 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002586 }
bungemanbea97482016-08-24 08:29:50 -07002587 return create_from_CTFontRef(ct.release(), cg.release(), true);
bungeman41868fe2015-05-20 09:21:04 -07002588 }
2589
mtklein36352bf2015-03-25 18:17:31 -07002590 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002591 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
halcanary96fcdcc2015-08-27 07:41:13 -07002592 if (nullptr == pr) {
2593 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002594 }
2595 return create_from_dataProvider(pr);
2596 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002597
bungeman11a77c62016-04-12 13:45:06 -07002598 SkTypeface* onLegacyCreateTypeface(const char familyName[], SkFontStyle style) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002599 if (familyName) {
2600 familyName = map_css_names(familyName);
2601 }
2602
2603 if (!familyName || !*familyName) {
2604 familyName = FONT_DEFAULT_NAME;
2605 }
2606
bungemanbea97482016-08-24 08:29:50 -07002607 SkTypeface* face = create_from_name(familyName, style);
bungeman53d5c6e2016-04-08 07:22:29 -07002608 if (face) {
2609 return face;
bungemana4c4a2d2014-10-20 13:33:19 -07002610 }
bungeman53d5c6e2016-04-08 07:22:29 -07002611
2612 return SkSafeRef(GetDefaultFace());
reed@google.com7fdcd442013-07-30 21:25:49 +00002613 }
reed@google.com95625db2013-03-25 20:44:02 +00002614};
2615
reed@google.com7fdcd442013-07-30 21:25:49 +00002616///////////////////////////////////////////////////////////////////////////////
2617
halcanary385fe4d2015-08-26 13:07:48 -07002618SkFontMgr* SkFontMgr::Factory() { return new SkFontMgr_Mac; }
mtklein1ee76512015-11-02 10:20:27 -08002619
2620#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)