blob: f47add08186c23abc0607070c22ac7beb43af90a [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com0680d6c2008-12-19 19:46:15 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
mtkleinde9e7a72015-03-11 12:01:25 -07009#include "SkTypes.h" // Keep this before any #ifdef ...
mtklein1ee76512015-11-02 10:20:27 -080010#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
mtkleinde9e7a72015-03-11 12:01:25 -070011
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000012#ifdef SK_BUILD_FOR_MAC
13#import <ApplicationServices/ApplicationServices.h>
14#endif
reed@android.com0680d6c2008-12-19 19:46:15 +000015
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000016#ifdef SK_BUILD_FOR_IOS
17#include <CoreText/CoreText.h>
reed@google.com3dcbd462013-03-27 13:56:34 +000018#include <CoreText/CTFontManager.h>
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000019#include <CoreGraphics/CoreGraphics.h>
20#include <CoreFoundation/CoreFoundation.h>
21#endif
22
reed39a9a502015-05-12 09:50:04 -070023#include "SkAdvancedTypefaceMetrics.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000024#include "SkCGUtils.h"
25#include "SkColorPriv.h"
26#include "SkDescriptor.h"
27#include "SkEndian.h"
jvanverth02802f62015-07-02 06:42:49 -070028#include "SkFloatingPoint.h"
mtklein1b249332015-07-07 12:21:21 -070029#include "SkFontDescriptor.h"
30#include "SkFontMgr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000031#include "SkGlyph.h"
32#include "SkMaskGamma.h"
mtklein1b249332015-07-07 12:21:21 -070033#include "SkMutex.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000034#include "SkOTTable_glyf.h"
35#include "SkOTTable_head.h"
36#include "SkOTTable_hhea.h"
37#include "SkOTTable_loca.h"
38#include "SkOTUtils.h"
mtkleinbbd40182015-09-08 08:19:33 -070039#include "SkOncePtr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000040#include "SkPaint.h"
41#include "SkPath.h"
mtklein1b249332015-07-07 12:21:21 -070042#include "SkSFNTHeader.h"
jvanverth02802f62015-07-02 06:42:49 -070043#include "SkStream.h"
mtklein1b249332015-07-07 12:21:21 -070044#include "SkString.h"
bungemane280d062016-03-24 11:27:05 -070045#include "SkTemplates.h"
mtklein1b249332015-07-07 12:21:21 -070046#include "SkTypefaceCache.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000047#include "SkTypeface_mac.h"
48#include "SkUtils.h"
reed@google.combcb42ae2013-07-02 13:56:39 +000049#include "SkUtils.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000050
bungeman34902632014-12-10 21:43:27 -080051#include <dlfcn.h>
reed@google.comf77b35d2013-05-02 20:39:44 +000052
reedd0f41732015-07-10 12:08:38 -070053// Experimental code to use a global lock whenever we access CG, to see if this reduces
54// crashes in Chrome
55#define USE_GLOBAL_MUTEX_FOR_CG_ACCESS
56
57#ifdef USE_GLOBAL_MUTEX_FOR_CG_ACCESS
mtklein4598fc32015-07-13 06:15:36 -070058 SK_DECLARE_STATIC_MUTEX(gCGMutex);
reedd0f41732015-07-10 12:08:38 -070059 #define AUTO_CG_LOCK() SkAutoMutexAcquire amc(gCGMutex)
60#else
61 #define AUTO_CG_LOCK()
62#endif
63
bungeman3b4b66c2015-01-08 08:33:44 -080064// Set to make glyph bounding boxes visible.
65#define SK_SHOW_TEXT_BLIT_COVERAGE 0
66
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000067class SkScalerContext_Mac;
68
reed@google.com3dcbd462013-03-27 13:56:34 +000069// CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
70// provide a wrapper here that will return an empty array if need be.
71static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() {
72#ifdef SK_BUILD_FOR_IOS
halcanary96fcdcc2015-08-27 07:41:13 -070073 return CFArrayCreate(nullptr, nullptr, 0, nullptr);
reed@google.com3dcbd462013-03-27 13:56:34 +000074#else
75 return CTFontManagerCopyAvailableFontFamilyNames();
76#endif
77}
78
79
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000080// Being templated and taking const T* prevents calling
81// CFSafeRelease(autoCFRelease) through implicit conversion.
82template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
83 if (cfTypeRef) {
84 CFRelease(cfTypeRef);
85 }
86}
87
88// Being templated and taking const T* prevents calling
89// CFSafeRetain(autoCFRelease) through implicit conversion.
90template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
91 if (cfTypeRef) {
92 CFRetain(cfTypeRef);
93 }
94}
95
96/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
97template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
98public:
halcanary96fcdcc2015-08-27 07:41:13 -070099 explicit AutoCFRelease(CFRef cfRef = nullptr) : fCFRef(cfRef) { }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000100 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
101
halcanary96fcdcc2015-08-27 07:41:13 -0700102 void reset(CFRef that = nullptr) {
ljagielskie9d2d092014-07-15 20:02:04 -0700103 if (that != fCFRef) {
104 CFSafeRelease(fCFRef);
105 fCFRef = that;
106 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000107 }
108
mtklein18300a32016-03-16 13:53:35 -0700109 CFRef release() {
bungeman64fb51d2015-05-04 12:03:50 -0700110 CFRef self = fCFRef;
halcanary96fcdcc2015-08-27 07:41:13 -0700111 fCFRef = nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700112 return self;
113 }
114
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000115 operator CFRef() const { return fCFRef; }
116 CFRef get() const { return fCFRef; }
117
halcanary96fcdcc2015-08-27 07:41:13 -0700118 CFRef* operator&() { SkASSERT(fCFRef == nullptr); return &fCFRef; }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000119private:
120 CFRef fCFRef;
121};
122
reed@google.com964988f2013-03-29 14:57:22 +0000123static CFStringRef make_CFString(const char str[]) {
halcanary96fcdcc2015-08-27 07:41:13 -0700124 return CFStringCreateWithCString(nullptr, str, kCFStringEncodingUTF8);
reed@google.com964988f2013-03-29 14:57:22 +0000125}
126
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000127template<typename T> class AutoCGTable : SkNoncopyable {
128public:
129 AutoCGTable(CGFontRef font)
130 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
131 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
halcanary96fcdcc2015-08-27 07:41:13 -0700132 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : nullptr)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000133 { }
134
135 const T* operator->() const { return fData; }
136
137private:
138 AutoCFRelease<CFDataRef> fCFData;
139public:
140 const T* fData;
141};
142
143// inline versions of these rect helpers
144
145static bool CGRectIsEmpty_inline(const CGRect& rect) {
146 return rect.size.width <= 0 || rect.size.height <= 0;
147}
148
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000149static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
150 return rect.origin.x;
151}
152
153static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
154 return rect.origin.x + rect.size.width;
155}
156
157static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
158 return rect.origin.y;
159}
160
161static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
162 return rect.origin.y + rect.size.height;
163}
164
165static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
166 return rect.size.width;
167}
168
169///////////////////////////////////////////////////////////////////////////////
170
171static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
reed@google.com7fa2a652014-01-27 13:42:58 +0000172 int width, int height, size_t rowBytes) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000173 SkASSERT(width);
174 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
175
176 if (width >= 32) {
177 while (height) {
178 sk_memset32(ptr, value, width);
179 ptr = (uint32_t*)((char*)ptr + rowBytes);
180 height -= 1;
181 }
182 return;
183 }
184
185 rowBytes -= width * sizeof(uint32_t);
186
187 if (width >= 8) {
188 while (height) {
189 int w = width;
190 do {
191 *ptr++ = value; *ptr++ = value;
192 *ptr++ = value; *ptr++ = value;
193 *ptr++ = value; *ptr++ = value;
194 *ptr++ = value; *ptr++ = value;
195 w -= 8;
196 } while (w >= 8);
197 while (--w >= 0) {
198 *ptr++ = value;
199 }
200 ptr = (uint32_t*)((char*)ptr + rowBytes);
201 height -= 1;
202 }
203 } else {
204 while (height) {
205 int w = width;
206 do {
207 *ptr++ = value;
208 } while (--w > 0);
209 ptr = (uint32_t*)((char*)ptr + rowBytes);
210 height -= 1;
211 }
212 }
213}
214
215#include <sys/utsname.h>
216
217typedef uint32_t CGRGBPixel;
218
219static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
220 return pixel & 0xFF;
221}
222
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000223static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
224
225// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
226static int readVersion() {
227 struct utsname info;
228 if (uname(&info) != 0) {
229 SkDebugf("uname failed\n");
230 return 0;
231 }
232 if (strcmp(info.sysname, "Darwin") != 0) {
233 SkDebugf("unexpected uname sysname %s\n", info.sysname);
234 return 0;
235 }
236 char* dot = strchr(info.release, '.');
237 if (!dot) {
238 SkDebugf("expected dot in uname release %s\n", info.release);
239 return 0;
240 }
241 int version = atoi(info.release);
242 if (version == 0) {
243 SkDebugf("could not parse uname release %s\n", info.release);
244 }
245 return version;
246}
247
248static int darwinVersion() {
249 static int darwin_version = readVersion();
250 return darwin_version;
251}
252
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000253static bool isSnowLeopard() {
254 return darwinVersion() == 10;
255}
256
257static bool isLion() {
258 return darwinVersion() == 11;
259}
260
261static bool isMountainLion() {
262 return darwinVersion() == 12;
263}
264
265static bool isLCDFormat(unsigned format) {
reedd54d3fc2014-11-13 14:39:58 -0800266 return SkMask::kLCD16_Format == format;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000267}
268
269static CGFloat ScalarToCG(SkScalar scalar) {
270 if (sizeof(CGFloat) == sizeof(float)) {
271 return SkScalarToFloat(scalar);
272 } else {
273 SkASSERT(sizeof(CGFloat) == sizeof(double));
274 return (CGFloat) SkScalarToDouble(scalar);
275 }
276}
277
278static SkScalar CGToScalar(CGFloat cgFloat) {
279 if (sizeof(CGFloat) == sizeof(float)) {
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000280 return cgFloat;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000281 } else {
282 SkASSERT(sizeof(CGFloat) == sizeof(double));
283 return SkDoubleToScalar(cgFloat);
284 }
285}
286
bungeman4de8c3a2015-09-18 23:03:24 -0700287static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
288 SkScalar sx = SK_Scalar1,
289 SkScalar sy = SK_Scalar1) {
290 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
291 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
292 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
293 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
294 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
295 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000296}
297
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000298///////////////////////////////////////////////////////////////////////////////
299
300#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000301
302/**
303 * There does not appear to be a publicly accessable API for determining if lcd
304 * font smoothing will be applied if we request it. The main issue is that if
305 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
306 */
307static bool supports_LCD() {
308 static int gSupportsLCD = -1;
309 if (gSupportsLCD >= 0) {
310 return (bool) gSupportsLCD;
311 }
312 uint32_t rgb = 0;
313 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
314 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
315 colorspace, BITMAP_INFO_RGB));
316 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
317 CGContextSetShouldSmoothFonts(cgContext, true);
318 CGContextSetShouldAntialias(cgContext, true);
319 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
320 CGContextSetGrayFillColor(cgContext, 1, 1);
321 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
322 uint32_t r = (rgb >> 16) & 0xFF;
323 uint32_t g = (rgb >> 8) & 0xFF;
324 uint32_t b = (rgb >> 0) & 0xFF;
325 gSupportsLCD = (r != g || r != b);
326 return (bool) gSupportsLCD;
327}
328
329class Offscreen {
330public:
bungeman34902632014-12-10 21:43:27 -0800331 Offscreen()
halcanary96fcdcc2015-08-27 07:41:13 -0700332 : fRGBSpace(nullptr)
333 , fCG(nullptr)
bungeman34902632014-12-10 21:43:27 -0800334 , fDoAA(false)
335 , fDoLCD(false)
336 {
337 fSize.set(0, 0);
338 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000339
340 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
341 CGGlyph glyphID, size_t* rowBytesPtr,
342 bool generateA8FromLCD);
343
344private:
345 enum {
346 kSize = 32 * 32 * sizeof(CGRGBPixel)
347 };
348 SkAutoSMalloc<kSize> fImageStorage;
349 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
350
351 // cached state
352 AutoCFRelease<CGContextRef> fCG;
353 SkISize fSize;
354 bool fDoAA;
355 bool fDoLCD;
356
357 static int RoundSize(int dimension) {
358 return SkNextPow2(dimension);
359 }
360};
361
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000362///////////////////////////////////////////////////////////////////////////////
363
bungemana4c4a2d2014-10-20 13:33:19 -0700364static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
365 CFNumberRef num;
366 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
367 && CFNumberIsFloatType(num)
368 && CFNumberGetValue(num, kCFNumberFloatType, value);
369}
370
371static int unit_weight_to_fontstyle(float unit) {
372 float value;
373 if (unit < 0) {
374 value = 100 + (1 + unit) * 300;
375 } else {
376 value = 400 + unit * 500;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000377 }
bungemana4c4a2d2014-10-20 13:33:19 -0700378 return sk_float_round2int(value);
379}
380
381static int unit_width_to_fontstyle(float unit) {
382 float value;
383 if (unit < 0) {
384 value = 1 + (1 + unit) * 4;
385 } else {
386 value = 5 + unit * 4;
387 }
388 return sk_float_round2int(value);
389}
390
bungeman336fdf22014-11-10 07:48:55 -0800391static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc) {
bungemana4c4a2d2014-10-20 13:33:19 -0700392 AutoCFRelease<CFDictionaryRef> dict(
393 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
halcanary96fcdcc2015-08-27 07:41:13 -0700394 if (nullptr == dict.get()) {
bungemana4c4a2d2014-10-20 13:33:19 -0700395 return SkFontStyle();
396 }
397
bungemana4c4a2d2014-10-20 13:33:19 -0700398 float weight, width, slant;
399 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800400 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700401 }
402 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
403 width = 0;
404 }
405 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800406 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700407 }
408
409 return SkFontStyle(unit_weight_to_fontstyle(weight),
410 unit_width_to_fontstyle(width),
411 slant ? SkFontStyle::kItalic_Slant
412 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000413}
414
bungeman336fdf22014-11-10 07:48:55 -0800415static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) {
416 unsigned style = SkTypeface::kNormal;
417 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
418
419 if (traits & kCTFontBoldTrait) {
420 style |= SkTypeface::kBold;
421 }
422 if (traits & kCTFontItalicTrait) {
423 style |= SkTypeface::kItalic;
424 }
425 if (isFixedPitch) {
426 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0;
427 }
428 return (SkTypeface::Style)style;
429}
430
reed@google.comce8b3de2013-03-26 19:30:16 +0000431#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
432
bungeman994e8182015-02-23 16:17:43 -0800433// kCTFontColorGlyphsTrait was added in the Mac 10.7 and iPhone 4.3 SDKs.
434// Being an enum value it is not guarded by version macros, but old SDKs must still be supported.
435#if defined(__MAC_10_7) || defined(__IPHONE_4_3)
436static const uint32_t SkCTFontColorGlyphsTrait = kCTFontColorGlyphsTrait;
437#else
438static const uint32_t SkCTFontColorGlyphsTrait = (1 << 13);
439#endif
440
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000441class SkTypeface_Mac : public SkTypeface {
442public:
bungeman78884012015-06-08 13:39:12 -0700443 SkTypeface_Mac(CTFontRef fontRef, CFTypeRef resourceRef,
444 const SkFontStyle& fs, bool isFixedPitch,
445 const char requestedName[], bool isLocalStream)
bungeman64fb51d2015-05-04 12:03:50 -0700446 : SkTypeface(fs, SkTypefaceCache::NewFontID(), isFixedPitch)
bungeman967937c2014-10-30 11:49:27 -0700447 , fRequestedName(requestedName)
mtklein802ad832014-10-20 12:54:31 -0700448 , fFontRef(fontRef) // caller has already called CFRetain for us
bungeman78884012015-06-08 13:39:12 -0700449 , fOriginatingCFTypeRef(resourceRef) // caller has already called CFRetain for us
bungemane3bea5c2015-04-07 07:34:36 -0700450 , fHasColorGlyphs(SkToBool(CTFontGetSymbolicTraits(fFontRef) & SkCTFontColorGlyphsTrait))
caseq26337e92014-06-30 12:14:52 -0700451 , fIsLocalStream(isLocalStream)
reed@google.comce8b3de2013-03-26 19:30:16 +0000452 {
453 SkASSERT(fontRef);
454 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000455
bungeman967937c2014-10-30 11:49:27 -0700456 SkString fRequestedName;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000457 AutoCFRelease<CTFontRef> fFontRef;
bungeman78884012015-06-08 13:39:12 -0700458 AutoCFRelease<CFTypeRef> fOriginatingCFTypeRef;
bungemane3bea5c2015-04-07 07:34:36 -0700459 const bool fHasColorGlyphs;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000460
461protected:
mtklein36352bf2015-03-25 18:17:31 -0700462 int onGetUPEM() const override;
463 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
bungeman41868fe2015-05-20 09:21:04 -0700464 SkFontData* onCreateFontData() const override;
mtklein36352bf2015-03-25 18:17:31 -0700465 void onGetFamilyName(SkString* familyName) const override;
466 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
467 int onGetTableTags(SkFontTableTag tags[]) const override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000468 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
mtklein36352bf2015-03-25 18:17:31 -0700469 size_t length, void* data) const override;
470 SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override;
471 void onFilterRec(SkScalerContextRec*) const override;
472 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
reed@google.com2689f612013-03-20 20:01:47 +0000473 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -0700474 PerGlyphInfo,
mtklein36352bf2015-03-25 18:17:31 -0700475 const uint32_t*, uint32_t) const override;
reed@google.combcb42ae2013-07-02 13:56:39 +0000476 virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
mtklein36352bf2015-03-25 18:17:31 -0700477 int glyphCount) const override;
478 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000479
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000480private:
caseq26337e92014-06-30 12:14:52 -0700481 bool fIsLocalStream;
reed@google.comce8b3de2013-03-26 19:30:16 +0000482
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000483 typedef SkTypeface INHERITED;
484};
485
bungeman64fb51d2015-05-04 12:03:50 -0700486/** Creates a typeface without searching the cache. Takes ownership of the CTFontRef. */
bungeman78884012015-06-08 13:39:12 -0700487static SkTypeface* NewFromFontRef(CTFontRef fontRef, CFTypeRef resourceRef,
488 const char name[], bool isLocalStream)
bungeman41868fe2015-05-20 09:21:04 -0700489{
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000490 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000491 bool isFixedPitch;
bungeman336fdf22014-11-10 07:48:55 -0800492 SkFontStyle style = SkFontStyle(computeStyleBits(fontRef, &isFixedPitch));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000493
bungeman78884012015-06-08 13:39:12 -0700494 return new SkTypeface_Mac(fontRef, resourceRef, style, isFixedPitch, name, isLocalStream);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000495}
496
bungeman64fb51d2015-05-04 12:03:50 -0700497static bool find_by_CTFontRef(SkTypeface* cached, const SkFontStyle&, void* context) {
498 CTFontRef self = (CTFontRef)context;
499 CTFontRef other = ((SkTypeface_Mac*)cached)->fFontRef;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000500
bungeman64fb51d2015-05-04 12:03:50 -0700501 return CFEqual(self, other);
502}
503
504/** Creates a typeface from a name, searching the cache. */
505static SkTypeface* NewFromName(const char familyName[], const SkFontStyle& theStyle) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000506 CTFontSymbolicTraits ctFontTraits = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700507 if (theStyle.weight() >= SkFontStyle::kBold_Weight) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000508 ctFontTraits |= kCTFontBoldTrait;
509 }
bungemana4c4a2d2014-10-20 13:33:19 -0700510 if (theStyle.slant() != SkFontStyle::kUpright_Slant) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000511 ctFontTraits |= kCTFontItalicTrait;
512 }
513
bungemana4c4a2d2014-10-20 13:33:19 -0700514 //TODO: add weight width slant
515
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000516 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000517 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000518
519 AutoCFRelease<CFNumberRef> cfFontTraits(
520 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
521
522 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
523 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
524 &kCFTypeDictionaryKeyCallBacks,
525 &kCFTypeDictionaryValueCallBacks));
526
527 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
528 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
529 &kCFTypeDictionaryKeyCallBacks,
530 &kCFTypeDictionaryValueCallBacks));
531
bungeman64fb51d2015-05-04 12:03:50 -0700532 if (!cfFontName || !cfFontTraits || !cfAttributes || !cfTraits) {
halcanary96fcdcc2015-08-27 07:41:13 -0700533 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000534 }
535
bungeman64fb51d2015-05-04 12:03:50 -0700536 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
537
538 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
539 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
540
541 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
542 CTFontDescriptorCreateWithAttributes(cfAttributes));
543 if (!ctFontDesc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700544 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700545 }
546
halcanary96fcdcc2015-08-27 07:41:13 -0700547 AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(ctFontDesc, 0, nullptr));
bungeman64fb51d2015-05-04 12:03:50 -0700548 if (!ctFont) {
halcanary96fcdcc2015-08-27 07:41:13 -0700549 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700550 }
551
552 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)ctFont.get());
553 if (!face) {
mtklein18300a32016-03-16 13:53:35 -0700554 face = NewFromFontRef(ctFont.release(), nullptr, nullptr, false);
bungeman64fb51d2015-05-04 12:03:50 -0700555 SkTypefaceCache::Add(face, face->fontStyle());
556 }
557 return face;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000558}
559
bungemand6aeb6d2014-07-25 11:52:47 -0700560SK_DECLARE_STATIC_MUTEX(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000561static SkTypeface* GetDefaultFace() {
bungemand6aeb6d2014-07-25 11:52:47 -0700562 SkAutoMutexAcquire ma(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000563
564 static SkTypeface* gDefaultFace;
565
halcanary96fcdcc2015-08-27 07:41:13 -0700566 if (nullptr == gDefaultFace) {
bungemana4c4a2d2014-10-20 13:33:19 -0700567 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkFontStyle());
568 SkTypefaceCache::Add(gDefaultFace, SkFontStyle());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000569 }
570 return gDefaultFace;
571}
572
573///////////////////////////////////////////////////////////////////////////////
574
575extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
576CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
577 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
halcanary96fcdcc2015-08-27 07:41:13 -0700578 return macface ? macface->fFontRef.get() : nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000579}
580
581/* This function is visible on the outside. It first searches the cache, and if
582 * not found, returns a new entry (after adding it to the cache).
583 */
bungeman78884012015-06-08 13:39:12 -0700584SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef, CFTypeRef resourceRef) {
bungeman64fb51d2015-05-04 12:03:50 -0700585 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)fontRef);
586 if (!face) {
587 CFRetain(fontRef);
bungeman78884012015-06-08 13:39:12 -0700588 if (resourceRef) {
589 CFRetain(resourceRef);
590 }
halcanary96fcdcc2015-08-27 07:41:13 -0700591 face = NewFromFontRef(fontRef, resourceRef, nullptr, false);
bungemana4c4a2d2014-10-20 13:33:19 -0700592 SkTypefaceCache::Add(face, face->fontStyle());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000593 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000594 return face;
595}
596
bungeman967937c2014-10-30 11:49:27 -0700597struct NameStyle {
598 const char* fName;
599 SkFontStyle fStyle;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000600};
601
bungeman967937c2014-10-30 11:49:27 -0700602static bool find_by_NameStyle(SkTypeface* cachedFace, const SkFontStyle& cachedStyle, void* ctx) {
603 const SkTypeface_Mac* cachedMacFace = static_cast<SkTypeface_Mac*>(cachedFace);
604 const NameStyle* requested = static_cast<const NameStyle*>(ctx);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000605
bungeman967937c2014-10-30 11:49:27 -0700606 return cachedStyle == requested->fStyle
607 && cachedMacFace->fRequestedName.equals(requested->fName);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000608}
609
610static const char* map_css_names(const char* name) {
611 static const struct {
612 const char* fFrom; // name the caller specified
613 const char* fTo; // "canonical" name we map to
614 } gPairs[] = {
615 { "sans-serif", "Helvetica" },
616 { "serif", "Times" },
617 { "monospace", "Courier" }
618 };
619
620 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
621 if (strcmp(name, gPairs[i].fFrom) == 0) {
622 return gPairs[i].fTo;
623 }
624 }
625 return name; // no change
626}
627
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000628///////////////////////////////////////////////////////////////////////////////
629
bungeman@google.comcefd9812013-05-15 15:07:32 +0000630/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000631struct GlyphRect {
632 int16_t fMinX;
633 int16_t fMinY;
634 int16_t fMaxX;
635 int16_t fMaxY;
636};
637
638class SkScalerContext_Mac : public SkScalerContext {
639public:
reed@google.com0da48612013-03-19 16:06:52 +0000640 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000641
642protected:
mtklein36352bf2015-03-25 18:17:31 -0700643 unsigned generateGlyphCount(void) override;
644 uint16_t generateCharToGlyph(SkUnichar uni) override;
645 void generateAdvance(SkGlyph* glyph) override;
646 void generateMetrics(SkGlyph* glyph) override;
647 void generateImage(const SkGlyph& glyph) override;
648 void generatePath(const SkGlyph& glyph, SkPath* path) override;
649 void generateFontMetrics(SkPaint::FontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000650
651private:
652 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000653
bungeman@google.comcefd9812013-05-15 15:07:32 +0000654 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
655 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000656
657 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
658 *
659 * For use with (and must be called before) generateBBoxes.
660 */
661 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000662
bungeman@google.comcefd9812013-05-15 15:07:32 +0000663 /** Initializes fFBoundingBoxes and returns true on success.
664 *
665 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
666 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
667 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
668 * font directly.
669 *
670 * This routine initializes fFBoundingBoxes to an array of
671 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
672 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
673 *
674 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
675 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
676 *
677 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
678 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000679 bool generateBBoxes();
680
bungeman@google.comcefd9812013-05-15 15:07:32 +0000681 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
682 *
683 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
684 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
685 */
686 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000687
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000688 Offscreen fOffscreen;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000689
bungeman3b4b66c2015-01-08 08:33:44 -0800690 /** Unrotated variant of fCTFont.
691 *
692 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
693 * advances, but always sets the height to 0. This font is used to get the advances of the
694 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000695 *
696 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
697 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
698 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
bungeman3b4b66c2015-01-08 08:33:44 -0800699 * With kCTFontVerticalOrientation the advances must be unrotated.
bungemanef27ce32015-10-29 09:30:32 -0700700 *
701 * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
702 * different underlying font data. As a result, avoid ever creating more than one CTFont per
703 * SkScalerContext to ensure that only one CTFont is used.
704 *
705 * As a result of the above (and other constraints) this font contains the size, but not the
706 * transform. The transform must always be applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000707 */
bungemanef27ce32015-10-29 09:30:32 -0700708 AutoCFRelease<CTFontRef> fCTFont;
709
710 /** The transform without the font size. */
711 CGAffineTransform fTransform;
712 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000713
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000714 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000715 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000716 uint16_t fFBoundingBoxesGlyphOffset;
717 uint16_t fGlyphCount;
718 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000719 const bool fDoSubPosition;
720 const bool fVertical;
721
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000722 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000723
724 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000725};
726
bungeman7cbeaae2015-09-22 09:54:56 -0700727// CTFontCreateCopyWithAttributes or CTFontCreateCopyWithSymbolicTraits cannot be used on 10.10
bungeman05846312015-09-23 12:51:28 -0700728// and later, as they will return different underlying fonts depending on the size requested.
729// It is not possible to use descriptors with CTFontCreateWithFontDescriptor, since that does not
730// work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
731// CGFont.
bungeman7cbeaae2015-09-22 09:54:56 -0700732static CTFontRef ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
bungeman05846312015-09-23 12:51:28 -0700733 const CGAffineTransform* transform)
bungeman7cbeaae2015-09-22 09:54:56 -0700734{
bungeman05846312015-09-23 12:51:28 -0700735 AutoCFRelease<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700736
bungeman05846312015-09-23 12:51:28 -0700737 // The last parameter (CTFontDescriptorRef attributes) *must* be nullptr.
738 // If non-nullptr then with fonts with variation axes, the copy will fail in
739 // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
740 // which it quite obviously is not.
bungeman7cbeaae2015-09-22 09:54:56 -0700741
bungeman05846312015-09-23 12:51:28 -0700742 // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
743 // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
744 // the scaler context, since they aren't 'normal'.
745 return CTFontCreateWithGraphicsFont(baseCGFont, textSize, transform, nullptr);
bungeman7cbeaae2015-09-22 09:54:56 -0700746}
747
reed@google.com0da48612013-03-19 16:06:52 +0000748SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
749 const SkDescriptor* desc)
750 : INHERITED(typeface, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000751 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000752 , fFBoundingBoxesGlyphOffset(0)
753 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000754 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
755 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
756
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000757{
reedd0f41732015-07-10 12:08:38 -0700758 AUTO_CG_LOCK();
759
reed@google.com2689f612013-03-20 20:01:47 +0000760 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000761 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000762 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
763 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000764
bungeman34902632014-12-10 21:43:27 -0800765 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
766 // As a result, it is necessary to know the actual device size and request that.
767 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800768 SkMatrix skTransform;
bungeman34902632014-12-10 21:43:27 -0800769 fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, &skTransform,
halcanary96fcdcc2015-08-27 07:41:13 -0700770 nullptr, nullptr, &fFUnitMatrix);
bungemanaae30912015-03-02 13:43:26 -0800771 fTransform = MatrixToCGAffineTransform(skTransform);
772 fInvTransform = CGAffineTransformInvert(fTransform);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000773
bungemanbe2284d2014-11-25 08:08:09 -0800774 // The transform contains everything except the requested text size.
775 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -0800776 CGFloat textSize = ScalarToCG(scale.y());
bungemanef27ce32015-10-29 09:30:32 -0700777 fCTFont.reset(ctfont_create_exact_copy(ctFont, textSize, nullptr));
halcanary96fcdcc2015-08-27 07:41:13 -0700778 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont, nullptr));
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000779
bungemanbe2284d2014-11-25 08:08:09 -0800780 // 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 +0000781 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
782 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000783}
784
mtkleinbbd40182015-09-08 08:19:33 -0700785/** This is an implementation of CTFontDrawGlyphs for 10.6; it was introduced in 10.7. */
786static void legacy_CTFontDrawGlyphs(CTFontRef, const CGGlyph glyphs[], const CGPoint points[],
787 size_t count, CGContextRef cg) {
bungeman34902632014-12-10 21:43:27 -0800788 CGContextShowGlyphsAtPositions(cg, glyphs, points, count);
789}
790
mtkleinbbd40182015-09-08 08:19:33 -0700791typedef decltype(legacy_CTFontDrawGlyphs) CTFontDrawGlyphsProc;
792
793static CTFontDrawGlyphsProc* choose_CTFontDrawGlyphs() {
794 if (void* real = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs")) {
795 return (CTFontDrawGlyphsProc*)real;
796 }
797 return &legacy_CTFontDrawGlyphs;
bungeman34902632014-12-10 21:43:27 -0800798}
799
mtkleinbbd40182015-09-08 08:19:33 -0700800SK_DECLARE_STATIC_ONCE_PTR(CTFontDrawGlyphsProc, gCTFontDrawGlyphs);
bungeman34902632014-12-10 21:43:27 -0800801
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000802CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
803 CGGlyph glyphID, size_t* rowBytesPtr,
mtkleinbbd40182015-09-08 08:19:33 -0700804 bool generateA8FromLCD) {
805 auto ctFontDrawGlyphs = gCTFontDrawGlyphs.get(choose_CTFontDrawGlyphs);
bungeman34902632014-12-10 21:43:27 -0800806
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000807 if (!fRGBSpace) {
808 //It doesn't appear to matter what color space is specified.
809 //Regular blends and antialiased text are always (s*a + d*(1-a))
810 //and smoothed text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -0700811 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000812 }
813
814 // default to kBW_Format
815 bool doAA = false;
816 bool doLCD = false;
817
818 if (SkMask::kBW_Format != glyph.fMaskFormat) {
819 doLCD = true;
820 doAA = true;
821 }
822
823 // FIXME: lcd smoothed un-hinted rasterization unsupported.
824 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
825 doLCD = false;
826 doAA = true;
827 }
828
bungeman34902632014-12-10 21:43:27 -0800829 // If this font might have color glyphs, disable LCD as there's no way to support it.
830 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
bungemane280d062016-03-24 11:27:05 -0700831 // A8 will end up black on transparent, but TODO: we can detect gray and set to A8.
bungeman34902632014-12-10 21:43:27 -0800832 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
833 doLCD = false;
834 }
835
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000836 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
837 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
838 if (fSize.fWidth < glyph.fWidth) {
839 fSize.fWidth = RoundSize(glyph.fWidth);
840 }
841 if (fSize.fHeight < glyph.fHeight) {
842 fSize.fHeight = RoundSize(glyph.fHeight);
843 }
844
845 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
846 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -0800847 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
848 ? kCGImageAlphaPremultipliedFirst
849 : kCGImageAlphaNoneSkipFirst;
850 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -0700851 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungeman34902632014-12-10 21:43:27 -0800852 rowBytes, fRGBSpace, bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000853
bungeman34902632014-12-10 21:43:27 -0800854 // Skia handles quantization and subpixel positioning,
855 // so disable quantization and enabe subpixel positioning in CG.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000856 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
857 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
858
bungeman@google.comcefd9812013-05-15 15:07:32 +0000859 // Because CG always draws from the horizontal baseline,
860 // if there is a non-integral translation from the horizontal origin to the vertical origin,
861 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungeman34902632014-12-10 21:43:27 -0800862 CGContextSetAllowsFontSubpixelPositioning(fCG, true);
863 CGContextSetShouldSubpixelPositionFonts(fCG, true);
864
865 CGContextSetTextDrawingMode(fCG, kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000866
bungemane280d062016-03-24 11:27:05 -0700867#if SK_IGNORE_MAC_FONT_WEIGHT_FIX
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000868 // Draw white on black to create mask.
869 // TODO: Draw black on white and invert, CG has a special case codepath.
870 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
bungemane280d062016-03-24 11:27:05 -0700871#else
872 // Draw black on white to create mask. (Special path exists to speed this up in CG.)
873 CGContextSetGrayFillColor(fCG, 0.0f, 1.0f);
874#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000875
876 // force our checks below to happen
877 fDoAA = !doAA;
878 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -0800879
mtkleinbbd40182015-09-08 08:19:33 -0700880 if (legacy_CTFontDrawGlyphs == ctFontDrawGlyphs) {
bungeman34902632014-12-10 21:43:27 -0800881 // CTFontDrawGlyphs will apply the font, font size, and font matrix to the CGContext.
882 // Our 'fake' one does not, so set up the CGContext here.
883 CGContextSetFont(fCG, context.fCGFont);
884 CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont));
bungeman34902632014-12-10 21:43:27 -0800885 }
bungemanaae30912015-03-02 13:43:26 -0800886 CGContextSetTextMatrix(fCG, context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000887 }
888
889 if (fDoAA != doAA) {
890 CGContextSetShouldAntialias(fCG, doAA);
891 fDoAA = doAA;
892 }
893 if (fDoLCD != doLCD) {
894 CGContextSetShouldSmoothFonts(fCG, doLCD);
895 fDoLCD = doLCD;
896 }
897
898 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
899 // skip rows based on the glyph's height
900 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
901
bungemane280d062016-03-24 11:27:05 -0700902#if SK_IGNORE_MAC_FONT_WEIGHT_FIX
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000903 // erase to black
904 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
bungemane280d062016-03-24 11:27:05 -0700905#else
906 // Erase to white (or transparent black if it's a color glyph, to not composite against white).
907 uint32_t bgColor = (SkMask::kARGB32_Format != glyph.fMaskFormat) ? 0xFFFFFFFF : 0x00000000;
908 sk_memset_rect32(image, bgColor, glyph.fWidth, glyph.fHeight, rowBytes);
909#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000910
911 float subX = 0;
912 float subY = 0;
913 if (context.fDoSubPosition) {
914 subX = SkFixedToFloat(glyph.getSubXFixed());
915 subY = SkFixedToFloat(glyph.getSubYFixed());
916 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000917
bungeman34902632014-12-10 21:43:27 -0800918 // CoreText and CoreGraphics always draw using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000919 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000920 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000921 context.getVerticalOffset(glyphID, &offset);
922 subX += offset.fX;
923 subY += offset.fY;
924 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000925
bungeman34902632014-12-10 21:43:27 -0800926 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -0800927 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
928 // 'positions' which are in text space. The glyph location (in device space) must be
929 // mapped into text space, so that CG can convert it back into device space.
930 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
bungeman1b5f25c2015-06-08 14:32:24 -0700931 //
bungemanaae30912015-03-02 13:43:26 -0800932 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
933 // So always make the font transform identity and place the transform on the context.
934 point = CGPointApplyAffineTransform(point, context.fInvTransform);
935
bungemanef27ce32015-10-29 09:30:32 -0700936 ctFontDrawGlyphs(context.fCTFont, &glyphID, &point, 1, fCG);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000937
938 SkASSERT(rowBytesPtr);
939 *rowBytesPtr = rowBytes;
940 return image;
941}
942
bungeman@google.comcefd9812013-05-15 15:07:32 +0000943void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
944 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
945 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
946 CGSize cgVertOffset;
947 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000948 if (isSnowLeopard()) {
bungemanef27ce32015-10-29 09:30:32 -0700949 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
bungeman@google.comcefd9812013-05-15 15:07:32 +0000950 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
951 fFUnitMatrix.mapPoints(&skVertOffset, 1);
bungemanef27ce32015-10-29 09:30:32 -0700952 *offset = skVertOffset;
953 return;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000954 }
bungemanef27ce32015-10-29 09:30:32 -0700955 cgVertOffset = CGSizeApplyAffineTransform(cgVertOffset, fTransform);
956 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
957 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
958 skVertOffset.fY = -skVertOffset.fY;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000959 *offset = skVertOffset;
960}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000961
962uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
963 if (fFBoundingBoxesGlyphOffset) {
964 return fFBoundingBoxesGlyphOffset;
965 }
966 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
967 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
968 if (hheaTable.fData) {
969 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
970 }
971 return fFBoundingBoxesGlyphOffset;
972}
reed@android.com0680d6c2008-12-19 19:46:15 +0000973
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000974bool SkScalerContext_Mac::generateBBoxes() {
975 if (fGeneratedFBoundingBoxes) {
bsalomon49f085d2014-09-05 13:34:00 -0700976 return SkToBool(fFBoundingBoxes.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000977 }
978 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000979
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000980 AutoCGTable<SkOTTableHead> headTable(fCGFont);
981 if (!headTable.fData) {
982 return false;
983 }
984
985 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
986 if (!locaTable.fData) {
987 return false;
988 }
989
990 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
991 if (!glyfTable.fData) {
992 return false;
993 }
994
995 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000996 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000997
998 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
999 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
1000 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
1001 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
1002 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
1003 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
1004 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
1005 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
1006 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
1007 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
1008 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +00001009
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001010 return true;
1011}
1012
1013unsigned SkScalerContext_Mac::generateGlyphCount(void) {
1014 return fGlyphCount;
1015}
1016
1017uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
reedd0f41732015-07-10 12:08:38 -07001018 AUTO_CG_LOCK();
1019
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001020 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001021 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001022
1023 // Get the glyph
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001024 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar);
1025 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001026
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001027 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
1028 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1029 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
1030 CTFontGetGlyphsForCharacters(fCTFont, theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001031 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001032}
1033
1034void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
1035 this->generateMetrics(glyph);
1036}
1037
bungeman@google.comcefd9812013-05-15 15:07:32 +00001038void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
reedd0f41732015-07-10 12:08:38 -07001039 AUTO_CG_LOCK();
1040
djsollen1b277042014-08-06 06:58:06 -07001041 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +00001042 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001043
bungeman@google.comcefd9812013-05-15 15:07:32 +00001044 // The following block produces cgAdvance in CG units (pixels, y up).
1045 CGSize cgAdvance;
1046 if (fVertical) {
bungemanef27ce32015-10-29 09:30:32 -07001047 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontVerticalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001048 &cgGlyph, &cgAdvance, 1);
bungeman3b4b66c2015-01-08 08:33:44 -08001049 // Vertical advances are returned as widths instead of heights.
1050 SkTSwap(cgAdvance.height, cgAdvance.width);
1051 cgAdvance.height = -cgAdvance.height;
bungeman@google.comcefd9812013-05-15 15:07:32 +00001052 } else {
bungemanef27ce32015-10-29 09:30:32 -07001053 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001054 &cgGlyph, &cgAdvance, 1);
1055 }
bungemanef27ce32015-10-29 09:30:32 -07001056 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
bungeman@google.comcefd9812013-05-15 15:07:32 +00001057 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
1058 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001059
bungeman@google.comcefd9812013-05-15 15:07:32 +00001060 // The following produces skBounds in SkGlyph units (pixels, y down),
1061 // or returns early if skBounds would be empty.
1062 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001063
bungeman@google.comcefd9812013-05-15 15:07:32 +00001064 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
1065 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
1066 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
1067 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
1068 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
1069 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
1070 // to center the glyph along the vertical baseline and also perform some mysterious shift
1071 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
1072 // these steps.
1073 //
1074 // It is not known which is correct (or if either is correct). However, we must always draw
1075 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
1076 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001077
bungeman@google.comcefd9812013-05-15 15:07:32 +00001078 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
1079 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001080
bungeman@google.comcefd9812013-05-15 15:07:32 +00001081 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
1082 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
1083 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
1084 // font directly.
1085 if ((isLion() || isMountainLion()) &&
1086 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1087 {
1088 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1089 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1090 return;
1091 }
1092 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1093 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1094 fFUnitMatrix.mapRect(&skBounds);
1095
1096 } else {
1097 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1098 CGRect cgBounds;
1099 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1100 &cgGlyph, &cgBounds, 1);
bungemanef27ce32015-10-29 09:30:32 -07001101 cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001102
bungeman@google.comcefd9812013-05-15 15:07:32 +00001103 // BUG?
1104 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1105 // it should be empty. So, if we see a zero-advance, we check if it has an
1106 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1107 // is rare, so we won't incur a big performance cost for this extra check.
1108 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
halcanary96fcdcc2015-08-27 07:41:13 -07001109 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, nullptr));
1110 if (nullptr == path || CGPathIsEmpty(path)) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001111 return;
1112 }
1113 }
1114
1115 if (CGRectIsEmpty_inline(cgBounds)) {
1116 return;
1117 }
1118
1119 // Convert cgBounds to SkGlyph units (pixels, y down).
1120 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1121 cgBounds.size.width, cgBounds.size.height);
1122 }
1123
1124 if (fVertical) {
1125 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1126 // Convert these horizontal bounds into vertical bounds.
1127 SkPoint offset;
1128 getVerticalOffset(cgGlyph, &offset);
1129 skBounds.offset(offset);
1130 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001131
bungeman@google.comcefd9812013-05-15 15:07:32 +00001132 // Currently the bounds are based on being rendered at (0,0).
1133 // The top left must not move, since that is the base from which subpixel positioning is offset.
1134 if (fDoSubPosition) {
1135 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1136 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1137 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001138
bungeman@google.comcefd9812013-05-15 15:07:32 +00001139 SkIRect skIBounds;
1140 skBounds.roundOut(&skIBounds);
1141 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1142 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1143 // is not currently known, as CG dilates the outlines by some percentage.
1144 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1145 skIBounds.outset(1, 1);
1146 glyph->fLeft = SkToS16(skIBounds.fLeft);
1147 glyph->fTop = SkToS16(skIBounds.fTop);
1148 glyph->fWidth = SkToU16(skIBounds.width());
1149 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001150}
1151
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001152#include "SkColorPriv.h"
1153
bungemane280d062016-03-24 11:27:05 -07001154static void build_power_table(uint8_t table[]) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001155 for (int i = 0; i < 256; i++) {
1156 float x = i / 255.f;
bungemane280d062016-03-24 11:27:05 -07001157 int xx = SkScalarRoundToInt(x * x * 255);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001158 table[i] = SkToU8(xx);
1159 }
1160}
1161
1162/**
1163 * This will invert the gamma applied by CoreGraphics, so we can get linear
1164 * values.
1165 *
1166 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1167 * The color space used does not appear to affect this choice.
1168 */
1169static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1170 static bool gInited;
1171 static uint8_t gTableCoreGraphicsSmoothing[256];
1172 if (!gInited) {
bungemane280d062016-03-24 11:27:05 -07001173 build_power_table(gTableCoreGraphicsSmoothing);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001174 gInited = true;
1175 }
1176 return gTableCoreGraphicsSmoothing;
1177}
1178
1179static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1180 while (count > 0) {
1181 uint8_t mask = 0;
1182 for (int i = 7; i >= 0; --i) {
bungemane280d062016-03-24 11:27:05 -07001183#if SK_IGNORE_MAC_FONT_WEIGHT_FIX
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001184 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
bungemane280d062016-03-24 11:27:05 -07001185#else
1186 mask |= ((CGRGBPixel_getAlpha(*src++) >> 7) ^ 0x1) << i;
1187#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001188 if (0 == --count) {
1189 break;
1190 }
1191 }
1192 *dst++ = mask;
1193 }
1194}
1195
1196template<bool APPLY_PREBLEND>
1197static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
bungemane280d062016-03-24 11:27:05 -07001198#if SK_IGNORE_MAC_FONT_WEIGHT_FIX
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001199 U8CPU r = (rgb >> 16) & 0xFF;
1200 U8CPU g = (rgb >> 8) & 0xFF;
1201 U8CPU b = (rgb >> 0) & 0xFF;
bungemane280d062016-03-24 11:27:05 -07001202#else
1203 U8CPU r = 0xFF - ((rgb >> 16) & 0xFF);
1204 U8CPU g = 0xFF - ((rgb >> 8) & 0xFF);
1205 U8CPU b = 0xFF - ((rgb >> 0) & 0xFF);
1206#endif
bungeman3b4b66c2015-01-08 08:33:44 -08001207 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1208#if SK_SHOW_TEXT_BLIT_COVERAGE
1209 lum = SkTMax(lum, (U8CPU)0x30);
1210#endif
1211 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001212}
1213template<bool APPLY_PREBLEND>
1214static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1215 const SkGlyph& glyph, const uint8_t* table8) {
1216 const int width = glyph.fWidth;
1217 size_t dstRB = glyph.rowBytes();
1218 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1219
1220 for (int y = 0; y < glyph.fHeight; y++) {
1221 for (int i = 0; i < width; ++i) {
1222 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1223 }
1224 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1225 dst += dstRB;
1226 }
1227}
1228
1229template<bool APPLY_PREBLEND>
1230static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1231 const uint8_t* tableG,
1232 const uint8_t* tableB) {
bungemane280d062016-03-24 11:27:05 -07001233#if SK_IGNORE_MAC_FONT_WEIGHT_FIX
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001234 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1235 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1236 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungemane280d062016-03-24 11:27:05 -07001237#else
1238 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 16) & 0xFF), tableR);
1239 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 8) & 0xFF), tableG);
1240 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(0xFF - ((rgb >> 0) & 0xFF), tableB);
1241#endif
bungeman3b4b66c2015-01-08 08:33:44 -08001242#if SK_SHOW_TEXT_BLIT_COVERAGE
1243 r = SkTMax(r, (U8CPU)0x30);
1244 g = SkTMax(g, (U8CPU)0x30);
1245 b = SkTMax(b, (U8CPU)0x30);
1246#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001247 return SkPack888ToRGB16(r, g, b);
1248}
1249template<bool APPLY_PREBLEND>
1250static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1251 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1252 const int width = glyph.fWidth;
1253 size_t dstRB = glyph.rowBytes();
1254 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1255
1256 for (int y = 0; y < glyph.fHeight; y++) {
1257 for (int i = 0; i < width; i++) {
1258 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1259 }
1260 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1261 dst = (uint16_t*)((char*)dst + dstRB);
1262 }
1263}
1264
bungeman34902632014-12-10 21:43:27 -08001265static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1266 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001267 U8CPU r = (rgb >> 16) & 0xFF;
1268 U8CPU g = (rgb >> 8) & 0xFF;
1269 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001270#if SK_SHOW_TEXT_BLIT_COVERAGE
1271 a = SkTMax(a, (U8CPU)0x30);
1272#endif
bungeman34902632014-12-10 21:43:27 -08001273 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001274}
reed@google.comf77b35d2013-05-02 20:39:44 +00001275
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001276void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
djsollen1b277042014-08-06 06:58:06 -07001277 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001278
1279 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1280 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1281
1282 // Draw the glyph
1283 size_t cgRowBytes;
1284 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
halcanary96fcdcc2015-08-27 07:41:13 -07001285 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001286 return;
1287 }
1288
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001289 // Fix the glyph
1290 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1291 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1292 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1293
1294 //Note that the following cannot really be integrated into the
1295 //pre-blend, since we may not be applying the pre-blend; when we aren't
1296 //applying the pre-blend it means that a filter wants linear anyway.
1297 //Other code may also be applying the pre-blend, so we'd need another
1298 //one with this and one without.
1299 CGRGBPixel* addr = cgPixels;
1300 for (int y = 0; y < glyph.fHeight; ++y) {
1301 for (int x = 0; x < glyph.fWidth; ++x) {
1302 int r = (addr[x] >> 16) & 0xFF;
1303 int g = (addr[x] >> 8) & 0xFF;
1304 int b = (addr[x] >> 0) & 0xFF;
1305 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1306 }
bungemane280d062016-03-24 11:27:05 -07001307 addr = SkTAddOffset<CGRGBPixel>(addr, cgRowBytes);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001308 }
1309 }
1310
1311 // Convert glyph to mask
1312 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001313 case SkMask::kLCD16_Format: {
1314 if (fPreBlend.isApplicable()) {
1315 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1316 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1317 } else {
1318 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1319 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1320 }
1321 } break;
1322 case SkMask::kA8_Format: {
1323 if (fPreBlend.isApplicable()) {
1324 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1325 } else {
1326 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1327 }
1328 } break;
1329 case SkMask::kBW_Format: {
1330 const int width = glyph.fWidth;
1331 size_t dstRB = glyph.rowBytes();
1332 uint8_t* dst = (uint8_t*)glyph.fImage;
1333 for (int y = 0; y < glyph.fHeight; y++) {
1334 cgpixels_to_bits(dst, cgPixels, width);
1335 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1336 dst += dstRB;
1337 }
1338 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001339 case SkMask::kARGB32_Format: {
1340 const int width = glyph.fWidth;
1341 size_t dstRB = glyph.rowBytes();
1342 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1343 for (int y = 0; y < glyph.fHeight; y++) {
1344 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001345 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001346 }
1347 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1348 dst = (SkPMColor*)((char*)dst + dstRB);
1349 }
1350 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001351 default:
1352 SkDEBUGFAIL("unexpected mask format");
1353 break;
1354 }
1355}
1356
1357/*
1358 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1359 * seems sufficient, and possibly even correct, to allow the hinted outline
1360 * to be subpixel positioned.
1361 */
1362#define kScaleForSubPixelPositionHinting (4.0f)
1363
1364void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001365 AUTO_CG_LOCK();
1366
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001367 SkScalar scaleX = SK_Scalar1;
1368 SkScalar scaleY = SK_Scalar1;
1369
bungemanef27ce32015-10-29 09:30:32 -07001370 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001371 /*
1372 * For subpixel positioning, we want to return an unhinted outline, so it
1373 * can be positioned nicely at fractional offsets. However, we special-case
1374 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1375 * we want to retain hinting in the direction orthogonal to the baseline.
1376 * e.g. for horizontal baseline, we want to retain hinting in Y.
1377 * The way we remove hinting is to scale the font by some value (4) in that
1378 * direction, ask for the path, and then scale the path back down.
1379 */
bungeman7cbeaae2015-09-22 09:54:56 -07001380 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001381 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001382 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001383 // now see if we need to restore hinting for axis-aligned baselines
bungeman27876bc2016-02-29 11:22:55 -08001384 switch (this->computeAxisAlignmentForHText()) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001385 case kX_SkAxisAlignment:
1386 scaleY = SK_Scalar1; // want hinting in the Y direction
1387 break;
1388 case kY_SkAxisAlignment:
1389 scaleX = SK_Scalar1; // want hinting in the X direction
1390 break;
1391 default:
1392 break;
1393 }
1394
bungemanef27ce32015-10-29 09:30:32 -07001395 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1396 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001397 }
1398
djsollen1b277042014-08-06 06:58:06 -07001399 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID();
bungemanef27ce32015-10-29 09:30:32 -07001400 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont, cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001401
1402 path->reset();
halcanary96fcdcc2015-08-27 07:41:13 -07001403 if (cgPath != nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001404 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1405 }
1406
bungeman@google.comcefd9812013-05-15 15:07:32 +00001407 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001408 SkMatrix m;
1409 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1410 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001411 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001412 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001413 SkPoint offset;
1414 getVerticalOffset(cgGlyph, &offset);
1415 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001416 }
1417}
1418
bungeman41078062014-07-07 08:16:37 -07001419void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001420 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001421 return;
1422 }
1423
reedd0f41732015-07-10 12:08:38 -07001424 AUTO_CG_LOCK();
1425
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001426 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1427
bungeman41078062014-07-07 08:16:37 -07001428 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1429 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1430 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1431 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1432 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1433 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1434 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1435 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001436 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungeman41078062014-07-07 08:16:37 -07001437 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
bungeman6bd8d1c2015-06-09 08:40:51 -07001438 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont));
bungeman41078062014-07-07 08:16:37 -07001439 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont));
1440 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001441
bungeman41078062014-07-07 08:16:37 -07001442 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
1443 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001444}
1445
1446void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1447 SkPath* skPath = (SkPath*)info;
1448
1449 // Process the path element
1450 switch (element->type) {
1451 case kCGPathElementMoveToPoint:
1452 skPath->moveTo(element->points[0].x, -element->points[0].y);
1453 break;
1454
1455 case kCGPathElementAddLineToPoint:
1456 skPath->lineTo(element->points[0].x, -element->points[0].y);
1457 break;
1458
1459 case kCGPathElementAddQuadCurveToPoint:
1460 skPath->quadTo(element->points[0].x, -element->points[0].y,
1461 element->points[1].x, -element->points[1].y);
1462 break;
1463
1464 case kCGPathElementAddCurveToPoint:
1465 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1466 element->points[1].x, -element->points[1].y,
1467 element->points[2].x, -element->points[2].y);
1468 break;
1469
1470 case kCGPathElementCloseSubpath:
1471 skPath->close();
1472 break;
1473
1474 default:
1475 SkDEBUGFAIL("Unknown path element!");
1476 break;
1477 }
1478}
1479
1480
1481///////////////////////////////////////////////////////////////////////////////
1482
halcanary96fcdcc2015-08-27 07:41:13 -07001483// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001484// Call must still manage its ownership of provider
1485static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1486 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
halcanary96fcdcc2015-08-27 07:41:13 -07001487 if (nullptr == cg) {
1488 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001489 }
halcanary96fcdcc2015-08-27 07:41:13 -07001490 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr);
1491 return ct ? NewFromFontRef(ct, nullptr, nullptr, true) : nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001492}
1493
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001494// Web fonts added to the the CTFont registry do not return their character set.
1495// Iterate through the font in this case. The existing caller caches the result,
1496// so the performance impact isn't too bad.
1497static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1498 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001499 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001500 SkUnichar* out = glyphToUnicode->begin();
1501 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1502 UniChar unichar = 0;
1503 while (glyphCount > 0) {
1504 CGGlyph glyph;
1505 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1506 out[glyph] = unichar;
1507 --glyphCount;
1508 }
1509 if (++unichar == 0) {
1510 break;
1511 }
1512 }
1513}
1514
1515// Construct Glyph to Unicode table.
1516// Unicode code points that require conjugate pairs in utf16 are not
1517// supported.
1518static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1519 SkTDArray<SkUnichar>* glyphToUnicode) {
1520 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1521 if (!charSet) {
1522 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1523 return;
1524 }
1525
1526 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1527 charSet));
1528 if (!bitmap) {
1529 return;
1530 }
1531 CFIndex length = CFDataGetLength(bitmap);
1532 if (!length) {
1533 return;
1534 }
1535 if (length > 8192) {
1536 // TODO: Add support for Unicode above 0xFFFF
1537 // Consider only the BMP portion of the Unicode character points.
1538 // The bitmap may contain other planes, up to plane 16.
1539 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1540 length = 8192;
1541 }
1542 const UInt8* bits = CFDataGetBytePtr(bitmap);
reed@google.com7fa2a652014-01-27 13:42:58 +00001543 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001544 SkUnichar* out = glyphToUnicode->begin();
1545 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1546 for (int i = 0; i < length; i++) {
1547 int mask = bits[i];
1548 if (!mask) {
1549 continue;
1550 }
1551 for (int j = 0; j < 8; j++) {
1552 CGGlyph glyph;
1553 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1554 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1555 out[glyph] = unichar;
1556 }
1557 }
1558 }
1559}
1560
1561static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1562 CGSize advance;
1563 advance.width = 0;
1564 CGGlyph glyph = gId;
1565 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1566 *data = sk_float_round2int(advance.width);
1567 return true;
1568}
1569
halcanary96fcdcc2015-08-27 07:41:13 -07001570/** Assumes src and dst are not nullptr. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001571static void CFStringToSkString(CFStringRef src, SkString* dst) {
1572 // Reserve enough room for the worst-case string,
1573 // plus 1 byte for the trailing null.
1574 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1575 kCFStringEncodingUTF8) + 1;
1576 dst->resize(length);
1577 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1578 // Resize to the actual UTF-8 length used, stripping the null character.
1579 dst->resize(strlen(dst->c_str()));
1580}
1581
reed@google.com2689f612013-03-20 20:01:47 +00001582SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -07001583 PerGlyphInfo perGlyphInfo,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001584 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001585 uint32_t glyphIDsCount) const {
1586
reedd0f41732015-07-10 12:08:38 -07001587 AUTO_CG_LOCK();
1588
reed@google.com2689f612013-03-20 20:01:47 +00001589 CTFontRef originalCTFont = fFontRef.get();
bungeman7cbeaae2015-09-22 09:54:56 -07001590 AutoCFRelease<CTFontRef> ctFont(ctfont_create_exact_copy(
bungeman05846312015-09-23 12:51:28 -07001591 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -07001592
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001593 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1594
1595 {
1596 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
bungeman256b3512014-07-02 07:57:59 -07001597 if (fontName.get()) {
1598 CFStringToSkString(fontName, &info->fFontName);
1599 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001600 }
1601
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001602 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1603 info->fLastGlyphID = SkToU16(glyphCount - 1);
1604 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1605
reed39a9a502015-05-12 09:50:04 -07001606 if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001607 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1608 }
1609
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001610 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1611 // fonts always have both glyf and loca tables. At the least, this is what
1612 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1613 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001614 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001615 return info;
1616 }
1617
1618 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1619 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1620 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1621 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1622 }
1623 if (symbolicTraits & kCTFontItalicTrait) {
1624 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1625 }
1626 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001627 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1628 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1629 } else if (stylisticClass & kCTFontScriptsClass) {
1630 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1631 }
1632 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1633 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1634 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1635 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1636 CGRect bbox = CTFontGetBoundingBox(ctFont);
1637
1638 SkRect r;
1639 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1640 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1641 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1642 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1643
1644 r.roundOut(&(info->fBBox));
1645
1646 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1647 // This probably isn't very good with an italic font.
1648 int16_t min_width = SHRT_MAX;
1649 info->fStemV = 0;
1650 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1651 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1652 CGGlyph glyphs[count];
1653 CGRect boundingRects[count];
1654 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1655 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1656 glyphs, boundingRects, count);
1657 for (size_t i = 0; i < count; i++) {
1658 int16_t width = (int16_t) boundingRects[i].size.width;
1659 if (width > 0 && width < min_width) {
1660 min_width = width;
1661 info->fStemV = min_width;
1662 }
1663 }
1664 }
1665
reed39a9a502015-05-12 09:50:04 -07001666 if (perGlyphInfo & kHAdvance_PerGlyphInfo) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001667 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1668 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1669 info->fGlyphWidths->fAdvance.append(1, &min_width);
1670 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1671 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1672 } else {
1673 info->fGlyphWidths.reset(
1674 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
reed@google.com7fa2a652014-01-27 13:42:58 +00001675 SkToInt(glyphCount),
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001676 glyphIDs,
1677 glyphIDsCount,
1678 &getWidthAdvance));
1679 }
1680 }
1681 return info;
1682}
1683
1684///////////////////////////////////////////////////////////////////////////////
1685
reed@google.comcc9aad52013-03-21 19:28:10 +00001686static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1687 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001688 AutoCFRelease<CFNumberRef> fontFormatRef(
1689 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1690 if (!fontFormatRef) {
1691 return 0;
1692 }
1693
1694 SInt32 fontFormatValue;
1695 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1696 return 0;
1697 }
1698
1699 switch (fontFormatValue) {
1700 case kCTFontFormatOpenTypePostScript:
1701 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1702 case kCTFontFormatOpenTypeTrueType:
1703 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1704 case kCTFontFormatTrueType:
1705 return SkSFNTHeader::fontType_MacTrueType::TAG;
1706 case kCTFontFormatPostScript:
1707 return SkSFNTHeader::fontType_PostScript::TAG;
1708 case kCTFontFormatBitmap:
1709 return SkSFNTHeader::fontType_MacTrueType::TAG;
1710 case kCTFontFormatUnrecognized:
1711 default:
1712 //CT seems to be unreliable in being able to obtain the type,
1713 //even if all we want is the first four bytes of the font resource.
1714 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1715 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1716 }
1717}
1718
bungeman5f213d92015-01-27 05:39:10 -08001719SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
reed@google.comcc9aad52013-03-21 19:28:10 +00001720 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001721 if (0 == fontType) {
halcanary96fcdcc2015-08-27 07:41:13 -07001722 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001723 }
1724
1725 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001726 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001727 SkTDArray<SkFontTableTag> tableTags;
1728 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001729 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001730
1731 // calc total size for font, save sizes
1732 SkTDArray<size_t> tableSizes;
1733 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1734 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001735 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001736 totalSize += (tableSize + 3) & ~3;
1737 *tableSizes.append() = tableSize;
1738 }
1739
1740 // reserve memory for stream, and zero it (tables must be zero padded)
1741 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1742 char* dataStart = (char*)stream->getMemoryBase();
1743 sk_bzero(dataStart, totalSize);
1744 char* dataPtr = dataStart;
1745
1746 // compute font header entries
1747 uint16_t entrySelector = 0;
1748 uint16_t searchRange = 1;
1749 while (searchRange < numTables >> 1) {
1750 entrySelector++;
1751 searchRange <<= 1;
1752 }
1753 searchRange <<= 4;
1754 uint16_t rangeShift = (numTables << 4) - searchRange;
1755
1756 // write font header
1757 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1758 header->fontType = fontType;
1759 header->numTables = SkEndian_SwapBE16(numTables);
1760 header->searchRange = SkEndian_SwapBE16(searchRange);
1761 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1762 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1763 dataPtr += sizeof(SkSFNTHeader);
1764
1765 // write tables
1766 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1767 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1768 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1769 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001770 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001771 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1772 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1773 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001774 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1775 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001776
1777 dataPtr += (tableSize + 3) & ~3;
1778 ++entry;
1779 }
1780
bungemanb3310c22015-03-02 09:05:36 -08001781 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001782 return stream;
1783}
1784
bungeman41868fe2015-05-20 09:21:04 -07001785struct NonDefaultAxesContext {
1786 SkFixed* axisValue;
1787 CFArrayRef cgAxes;
1788};
1789static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1790 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1791
1792 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1793 return;
1794 }
1795
1796 // The key is a CFString which is a string from the 'name' table.
1797 // Search the cgAxes for an axis with this name, and use its index to store the value.
1798 CFIndex keyIndex = -1;
1799 CFStringRef keyString = static_cast<CFStringRef>(key);
1800 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1801 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1802 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1803 continue;
1804 }
1805
1806 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1807 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1808 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1809 continue;
1810 }
1811 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1812 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1813 keyIndex = i;
1814 break;
1815 }
1816 }
1817 if (keyIndex == -1) {
1818 return;
1819 }
1820
1821 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1822 double valueDouble;
1823 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1824 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1825 {
1826 return;
1827 }
1828 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1829}
1830static bool get_variations(CTFontRef fFontRef, CFIndex* cgAxisCount,
1831 SkAutoSTMalloc<4, SkFixed>* axisValues)
1832{
1833 // CTFontCopyVariationAxes and CTFontCopyVariation do not work when applied to fonts which
halcanary96fcdcc2015-08-27 07:41:13 -07001834 // started life with CGFontCreateWithDataProvider (they simply always return nullptr).
bungeman41868fe2015-05-20 09:21:04 -07001835 // As a result, we are limited to CGFontCopyVariationAxes and CGFontCopyVariations.
halcanary96fcdcc2015-08-27 07:41:13 -07001836 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07001837
1838 AutoCFRelease<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont));
halcanary96fcdcc2015-08-27 07:41:13 -07001839 // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
bungeman41868fe2015-05-20 09:21:04 -07001840 if (!cgVariations.get()) {
1841 return false;
1842 }
1843
1844 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont));
1845 *cgAxisCount = CFArrayGetCount(cgAxes);
1846 axisValues->reset(*cgAxisCount);
1847
1848 // Set all of the axes to their default values.
1849 // Fail if any default value cannot be determined.
1850 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
1851 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
1852 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1853 return false;
1854 }
1855
1856 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1857 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1858 kCGFontVariationAxisDefaultValue);
1859 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1860 return false;
1861 }
1862 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
1863 double axisDefaultValueDouble;
1864 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
1865 {
1866 return false;
1867 }
1868 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
1869 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
1870 {
1871 return false;
1872 }
1873 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
1874 }
1875
1876 // Override the default values with the given font's stated axis values.
1877 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
1878 CFDictionaryApplyFunction(cgVariations, set_non_default_axes, &c);
1879
1880 return true;
1881}
1882SkFontData* SkTypeface_Mac::onCreateFontData() const {
1883 int index;
1884 SkAutoTDelete<SkStreamAsset> stream(this->onOpenStream(&index));
1885
1886 CFIndex cgAxisCount;
1887 SkAutoSTMalloc<4, SkFixed> axisValues;
1888 if (get_variations(fFontRef, &cgAxisCount, &axisValues)) {
mtklein18300a32016-03-16 13:53:35 -07001889 return new SkFontData(stream.release(), index, axisValues.get(), cgAxisCount);
bungeman41868fe2015-05-20 09:21:04 -07001890 }
mtklein18300a32016-03-16 13:53:35 -07001891 return new SkFontData(stream.release(), index, nullptr, 0);
bungeman41868fe2015-05-20 09:21:04 -07001892}
1893
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001894///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001895///////////////////////////////////////////////////////////////////////////////
1896
1897int SkTypeface_Mac::onGetUPEM() const {
halcanary96fcdcc2015-08-27 07:41:13 -07001898 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001899 return CGFontGetUnitsPerEm(cgFont);
1900}
1901
bungeman@google.com839702b2013-08-07 17:09:22 +00001902SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001903 SkTypeface::LocalizedStrings* nameIter =
1904 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07001905 if (nullptr == nameIter) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001906 AutoCFRelease<CFStringRef> cfLanguage;
1907 AutoCFRelease<CFStringRef> cfFamilyName(
1908 CTFontCopyLocalizedName(fFontRef, kCTFontFamilyNameKey, &cfLanguage));
1909
1910 SkString skLanguage;
1911 SkString skFamilyName;
1912 if (cfLanguage.get()) {
1913 CFStringToSkString(cfLanguage.get(), &skLanguage);
1914 } else {
1915 skLanguage = "und"; //undetermined
1916 }
1917 if (cfFamilyName.get()) {
1918 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1919 }
1920
1921 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1922 }
1923 return nameIter;
1924}
1925
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001926// If, as is the case with web fonts, the CTFont data isn't available,
1927// the CGFont data may work. While the CGFont may always provide the
1928// right result, leave the CTFont code path to minimize disruption.
1929static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1930 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1931 kCTFontTableOptionNoOptions);
halcanary96fcdcc2015-08-27 07:41:13 -07001932 if (nullptr == data) {
1933 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001934 data = CGFontCopyTableForTag(cgFont, tag);
1935 }
1936 return data;
1937}
1938
1939int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1940 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1941 kCTFontTableOptionNoOptions));
halcanary96fcdcc2015-08-27 07:41:13 -07001942 if (nullptr == cfArray) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001943 return 0;
1944 }
reed@google.com7fa2a652014-01-27 13:42:58 +00001945 int count = SkToInt(CFArrayGetCount(cfArray));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001946 if (tags) {
1947 for (int i = 0; i < count; ++i) {
1948 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1949 tags[i] = static_cast<SkFontTableTag>(fontTag);
1950 }
1951 }
1952 return count;
1953}
1954
1955size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1956 size_t length, void* dstData) const {
1957 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
halcanary96fcdcc2015-08-27 07:41:13 -07001958 if (nullptr == srcData) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001959 return 0;
1960 }
1961
1962 size_t srcSize = CFDataGetLength(srcData);
1963 if (offset >= srcSize) {
1964 return 0;
1965 }
1966 if (length > srcSize - offset) {
1967 length = srcSize - offset;
1968 }
1969 if (dstData) {
1970 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1971 }
1972 return length;
1973}
1974
1975SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001976 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001977}
1978
1979void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001980 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1981 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1982 {
1983 rec->fMaskFormat = SkMask::kA8_Format;
1984 // Render the glyphs as close as possible to what was requested.
1985 // The above turns off subpixel rendering, but the user requested it.
1986 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1987 // See comments below for more details.
1988 rec->setHinting(SkPaint::kNormal_Hinting);
1989 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001990
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001991 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00001992 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001993 SkScalerContext::kLCD_BGROrder_Flag |
1994 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001995
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001996 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001997
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001998 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001999
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002000 // Only two levels of hinting are supported.
2001 // kNo_Hinting means avoid CoreGraphics outline dilation.
2002 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
2003 // If there is no lcd support, hinting (dilation) cannot be supported.
2004 SkPaint::Hinting hinting = rec->getHinting();
2005 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
2006 hinting = SkPaint::kNo_Hinting;
2007 } else if (SkPaint::kFull_Hinting == hinting) {
2008 hinting = SkPaint::kNormal_Hinting;
2009 }
2010 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002011
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002012 // FIXME: lcd smoothed un-hinted rasterization unsupported.
2013 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
2014 // There is no current means to honor a request for unhinted lcd,
2015 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002016
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002017 // Hinting and smoothing should be orthogonal, but currently they are not.
2018 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
2019 // output is drawn from auto-dilated outlines (the amount of which is
2020 // determined by AppleFontSmoothing). Its regular anti-aliased output is
2021 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002022
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002023 // The behavior of Skia is as follows:
2024 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
2025 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
2026 // channel. This matches [LCD][yes-hint] in weight.
2027 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
2028 // Currenly side with LCD, effectively ignoring the hinting setting.
2029 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002030
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002031 if (isLCDFormat(rec->fMaskFormat)) {
2032 if (lcdSupport) {
2033 //CoreGraphics creates 555 masks for smoothed text anyway.
2034 rec->fMaskFormat = SkMask::kLCD16_Format;
2035 rec->setHinting(SkPaint::kNormal_Hinting);
2036 } else {
2037 rec->fMaskFormat = SkMask::kA8_Format;
2038 }
2039 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002040
bungeman34902632014-12-10 21:43:27 -08002041 // CoreText provides no information as to whether a glyph will be color or not.
2042 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
2043 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08002044 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08002045 rec->fMaskFormat = SkMask::kARGB32_Format;
2046 }
2047
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002048 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2049 // All other masks can use regular gamma.
2050 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
2051#ifndef SK_GAMMA_APPLY_TO_A8
2052 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002053#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002054 } else {
2055 //CoreGraphics dialates smoothed text as needed.
2056 rec->setContrast(0);
2057 }
2058}
2059
2060// we take ownership of the ref
2061static const char* get_str(CFStringRef ref, SkString* str) {
halcanary96fcdcc2015-08-27 07:41:13 -07002062 if (nullptr == ref) {
2063 return nullptr;
bungeman256b3512014-07-02 07:57:59 -07002064 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002065 CFStringToSkString(ref, str);
2066 CFSafeRelease(ref);
2067 return str->c_str();
2068}
2069
bungemanb374d6a2014-09-17 07:48:59 -07002070void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
2071 get_str(CTFontCopyFamilyName(fFontRef), familyName);
2072}
2073
reed@google.com5526ede2013-03-25 13:03:37 +00002074void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2075 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002076 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002077
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002078 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
2079 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
2080 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
caseq26337e92014-06-30 12:14:52 -07002081 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002082}
reed@google.com5526ede2013-03-25 13:03:37 +00002083
reed@google.combcb42ae2013-07-02 13:56:39 +00002084int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002085 uint16_t glyphs[], int glyphCount) const
2086{
2087 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
2088 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
2089 // 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 +00002090
reed@google.combcb42ae2013-07-02 13:56:39 +00002091 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002092 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
2093 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00002094 switch (encoding) {
2095 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002096 const char* utf8 = reinterpret_cast<const char*>(chars);
2097 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2098 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00002099 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002100 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
2101 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00002102 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002103 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002104 break;
2105 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002106 case kUTF16_Encoding: {
2107 src = reinterpret_cast<const UniChar*>(chars);
2108 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00002109 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002110 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2111 ++extra;
2112 }
reed@google.combcb42ae2013-07-02 13:56:39 +00002113 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002114 srcCount = glyphCount + extra;
2115 break;
2116 }
2117 case kUTF32_Encoding: {
2118 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
2119 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2120 src = utf16;
2121 for (int i = 0; i < glyphCount; ++i) {
2122 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
2123 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002124 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002125 break;
2126 }
2127 }
2128
halcanary96fcdcc2015-08-27 07:41:13 -07002129 // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002130 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002131 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2132 uint16_t* macGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002133 if (nullptr == macGlyphs || srcCount > glyphCount) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002134 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002135 }
2136
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002137 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, srcCount);
2138
2139 // If there were any non-bmp, then copy and compact.
halcanary96fcdcc2015-08-27 07:41:13 -07002140 // If 'glyphs' is nullptr, then compact glyphStorage in-place.
2141 // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
2142 // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002143 uint16_t* compactedGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002144 if (nullptr == compactedGlyphs) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002145 compactedGlyphs = macGlyphs;
2146 }
2147 if (srcCount > glyphCount) {
2148 int extra = 0;
2149 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002150 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002151 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2152 ++extra;
2153 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002154 }
2155 }
2156
2157 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002158 return glyphCount;
2159 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002160
2161 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002162 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002163 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002164 return i;
2165 }
2166 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002167 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002168 return glyphCount;
2169}
2170
2171int SkTypeface_Mac::onCountGlyphs() const {
reed@google.com7fa2a652014-01-27 13:42:58 +00002172 return SkToInt(CTFontGetGlyphCount(fFontRef));
reed@google.combcb42ae2013-07-02 13:56:39 +00002173}
2174
reed@google.com95625db2013-03-25 20:44:02 +00002175///////////////////////////////////////////////////////////////////////////////
2176///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002177
2178static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
2179 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
halcanary96fcdcc2015-08-27 07:41:13 -07002180 if (nullptr == ref.get()) {
reed@google.com83787c52013-03-26 17:19:15 +00002181 return false;
2182 }
2183 CFStringToSkString(ref, value);
2184 return true;
2185}
2186
reed@google.com95625db2013-03-25 20:44:02 +00002187#include "SkFontMgr.h"
2188
reed@google.com964988f2013-03-29 14:57:22 +00002189static inline int sqr(int value) {
2190 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2191 return value * value;
2192}
2193
2194// We normalize each axis (weight, width, italic) to be base-900
2195static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2196 return sqr(a.weight() - b.weight()) +
2197 sqr((a.width() - b.width()) * 100) +
2198 sqr((a.isItalic() != b.isItalic()) * 900);
2199}
2200
bungemana4c4a2d2014-10-20 13:33:19 -07002201static SkTypeface* createFromDesc(CFStringRef cfFamilyName, CTFontDescriptorRef desc) {
bungeman967937c2014-10-30 11:49:27 -07002202 NameStyle cacheRequest;
2203 SkString skFamilyName;
2204 CFStringToSkString(cfFamilyName, &skFamilyName);
2205 cacheRequest.fName = skFamilyName.c_str();
bungeman336fdf22014-11-10 07:48:55 -08002206 cacheRequest.fStyle = fontstyle_from_descriptor(desc);
reed@google.comdea7ee02013-03-28 14:12:10 +00002207
bungeman967937c2014-10-30 11:49:27 -07002208 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_NameStyle, &cacheRequest);
reed@google.comdea7ee02013-03-28 14:12:10 +00002209 if (face) {
2210 return face;
2211 }
2212
halcanary96fcdcc2015-08-27 07:41:13 -07002213 AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
bungeman64fb51d2015-05-04 12:03:50 -07002214 if (!ctFont) {
halcanary96fcdcc2015-08-27 07:41:13 -07002215 return nullptr;
reed@google.comce8b3de2013-03-26 19:30:16 +00002216 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002217
reed@google.comce8b3de2013-03-26 19:30:16 +00002218 bool isFixedPitch;
bungeman336fdf22014-11-10 07:48:55 -08002219 (void)computeStyleBits(ctFont, &isFixedPitch);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002220
mtklein18300a32016-03-16 13:53:35 -07002221 face = new SkTypeface_Mac(ctFont.release(), nullptr, cacheRequest.fStyle, isFixedPitch,
halcanary385fe4d2015-08-26 13:07:48 -07002222 skFamilyName.c_str(), false);
bungemana4c4a2d2014-10-20 13:33:19 -07002223 SkTypefaceCache::Add(face, face->fontStyle());
reed@google.comdea7ee02013-03-28 14:12:10 +00002224 return face;
reed@google.comce8b3de2013-03-26 19:30:16 +00002225}
2226
reed@google.com83787c52013-03-26 17:19:15 +00002227class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002228public:
reed@google.com83787c52013-03-26 17:19:15 +00002229 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
halcanary96fcdcc2015-08-27 07:41:13 -07002230 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
reed@google.comdea7ee02013-03-28 14:12:10 +00002231 , fFamilyName(familyName)
2232 , fCount(0) {
reed@google.com83787c52013-03-26 17:19:15 +00002233 CFRetain(familyName);
halcanary96fcdcc2015-08-27 07:41:13 -07002234 if (nullptr == fArray) {
2235 fArray = CFArrayCreate(nullptr, nullptr, 0, nullptr);
reed@google.comdea7ee02013-03-28 14:12:10 +00002236 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002237 fCount = SkToInt(CFArrayGetCount(fArray));
reed@google.com83787c52013-03-26 17:19:15 +00002238 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002239
reed@google.com83787c52013-03-26 17:19:15 +00002240 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002241 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002242 CFRelease(fFamilyName);
2243 }
2244
mtklein36352bf2015-03-25 18:17:31 -07002245 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002246 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002247 }
2248
mtklein36352bf2015-03-25 18:17:31 -07002249 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002250 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002251 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2252 if (style) {
bungeman336fdf22014-11-10 07:48:55 -08002253 *style = fontstyle_from_descriptor(desc);
reed@google.com83787c52013-03-26 17:19:15 +00002254 }
2255 if (name) {
2256 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2257 name->reset();
2258 }
2259 }
2260 }
2261
mtklein36352bf2015-03-25 18:17:31 -07002262 SkTypeface* createTypeface(int index) override {
reed@google.com83787c52013-03-26 17:19:15 +00002263 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2264 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002265
reed@google.com83787c52013-03-26 17:19:15 +00002266 return createFromDesc(fFamilyName, desc);
2267 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002268
mtklein36352bf2015-03-25 18:17:31 -07002269 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002270 if (0 == fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002271 return nullptr;
reed@google.com964988f2013-03-29 14:57:22 +00002272 }
2273 return createFromDesc(fFamilyName, findMatchingDesc(pattern));
2274 }
2275
reed@google.com83787c52013-03-26 17:19:15 +00002276private:
2277 CFArrayRef fArray;
2278 CFStringRef fFamilyName;
reed@google.comdea7ee02013-03-28 14:12:10 +00002279 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002280
2281 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2282 int bestMetric = SK_MaxS32;
halcanary96fcdcc2015-08-27 07:41:13 -07002283 CTFontDescriptorRef bestDesc = nullptr;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002284
reed@google.com964988f2013-03-29 14:57:22 +00002285 for (int i = 0; i < fCount; ++i) {
2286 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
bungeman336fdf22014-11-10 07:48:55 -08002287 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc));
reed@google.com964988f2013-03-29 14:57:22 +00002288 if (0 == metric) {
2289 return desc;
2290 }
2291 if (metric < bestMetric) {
2292 bestMetric = metric;
2293 bestDesc = desc;
2294 }
2295 }
2296 SkASSERT(bestDesc);
2297 return bestDesc;
2298 }
reed@google.com83787c52013-03-26 17:19:15 +00002299};
2300
2301class SkFontMgr_Mac : public SkFontMgr {
reed@google.com83787c52013-03-26 17:19:15 +00002302 CFArrayRef fNames;
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002303 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002304
2305 CFStringRef stringAt(int index) const {
2306 SkASSERT((unsigned)index < (unsigned)fCount);
2307 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2308 }
2309
reed@google.com964988f2013-03-29 14:57:22 +00002310 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2311 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2312 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2313 &kCFTypeDictionaryKeyCallBacks,
2314 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002315
reed@google.com964988f2013-03-29 14:57:22 +00002316 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002317
reed@google.com964988f2013-03-29 14:57:22 +00002318 AutoCFRelease<CTFontDescriptorRef> desc(
2319 CTFontDescriptorCreateWithAttributes(cfAttr));
halcanary385fe4d2015-08-26 13:07:48 -07002320 return new SkFontStyleSet_Mac(cfFamilyName, desc);
reed@google.com964988f2013-03-29 14:57:22 +00002321 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002322
reed@google.com83787c52013-03-26 17:19:15 +00002323public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002324 SkFontMgr_Mac()
2325 : fNames(SkCTFontManagerCopyAvailableFontFamilyNames())
2326 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames)) : 0) {}
reed@google.com83787c52013-03-26 17:19:15 +00002327
2328 virtual ~SkFontMgr_Mac() {
2329 CFSafeRelease(fNames);
2330 }
reed@google.com95625db2013-03-25 20:44:02 +00002331
2332protected:
mtklein36352bf2015-03-25 18:17:31 -07002333 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002334 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002335 }
2336
mtklein36352bf2015-03-25 18:17:31 -07002337 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002338 if ((unsigned)index < (unsigned)fCount) {
2339 CFStringToSkString(this->stringAt(index), familyName);
2340 } else {
2341 familyName->reset();
2342 }
reed@google.com95625db2013-03-25 20:44:02 +00002343 }
2344
mtklein36352bf2015-03-25 18:17:31 -07002345 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002346 if ((unsigned)index >= (unsigned)fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002347 return nullptr;
reed@google.com83787c52013-03-26 17:19:15 +00002348 }
reed@google.com964988f2013-03-29 14:57:22 +00002349 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002350 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002351
mtklein36352bf2015-03-25 18:17:31 -07002352 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
reed@google.com964988f2013-03-29 14:57:22 +00002353 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2354 return CreateSet(cfName);
2355 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002356
reed@google.com95625db2013-03-25 20:44:02 +00002357 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
bungeman5eff9e72016-01-21 08:18:42 -08002358 const SkFontStyle& fontStyle) const override {
2359 SkAutoTUnref<SkFontStyleSet> sset(this->matchFamily(familyName));
2360 return sset->matchStyle(fontStyle);
reed@google.com95625db2013-03-25 20:44:02 +00002361 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002362
djsollen33068c12014-11-14 10:52:53 -08002363 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2364 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002365 SkUnichar character) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002366 return nullptr;
djsollen33068c12014-11-14 10:52:53 -08002367 }
2368
reed@google.com95625db2013-03-25 20:44:02 +00002369 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002370 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002371 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002372 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002373
mtklein36352bf2015-03-25 18:17:31 -07002374 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002375 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
halcanary96fcdcc2015-08-27 07:41:13 -07002376 if (nullptr == pr) {
2377 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002378 }
2379 return create_from_dataProvider(pr);
2380 }
2381
mtklein36352bf2015-03-25 18:17:31 -07002382 SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002383 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
halcanary96fcdcc2015-08-27 07:41:13 -07002384 if (nullptr == pr) {
2385 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002386 }
2387 return create_from_dataProvider(pr);
2388 }
2389
bungemanf6c71072016-01-21 14:17:47 -08002390 static CFNumberRef get_tag_for_name(CFStringRef name, CFArrayRef ctAxes) {
2391 CFIndex ctAxisCount = CFArrayGetCount(ctAxes);
2392 for (int i = 0; i < ctAxisCount; ++i) {
2393 CFTypeRef ctAxisInfo = CFArrayGetValueAtIndex(ctAxes, i);
2394 if (CFDictionaryGetTypeID() != CFGetTypeID(ctAxisInfo)) {
2395 return nullptr;
2396 }
2397 CFDictionaryRef ctAxisInfoDict = static_cast<CFDictionaryRef>(ctAxisInfo);
2398
2399 CFTypeRef ctAxisName = CFDictionaryGetValue(ctAxisInfoDict,
2400 kCTFontVariationAxisNameKey);
2401 if (!ctAxisName || CFGetTypeID(ctAxisName) != CFStringGetTypeID()) {
2402 return nullptr;
2403 }
2404
2405 if (CFEqual(name, ctAxisName)) {
2406 CFTypeRef tag = CFDictionaryGetValue(ctAxisInfoDict,
2407 kCTFontVariationAxisIdentifierKey);
2408 if (!tag || CFGetTypeID(tag) != CFNumberGetTypeID()) {
2409 return nullptr;
2410 }
2411 return static_cast<CFNumberRef>(tag);
2412 }
2413 }
2414 return nullptr;
2415 }
2416 static CFDictionaryRef get_axes(CGFontRef cg, const FontParameters& params) {
2417 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
2418 if (!cgAxes) {
2419 return nullptr;
2420 }
2421 CFIndex axisCount = CFArrayGetCount(cgAxes);
2422
2423 // The CGFont variation data is keyed by name, and lacks the tag.
2424 // The CTFont variation data is keyed by tag, and also has the name.
2425 // We would like to work with CTFont variaitons, but creating a CTFont font with
2426 // CTFont variation dictionary runs into bugs. So use the CTFont variation data
2427 // to match names to tags to create the appropriate CGFont.
2428 AutoCFRelease<CTFontRef> ct(CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr));
2429 AutoCFRelease<CFArrayRef> ctAxes(CTFontCopyVariationAxes(ct));
2430 if (!ctAxes || CFArrayGetCount(ctAxes) != axisCount) {
2431 return nullptr;
2432 }
2433
2434 int paramAxisCount;
2435 const FontParameters::Axis* paramAxes = params.getAxes(&paramAxisCount);
2436
2437 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2438 &kCFTypeDictionaryKeyCallBacks,
2439 &kCFTypeDictionaryValueCallBacks);
2440 for (int i = 0; i < axisCount; ++i) {
2441 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i);
2442 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
2443 return nullptr;
2444 }
2445 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2446
2447 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2448 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
2449 return nullptr;
2450 }
2451
2452 CFNumberRef tagNumber = get_tag_for_name(static_cast<CFStringRef>(axisName), ctAxes);
2453 if (!tagNumber) {
2454 // Could not find a tag to go with the name of this index.
2455 // This would be a bug in CG/CT.
2456 continue;
2457 }
2458 int64_t tagLong;
2459 if (!CFNumberGetValue(tagNumber, kCFNumberSInt64Type, &tagLong)) {
2460 return nullptr;
2461 }
2462
2463 // The variation axes can be set to any value, but cg will effectively pin them.
2464 // Pin them here to normalize.
2465 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2466 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2467 CFTypeRef def = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisDefaultValue);
2468 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2469 !max || CFGetTypeID(max) != CFNumberGetTypeID() ||
2470 !def || CFGetTypeID(def) != CFNumberGetTypeID())
2471 {
2472 return nullptr;
2473 }
2474 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2475 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2476 CFNumberRef defNumber = static_cast<CFNumberRef>(def);
2477 double minDouble;
2478 double maxDouble;
2479 double defDouble;
2480 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2481 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble) ||
2482 !CFNumberGetValue(defNumber, kCFNumberDoubleType, &defDouble))
2483 {
2484 return nullptr;
2485 }
2486
2487 double value = defDouble;
2488 for (int j = 0; j < paramAxisCount; ++j) {
2489 if (paramAxes[j].fTag == tagLong) {
2490 value = SkTPin(SkScalarToDouble(paramAxes[j].fStyleValue),minDouble,maxDouble);
2491 break;
2492 }
2493 }
2494 CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType,
2495 &value);
2496 CFDictionaryAddValue(dict, axisName, valueNumber);
2497 CFRelease(valueNumber);
2498 }
2499 return dict;
2500 }
2501 SkTypeface* onCreateFromStream(SkStreamAsset* s, const FontParameters& params) const override {
2502 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(s));
2503 if (nullptr == provider) {
2504 return nullptr;
2505 }
2506 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
2507 if (nullptr == cg) {
2508 return nullptr;
2509 }
2510
2511 AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, params));
2512 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2513 // created from a data provider does not appear to have any ownership of the underlying
2514 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
2515 AutoCFRelease<CGFontRef> cgVariant;
2516 if (cgVariations) {
2517 cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations));
2518 } else {
mtklein18300a32016-03-16 13:53:35 -07002519 cgVariant.reset(cg.release());
bungemanf6c71072016-01-21 14:17:47 -08002520 }
2521
2522 CTFontRef ct = CTFontCreateWithGraphicsFont(cgVariant, 0, nullptr, nullptr);
2523 if (!ct) {
2524 return nullptr;
2525 }
mtklein18300a32016-03-16 13:53:35 -07002526 return NewFromFontRef(ct, cg.release(), nullptr, true);
bungemanf6c71072016-01-21 14:17:47 -08002527 }
2528
bungeman41868fe2015-05-20 09:21:04 -07002529 static CFDictionaryRef get_axes(CGFontRef cg, SkFontData* fontData) {
2530 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
2531 if (!cgAxes) {
halcanary96fcdcc2015-08-27 07:41:13 -07002532 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002533 }
2534
2535 CFIndex axisCount = CFArrayGetCount(cgAxes);
2536 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002537 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002538 }
2539
2540 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2541 &kCFTypeDictionaryKeyCallBacks,
2542 &kCFTypeDictionaryValueCallBacks);
2543 for (int i = 0; i < fontData->getAxisCount(); ++i) {
2544 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i);
2545 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002546 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002547 }
2548 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2549
2550 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2551 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002552 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002553 }
2554
2555 // The variation axes can be set to any value, but cg will effectively pin them.
2556 // Pin them here to normalize.
2557 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2558 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2559 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2560 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2561 {
halcanary96fcdcc2015-08-27 07:41:13 -07002562 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002563 }
2564 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2565 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2566 double minDouble;
2567 double maxDouble;
2568 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2569 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2570 {
halcanary96fcdcc2015-08-27 07:41:13 -07002571 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002572 }
2573 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
2574 CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType,
2575 &value);
2576
2577 CFDictionaryAddValue(dict, axisName, valueNumber);
2578 CFRelease(valueNumber);
2579 }
2580 return dict;
2581 }
2582 SkTypeface* onCreateFromFontData(SkFontData* data) const override {
2583 SkAutoTDelete<SkFontData> fontData(data);
2584 SkStreamAsset* stream = fontData->detachStream();
2585 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
halcanary96fcdcc2015-08-27 07:41:13 -07002586 if (nullptr == provider) {
2587 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002588 }
2589 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
halcanary96fcdcc2015-08-27 07:41:13 -07002590 if (nullptr == cg) {
2591 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002592 }
2593
2594 AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, fontData));
2595 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2596 // created from a data provider does not appear to have any ownership of the underlying
2597 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
2598 AutoCFRelease<CGFontRef> cgVariant;
2599 if (cgVariations) {
2600 cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations));
2601 } else {
mtklein18300a32016-03-16 13:53:35 -07002602 cgVariant.reset(cg.release());
bungeman41868fe2015-05-20 09:21:04 -07002603 }
2604
halcanary96fcdcc2015-08-27 07:41:13 -07002605 CTFontRef ct = CTFontCreateWithGraphicsFont(cgVariant, 0, nullptr, nullptr);
bungeman41868fe2015-05-20 09:21:04 -07002606 if (!ct) {
halcanary96fcdcc2015-08-27 07:41:13 -07002607 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002608 }
mtklein18300a32016-03-16 13:53:35 -07002609 return NewFromFontRef(ct, cg.release(), nullptr, true);
bungeman41868fe2015-05-20 09:21:04 -07002610 }
2611
mtklein36352bf2015-03-25 18:17:31 -07002612 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002613 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
halcanary96fcdcc2015-08-27 07:41:13 -07002614 if (nullptr == pr) {
2615 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002616 }
2617 return create_from_dataProvider(pr);
2618 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002619
reed@google.com7fdcd442013-07-30 21:25:49 +00002620 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002621 unsigned styleBits) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002622
2623 SkFontStyle style = SkFontStyle((SkTypeface::Style)styleBits);
2624 if (familyName) {
2625 familyName = map_css_names(familyName);
2626 }
2627
2628 if (!familyName || !*familyName) {
2629 familyName = FONT_DEFAULT_NAME;
2630 }
2631
bungeman967937c2014-10-30 11:49:27 -07002632 NameStyle cacheRequest = { familyName, style };
2633 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_NameStyle, &cacheRequest);
bungemana4c4a2d2014-10-20 13:33:19 -07002634
halcanary96fcdcc2015-08-27 07:41:13 -07002635 if (nullptr == face) {
bungemana4c4a2d2014-10-20 13:33:19 -07002636 face = NewFromName(familyName, style);
2637 if (face) {
2638 SkTypefaceCache::Add(face, style);
2639 } else {
2640 face = GetDefaultFace();
2641 face->ref();
2642 }
2643 }
2644 return face;
reed@google.com7fdcd442013-07-30 21:25:49 +00002645 }
reed@google.com95625db2013-03-25 20:44:02 +00002646};
2647
reed@google.com7fdcd442013-07-30 21:25:49 +00002648///////////////////////////////////////////////////////////////////////////////
2649
halcanary385fe4d2015-08-26 13:07:48 -07002650SkFontMgr* SkFontMgr::Factory() { return new SkFontMgr_Mac; }
mtklein1ee76512015-11-02 10:20:27 -08002651
2652#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)