blob: 25030be4132952eba43469649ef24c5e3827f716 [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"
45#include "SkTypefaceCache.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000046#include "SkTypeface_mac.h"
47#include "SkUtils.h"
reed@google.combcb42ae2013-07-02 13:56:39 +000048#include "SkUtils.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000049
bungeman34902632014-12-10 21:43:27 -080050#include <dlfcn.h>
reed@google.comf77b35d2013-05-02 20:39:44 +000051
reedd0f41732015-07-10 12:08:38 -070052// Experimental code to use a global lock whenever we access CG, to see if this reduces
53// crashes in Chrome
54#define USE_GLOBAL_MUTEX_FOR_CG_ACCESS
55
56#ifdef USE_GLOBAL_MUTEX_FOR_CG_ACCESS
mtklein4598fc32015-07-13 06:15:36 -070057 SK_DECLARE_STATIC_MUTEX(gCGMutex);
reedd0f41732015-07-10 12:08:38 -070058 #define AUTO_CG_LOCK() SkAutoMutexAcquire amc(gCGMutex)
59#else
60 #define AUTO_CG_LOCK()
61#endif
62
bungeman3b4b66c2015-01-08 08:33:44 -080063// Set to make glyph bounding boxes visible.
64#define SK_SHOW_TEXT_BLIT_COVERAGE 0
65
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000066class SkScalerContext_Mac;
67
reed@google.com3dcbd462013-03-27 13:56:34 +000068// CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
69// provide a wrapper here that will return an empty array if need be.
70static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() {
71#ifdef SK_BUILD_FOR_IOS
halcanary96fcdcc2015-08-27 07:41:13 -070072 return CFArrayCreate(nullptr, nullptr, 0, nullptr);
reed@google.com3dcbd462013-03-27 13:56:34 +000073#else
74 return CTFontManagerCopyAvailableFontFamilyNames();
75#endif
76}
77
78
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000079// Being templated and taking const T* prevents calling
80// CFSafeRelease(autoCFRelease) through implicit conversion.
81template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
82 if (cfTypeRef) {
83 CFRelease(cfTypeRef);
84 }
85}
86
87// Being templated and taking const T* prevents calling
88// CFSafeRetain(autoCFRelease) through implicit conversion.
89template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
90 if (cfTypeRef) {
91 CFRetain(cfTypeRef);
92 }
93}
94
95/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
96template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
97public:
halcanary96fcdcc2015-08-27 07:41:13 -070098 explicit AutoCFRelease(CFRef cfRef = nullptr) : fCFRef(cfRef) { }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000099 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
100
halcanary96fcdcc2015-08-27 07:41:13 -0700101 void reset(CFRef that = nullptr) {
ljagielskie9d2d092014-07-15 20:02:04 -0700102 if (that != fCFRef) {
103 CFSafeRelease(fCFRef);
104 fCFRef = that;
105 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000106 }
107
bungeman64fb51d2015-05-04 12:03:50 -0700108 CFRef detach() {
109 CFRef self = fCFRef;
halcanary96fcdcc2015-08-27 07:41:13 -0700110 fCFRef = nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700111 return self;
112 }
113
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000114 operator CFRef() const { return fCFRef; }
115 CFRef get() const { return fCFRef; }
116
halcanary96fcdcc2015-08-27 07:41:13 -0700117 CFRef* operator&() { SkASSERT(fCFRef == nullptr); return &fCFRef; }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000118private:
119 CFRef fCFRef;
120};
121
reed@google.com964988f2013-03-29 14:57:22 +0000122static CFStringRef make_CFString(const char str[]) {
halcanary96fcdcc2015-08-27 07:41:13 -0700123 return CFStringCreateWithCString(nullptr, str, kCFStringEncodingUTF8);
reed@google.com964988f2013-03-29 14:57:22 +0000124}
125
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000126template<typename T> class AutoCGTable : SkNoncopyable {
127public:
128 AutoCGTable(CGFontRef font)
129 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
130 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
halcanary96fcdcc2015-08-27 07:41:13 -0700131 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : nullptr)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000132 { }
133
134 const T* operator->() const { return fData; }
135
136private:
137 AutoCFRelease<CFDataRef> fCFData;
138public:
139 const T* fData;
140};
141
142// inline versions of these rect helpers
143
144static bool CGRectIsEmpty_inline(const CGRect& rect) {
145 return rect.size.width <= 0 || rect.size.height <= 0;
146}
147
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000148static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
149 return rect.origin.x;
150}
151
152static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
153 return rect.origin.x + rect.size.width;
154}
155
156static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
157 return rect.origin.y;
158}
159
160static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
161 return rect.origin.y + rect.size.height;
162}
163
164static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
165 return rect.size.width;
166}
167
168///////////////////////////////////////////////////////////////////////////////
169
170static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
reed@google.com7fa2a652014-01-27 13:42:58 +0000171 int width, int height, size_t rowBytes) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000172 SkASSERT(width);
173 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
174
175 if (width >= 32) {
176 while (height) {
177 sk_memset32(ptr, value, width);
178 ptr = (uint32_t*)((char*)ptr + rowBytes);
179 height -= 1;
180 }
181 return;
182 }
183
184 rowBytes -= width * sizeof(uint32_t);
185
186 if (width >= 8) {
187 while (height) {
188 int w = width;
189 do {
190 *ptr++ = value; *ptr++ = value;
191 *ptr++ = value; *ptr++ = value;
192 *ptr++ = value; *ptr++ = value;
193 *ptr++ = value; *ptr++ = value;
194 w -= 8;
195 } while (w >= 8);
196 while (--w >= 0) {
197 *ptr++ = value;
198 }
199 ptr = (uint32_t*)((char*)ptr + rowBytes);
200 height -= 1;
201 }
202 } else {
203 while (height) {
204 int w = width;
205 do {
206 *ptr++ = value;
207 } while (--w > 0);
208 ptr = (uint32_t*)((char*)ptr + rowBytes);
209 height -= 1;
210 }
211 }
212}
213
214#include <sys/utsname.h>
215
216typedef uint32_t CGRGBPixel;
217
218static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
219 return pixel & 0xFF;
220}
221
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000222static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
223
224// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
225static int readVersion() {
226 struct utsname info;
227 if (uname(&info) != 0) {
228 SkDebugf("uname failed\n");
229 return 0;
230 }
231 if (strcmp(info.sysname, "Darwin") != 0) {
232 SkDebugf("unexpected uname sysname %s\n", info.sysname);
233 return 0;
234 }
235 char* dot = strchr(info.release, '.');
236 if (!dot) {
237 SkDebugf("expected dot in uname release %s\n", info.release);
238 return 0;
239 }
240 int version = atoi(info.release);
241 if (version == 0) {
242 SkDebugf("could not parse uname release %s\n", info.release);
243 }
244 return version;
245}
246
247static int darwinVersion() {
248 static int darwin_version = readVersion();
249 return darwin_version;
250}
251
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000252static bool isSnowLeopard() {
253 return darwinVersion() == 10;
254}
255
256static bool isLion() {
257 return darwinVersion() == 11;
258}
259
260static bool isMountainLion() {
261 return darwinVersion() == 12;
262}
263
264static bool isLCDFormat(unsigned format) {
reedd54d3fc2014-11-13 14:39:58 -0800265 return SkMask::kLCD16_Format == format;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000266}
267
268static CGFloat ScalarToCG(SkScalar scalar) {
269 if (sizeof(CGFloat) == sizeof(float)) {
270 return SkScalarToFloat(scalar);
271 } else {
272 SkASSERT(sizeof(CGFloat) == sizeof(double));
273 return (CGFloat) SkScalarToDouble(scalar);
274 }
275}
276
277static SkScalar CGToScalar(CGFloat cgFloat) {
278 if (sizeof(CGFloat) == sizeof(float)) {
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000279 return cgFloat;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000280 } else {
281 SkASSERT(sizeof(CGFloat) == sizeof(double));
282 return SkDoubleToScalar(cgFloat);
283 }
284}
285
bungeman4de8c3a2015-09-18 23:03:24 -0700286static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
287 SkScalar sx = SK_Scalar1,
288 SkScalar sy = SK_Scalar1) {
289 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
290 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
291 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
292 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
293 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
294 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000295}
296
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000297///////////////////////////////////////////////////////////////////////////////
298
299#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000300
301/**
302 * There does not appear to be a publicly accessable API for determining if lcd
303 * font smoothing will be applied if we request it. The main issue is that if
304 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
305 */
306static bool supports_LCD() {
307 static int gSupportsLCD = -1;
308 if (gSupportsLCD >= 0) {
309 return (bool) gSupportsLCD;
310 }
311 uint32_t rgb = 0;
312 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
313 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
314 colorspace, BITMAP_INFO_RGB));
315 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
316 CGContextSetShouldSmoothFonts(cgContext, true);
317 CGContextSetShouldAntialias(cgContext, true);
318 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
319 CGContextSetGrayFillColor(cgContext, 1, 1);
320 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
321 uint32_t r = (rgb >> 16) & 0xFF;
322 uint32_t g = (rgb >> 8) & 0xFF;
323 uint32_t b = (rgb >> 0) & 0xFF;
324 gSupportsLCD = (r != g || r != b);
325 return (bool) gSupportsLCD;
326}
327
328class Offscreen {
329public:
bungeman34902632014-12-10 21:43:27 -0800330 Offscreen()
halcanary96fcdcc2015-08-27 07:41:13 -0700331 : fRGBSpace(nullptr)
332 , fCG(nullptr)
bungeman34902632014-12-10 21:43:27 -0800333 , fDoAA(false)
334 , fDoLCD(false)
335 {
336 fSize.set(0, 0);
337 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000338
339 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
340 CGGlyph glyphID, size_t* rowBytesPtr,
341 bool generateA8FromLCD);
342
343private:
344 enum {
345 kSize = 32 * 32 * sizeof(CGRGBPixel)
346 };
347 SkAutoSMalloc<kSize> fImageStorage;
348 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
349
350 // cached state
351 AutoCFRelease<CGContextRef> fCG;
352 SkISize fSize;
353 bool fDoAA;
354 bool fDoLCD;
355
356 static int RoundSize(int dimension) {
357 return SkNextPow2(dimension);
358 }
359};
360
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000361///////////////////////////////////////////////////////////////////////////////
362
bungemana4c4a2d2014-10-20 13:33:19 -0700363static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
364 CFNumberRef num;
365 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
366 && CFNumberIsFloatType(num)
367 && CFNumberGetValue(num, kCFNumberFloatType, value);
368}
369
370static int unit_weight_to_fontstyle(float unit) {
371 float value;
372 if (unit < 0) {
373 value = 100 + (1 + unit) * 300;
374 } else {
375 value = 400 + unit * 500;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000376 }
bungemana4c4a2d2014-10-20 13:33:19 -0700377 return sk_float_round2int(value);
378}
379
380static int unit_width_to_fontstyle(float unit) {
381 float value;
382 if (unit < 0) {
383 value = 1 + (1 + unit) * 4;
384 } else {
385 value = 5 + unit * 4;
386 }
387 return sk_float_round2int(value);
388}
389
bungeman336fdf22014-11-10 07:48:55 -0800390static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc) {
bungemana4c4a2d2014-10-20 13:33:19 -0700391 AutoCFRelease<CFDictionaryRef> dict(
392 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
halcanary96fcdcc2015-08-27 07:41:13 -0700393 if (nullptr == dict.get()) {
bungemana4c4a2d2014-10-20 13:33:19 -0700394 return SkFontStyle();
395 }
396
bungemana4c4a2d2014-10-20 13:33:19 -0700397 float weight, width, slant;
398 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800399 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700400 }
401 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
402 width = 0;
403 }
404 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800405 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700406 }
407
408 return SkFontStyle(unit_weight_to_fontstyle(weight),
409 unit_width_to_fontstyle(width),
410 slant ? SkFontStyle::kItalic_Slant
411 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000412}
413
bungeman336fdf22014-11-10 07:48:55 -0800414static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) {
415 unsigned style = SkTypeface::kNormal;
416 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
417
418 if (traits & kCTFontBoldTrait) {
419 style |= SkTypeface::kBold;
420 }
421 if (traits & kCTFontItalicTrait) {
422 style |= SkTypeface::kItalic;
423 }
424 if (isFixedPitch) {
425 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0;
426 }
427 return (SkTypeface::Style)style;
428}
429
reed@google.comce8b3de2013-03-26 19:30:16 +0000430#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
431
bungeman994e8182015-02-23 16:17:43 -0800432// kCTFontColorGlyphsTrait was added in the Mac 10.7 and iPhone 4.3 SDKs.
433// Being an enum value it is not guarded by version macros, but old SDKs must still be supported.
434#if defined(__MAC_10_7) || defined(__IPHONE_4_3)
435static const uint32_t SkCTFontColorGlyphsTrait = kCTFontColorGlyphsTrait;
436#else
437static const uint32_t SkCTFontColorGlyphsTrait = (1 << 13);
438#endif
439
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000440class SkTypeface_Mac : public SkTypeface {
441public:
bungeman78884012015-06-08 13:39:12 -0700442 SkTypeface_Mac(CTFontRef fontRef, CFTypeRef resourceRef,
443 const SkFontStyle& fs, bool isFixedPitch,
444 const char requestedName[], bool isLocalStream)
bungeman64fb51d2015-05-04 12:03:50 -0700445 : SkTypeface(fs, SkTypefaceCache::NewFontID(), isFixedPitch)
bungeman967937c2014-10-30 11:49:27 -0700446 , fRequestedName(requestedName)
mtklein802ad832014-10-20 12:54:31 -0700447 , fFontRef(fontRef) // caller has already called CFRetain for us
bungeman78884012015-06-08 13:39:12 -0700448 , fOriginatingCFTypeRef(resourceRef) // caller has already called CFRetain for us
bungemane3bea5c2015-04-07 07:34:36 -0700449 , fHasColorGlyphs(SkToBool(CTFontGetSymbolicTraits(fFontRef) & SkCTFontColorGlyphsTrait))
caseq26337e92014-06-30 12:14:52 -0700450 , fIsLocalStream(isLocalStream)
reed@google.comce8b3de2013-03-26 19:30:16 +0000451 {
452 SkASSERT(fontRef);
453 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000454
bungeman967937c2014-10-30 11:49:27 -0700455 SkString fRequestedName;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000456 AutoCFRelease<CTFontRef> fFontRef;
bungeman78884012015-06-08 13:39:12 -0700457 AutoCFRelease<CFTypeRef> fOriginatingCFTypeRef;
bungemane3bea5c2015-04-07 07:34:36 -0700458 const bool fHasColorGlyphs;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000459
460protected:
mtklein36352bf2015-03-25 18:17:31 -0700461 int onGetUPEM() const override;
462 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
bungeman41868fe2015-05-20 09:21:04 -0700463 SkFontData* onCreateFontData() const override;
mtklein36352bf2015-03-25 18:17:31 -0700464 void onGetFamilyName(SkString* familyName) const override;
465 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
466 int onGetTableTags(SkFontTableTag tags[]) const override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000467 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
mtklein36352bf2015-03-25 18:17:31 -0700468 size_t length, void* data) const override;
469 SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override;
470 void onFilterRec(SkScalerContextRec*) const override;
471 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
reed@google.com2689f612013-03-20 20:01:47 +0000472 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -0700473 PerGlyphInfo,
mtklein36352bf2015-03-25 18:17:31 -0700474 const uint32_t*, uint32_t) const override;
reed@google.combcb42ae2013-07-02 13:56:39 +0000475 virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
mtklein36352bf2015-03-25 18:17:31 -0700476 int glyphCount) const override;
477 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000478
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000479private:
caseq26337e92014-06-30 12:14:52 -0700480 bool fIsLocalStream;
reed@google.comce8b3de2013-03-26 19:30:16 +0000481
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000482 typedef SkTypeface INHERITED;
483};
484
bungeman64fb51d2015-05-04 12:03:50 -0700485/** Creates a typeface without searching the cache. Takes ownership of the CTFontRef. */
bungeman78884012015-06-08 13:39:12 -0700486static SkTypeface* NewFromFontRef(CTFontRef fontRef, CFTypeRef resourceRef,
487 const char name[], bool isLocalStream)
bungeman41868fe2015-05-20 09:21:04 -0700488{
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000489 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000490 bool isFixedPitch;
bungeman336fdf22014-11-10 07:48:55 -0800491 SkFontStyle style = SkFontStyle(computeStyleBits(fontRef, &isFixedPitch));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000492
bungeman78884012015-06-08 13:39:12 -0700493 return new SkTypeface_Mac(fontRef, resourceRef, style, isFixedPitch, name, isLocalStream);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000494}
495
bungeman64fb51d2015-05-04 12:03:50 -0700496static bool find_by_CTFontRef(SkTypeface* cached, const SkFontStyle&, void* context) {
497 CTFontRef self = (CTFontRef)context;
498 CTFontRef other = ((SkTypeface_Mac*)cached)->fFontRef;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000499
bungeman64fb51d2015-05-04 12:03:50 -0700500 return CFEqual(self, other);
501}
502
503/** Creates a typeface from a name, searching the cache. */
504static SkTypeface* NewFromName(const char familyName[], const SkFontStyle& theStyle) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000505 CTFontSymbolicTraits ctFontTraits = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700506 if (theStyle.weight() >= SkFontStyle::kBold_Weight) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000507 ctFontTraits |= kCTFontBoldTrait;
508 }
bungemana4c4a2d2014-10-20 13:33:19 -0700509 if (theStyle.slant() != SkFontStyle::kUpright_Slant) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000510 ctFontTraits |= kCTFontItalicTrait;
511 }
512
bungemana4c4a2d2014-10-20 13:33:19 -0700513 //TODO: add weight width slant
514
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000515 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000516 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000517
518 AutoCFRelease<CFNumberRef> cfFontTraits(
519 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
520
521 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
522 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
523 &kCFTypeDictionaryKeyCallBacks,
524 &kCFTypeDictionaryValueCallBacks));
525
526 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
527 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
528 &kCFTypeDictionaryKeyCallBacks,
529 &kCFTypeDictionaryValueCallBacks));
530
bungeman64fb51d2015-05-04 12:03:50 -0700531 if (!cfFontName || !cfFontTraits || !cfAttributes || !cfTraits) {
halcanary96fcdcc2015-08-27 07:41:13 -0700532 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000533 }
534
bungeman64fb51d2015-05-04 12:03:50 -0700535 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
536
537 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
538 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
539
540 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
541 CTFontDescriptorCreateWithAttributes(cfAttributes));
542 if (!ctFontDesc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700543 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700544 }
545
halcanary96fcdcc2015-08-27 07:41:13 -0700546 AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(ctFontDesc, 0, nullptr));
bungeman64fb51d2015-05-04 12:03:50 -0700547 if (!ctFont) {
halcanary96fcdcc2015-08-27 07:41:13 -0700548 return nullptr;
bungeman64fb51d2015-05-04 12:03:50 -0700549 }
550
551 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)ctFont.get());
552 if (!face) {
halcanary96fcdcc2015-08-27 07:41:13 -0700553 face = NewFromFontRef(ctFont.detach(), nullptr, nullptr, false);
bungeman64fb51d2015-05-04 12:03:50 -0700554 SkTypefaceCache::Add(face, face->fontStyle());
555 }
556 return face;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000557}
558
bungemand6aeb6d2014-07-25 11:52:47 -0700559SK_DECLARE_STATIC_MUTEX(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000560static SkTypeface* GetDefaultFace() {
bungemand6aeb6d2014-07-25 11:52:47 -0700561 SkAutoMutexAcquire ma(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000562
563 static SkTypeface* gDefaultFace;
564
halcanary96fcdcc2015-08-27 07:41:13 -0700565 if (nullptr == gDefaultFace) {
bungemana4c4a2d2014-10-20 13:33:19 -0700566 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkFontStyle());
567 SkTypefaceCache::Add(gDefaultFace, SkFontStyle());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000568 }
569 return gDefaultFace;
570}
571
572///////////////////////////////////////////////////////////////////////////////
573
574extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
575CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
576 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
halcanary96fcdcc2015-08-27 07:41:13 -0700577 return macface ? macface->fFontRef.get() : nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000578}
579
580/* This function is visible on the outside. It first searches the cache, and if
581 * not found, returns a new entry (after adding it to the cache).
582 */
bungeman78884012015-06-08 13:39:12 -0700583SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef, CFTypeRef resourceRef) {
bungeman64fb51d2015-05-04 12:03:50 -0700584 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_CTFontRef, (void*)fontRef);
585 if (!face) {
586 CFRetain(fontRef);
bungeman78884012015-06-08 13:39:12 -0700587 if (resourceRef) {
588 CFRetain(resourceRef);
589 }
halcanary96fcdcc2015-08-27 07:41:13 -0700590 face = NewFromFontRef(fontRef, resourceRef, nullptr, false);
bungemana4c4a2d2014-10-20 13:33:19 -0700591 SkTypefaceCache::Add(face, face->fontStyle());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000592 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000593 return face;
594}
595
bungeman967937c2014-10-30 11:49:27 -0700596struct NameStyle {
597 const char* fName;
598 SkFontStyle fStyle;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000599};
600
bungeman967937c2014-10-30 11:49:27 -0700601static bool find_by_NameStyle(SkTypeface* cachedFace, const SkFontStyle& cachedStyle, void* ctx) {
602 const SkTypeface_Mac* cachedMacFace = static_cast<SkTypeface_Mac*>(cachedFace);
603 const NameStyle* requested = static_cast<const NameStyle*>(ctx);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000604
bungeman967937c2014-10-30 11:49:27 -0700605 return cachedStyle == requested->fStyle
606 && cachedMacFace->fRequestedName.equals(requested->fName);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000607}
608
609static const char* map_css_names(const char* name) {
610 static const struct {
611 const char* fFrom; // name the caller specified
612 const char* fTo; // "canonical" name we map to
613 } gPairs[] = {
614 { "sans-serif", "Helvetica" },
615 { "serif", "Times" },
616 { "monospace", "Courier" }
617 };
618
619 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
620 if (strcmp(name, gPairs[i].fFrom) == 0) {
621 return gPairs[i].fTo;
622 }
623 }
624 return name; // no change
625}
626
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000627///////////////////////////////////////////////////////////////////////////////
628
bungeman@google.comcefd9812013-05-15 15:07:32 +0000629/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000630struct GlyphRect {
631 int16_t fMinX;
632 int16_t fMinY;
633 int16_t fMaxX;
634 int16_t fMaxY;
635};
636
637class SkScalerContext_Mac : public SkScalerContext {
638public:
reed@google.com0da48612013-03-19 16:06:52 +0000639 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000640
641protected:
mtklein36352bf2015-03-25 18:17:31 -0700642 unsigned generateGlyphCount(void) override;
643 uint16_t generateCharToGlyph(SkUnichar uni) override;
644 void generateAdvance(SkGlyph* glyph) override;
645 void generateMetrics(SkGlyph* glyph) override;
646 void generateImage(const SkGlyph& glyph) override;
647 void generatePath(const SkGlyph& glyph, SkPath* path) override;
648 void generateFontMetrics(SkPaint::FontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000649
650private:
651 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000652
bungeman@google.comcefd9812013-05-15 15:07:32 +0000653 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
654 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000655
656 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
657 *
658 * For use with (and must be called before) generateBBoxes.
659 */
660 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000661
bungeman@google.comcefd9812013-05-15 15:07:32 +0000662 /** Initializes fFBoundingBoxes and returns true on success.
663 *
664 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
665 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
666 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
667 * font directly.
668 *
669 * This routine initializes fFBoundingBoxes to an array of
670 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
671 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
672 *
673 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
674 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
675 *
676 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
677 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000678 bool generateBBoxes();
679
bungeman@google.comcefd9812013-05-15 15:07:32 +0000680 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
681 *
682 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
683 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
684 */
685 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000686
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000687 Offscreen fOffscreen;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000688
bungeman3b4b66c2015-01-08 08:33:44 -0800689 /** Unrotated variant of fCTFont.
690 *
691 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
692 * advances, but always sets the height to 0. This font is used to get the advances of the
693 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000694 *
695 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
696 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
697 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
bungeman3b4b66c2015-01-08 08:33:44 -0800698 * With kCTFontVerticalOrientation the advances must be unrotated.
bungemanef27ce32015-10-29 09:30:32 -0700699 *
700 * Sometimes, creating a copy of a CTFont with the same size but different trasform will select
701 * different underlying font data. As a result, avoid ever creating more than one CTFont per
702 * SkScalerContext to ensure that only one CTFont is used.
703 *
704 * As a result of the above (and other constraints) this font contains the size, but not the
705 * transform. The transform must always be applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000706 */
bungemanef27ce32015-10-29 09:30:32 -0700707 AutoCFRelease<CTFontRef> fCTFont;
708
709 /** The transform without the font size. */
710 CGAffineTransform fTransform;
711 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000712
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000713 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000714 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000715 uint16_t fFBoundingBoxesGlyphOffset;
716 uint16_t fGlyphCount;
717 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000718 const bool fDoSubPosition;
719 const bool fVertical;
720
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000721 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000722
723 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000724};
725
bungeman7cbeaae2015-09-22 09:54:56 -0700726// CTFontCreateCopyWithAttributes or CTFontCreateCopyWithSymbolicTraits cannot be used on 10.10
bungeman05846312015-09-23 12:51:28 -0700727// and later, as they will return different underlying fonts depending on the size requested.
728// It is not possible to use descriptors with CTFontCreateWithFontDescriptor, since that does not
729// work with non-system fonts. As a result, create the strike specific CTFonts from the underlying
730// CGFont.
bungeman7cbeaae2015-09-22 09:54:56 -0700731static CTFontRef ctfont_create_exact_copy(CTFontRef baseFont, CGFloat textSize,
bungeman05846312015-09-23 12:51:28 -0700732 const CGAffineTransform* transform)
bungeman7cbeaae2015-09-22 09:54:56 -0700733{
bungeman05846312015-09-23 12:51:28 -0700734 AutoCFRelease<CGFontRef> baseCGFont(CTFontCopyGraphicsFont(baseFont, nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -0700735
bungeman05846312015-09-23 12:51:28 -0700736 // The last parameter (CTFontDescriptorRef attributes) *must* be nullptr.
737 // If non-nullptr then with fonts with variation axes, the copy will fail in
738 // CGFontVariationFromDictCallback when it assumes kCGFontVariationAxisName is CFNumberRef
739 // which it quite obviously is not.
bungeman7cbeaae2015-09-22 09:54:56 -0700740
bungeman05846312015-09-23 12:51:28 -0700741 // Because we cannot setup the CTFont descriptor to match, the same restriction applies here
742 // as other uses of CTFontCreateWithGraphicsFont which is that such CTFonts should not escape
743 // the scaler context, since they aren't 'normal'.
744 return CTFontCreateWithGraphicsFont(baseCGFont, textSize, transform, nullptr);
bungeman7cbeaae2015-09-22 09:54:56 -0700745}
746
reed@google.com0da48612013-03-19 16:06:52 +0000747SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
748 const SkDescriptor* desc)
749 : INHERITED(typeface, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000750 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000751 , fFBoundingBoxesGlyphOffset(0)
752 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000753 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
754 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
755
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000756{
reedd0f41732015-07-10 12:08:38 -0700757 AUTO_CG_LOCK();
758
reed@google.com2689f612013-03-20 20:01:47 +0000759 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000760 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000761 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
762 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000763
bungeman34902632014-12-10 21:43:27 -0800764 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
765 // As a result, it is necessary to know the actual device size and request that.
766 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800767 SkMatrix skTransform;
bungeman34902632014-12-10 21:43:27 -0800768 fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, &skTransform,
halcanary96fcdcc2015-08-27 07:41:13 -0700769 nullptr, nullptr, &fFUnitMatrix);
bungemanaae30912015-03-02 13:43:26 -0800770 fTransform = MatrixToCGAffineTransform(skTransform);
771 fInvTransform = CGAffineTransformInvert(fTransform);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000772
bungemanbe2284d2014-11-25 08:08:09 -0800773 // The transform contains everything except the requested text size.
774 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -0800775 CGFloat textSize = ScalarToCG(scale.y());
bungemanef27ce32015-10-29 09:30:32 -0700776 fCTFont.reset(ctfont_create_exact_copy(ctFont, textSize, nullptr));
halcanary96fcdcc2015-08-27 07:41:13 -0700777 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont, nullptr));
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000778
bungemanbe2284d2014-11-25 08:08:09 -0800779 // 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 +0000780 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
781 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000782}
783
mtkleinbbd40182015-09-08 08:19:33 -0700784/** This is an implementation of CTFontDrawGlyphs for 10.6; it was introduced in 10.7. */
785static void legacy_CTFontDrawGlyphs(CTFontRef, const CGGlyph glyphs[], const CGPoint points[],
786 size_t count, CGContextRef cg) {
bungeman34902632014-12-10 21:43:27 -0800787 CGContextShowGlyphsAtPositions(cg, glyphs, points, count);
788}
789
mtkleinbbd40182015-09-08 08:19:33 -0700790typedef decltype(legacy_CTFontDrawGlyphs) CTFontDrawGlyphsProc;
791
792static CTFontDrawGlyphsProc* choose_CTFontDrawGlyphs() {
793 if (void* real = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs")) {
794 return (CTFontDrawGlyphsProc*)real;
795 }
796 return &legacy_CTFontDrawGlyphs;
bungeman34902632014-12-10 21:43:27 -0800797}
798
mtkleinbbd40182015-09-08 08:19:33 -0700799SK_DECLARE_STATIC_ONCE_PTR(CTFontDrawGlyphsProc, gCTFontDrawGlyphs);
bungeman34902632014-12-10 21:43:27 -0800800
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000801CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
802 CGGlyph glyphID, size_t* rowBytesPtr,
mtkleinbbd40182015-09-08 08:19:33 -0700803 bool generateA8FromLCD) {
804 auto ctFontDrawGlyphs = gCTFontDrawGlyphs.get(choose_CTFontDrawGlyphs);
bungeman34902632014-12-10 21:43:27 -0800805
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000806 if (!fRGBSpace) {
807 //It doesn't appear to matter what color space is specified.
808 //Regular blends and antialiased text are always (s*a + d*(1-a))
809 //and smoothed text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -0700810 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000811 }
812
813 // default to kBW_Format
814 bool doAA = false;
815 bool doLCD = false;
816
817 if (SkMask::kBW_Format != glyph.fMaskFormat) {
818 doLCD = true;
819 doAA = true;
820 }
821
822 // FIXME: lcd smoothed un-hinted rasterization unsupported.
823 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
824 doLCD = false;
825 doAA = true;
826 }
827
bungeman34902632014-12-10 21:43:27 -0800828 // If this font might have color glyphs, disable LCD as there's no way to support it.
829 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
830 // A8 will be ugly too (white on transparent), but TODO: we can detect gray and set to A8.
831 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
832 doLCD = false;
833 }
834
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000835 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
836 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
837 if (fSize.fWidth < glyph.fWidth) {
838 fSize.fWidth = RoundSize(glyph.fWidth);
839 }
840 if (fSize.fHeight < glyph.fHeight) {
841 fSize.fHeight = RoundSize(glyph.fHeight);
842 }
843
844 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
845 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -0800846 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
847 ? kCGImageAlphaPremultipliedFirst
848 : kCGImageAlphaNoneSkipFirst;
849 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -0700850 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungeman34902632014-12-10 21:43:27 -0800851 rowBytes, fRGBSpace, bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000852
bungeman34902632014-12-10 21:43:27 -0800853 // Skia handles quantization and subpixel positioning,
854 // so disable quantization and enabe subpixel positioning in CG.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000855 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
856 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
857
bungeman@google.comcefd9812013-05-15 15:07:32 +0000858 // Because CG always draws from the horizontal baseline,
859 // if there is a non-integral translation from the horizontal origin to the vertical origin,
860 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungeman34902632014-12-10 21:43:27 -0800861 CGContextSetAllowsFontSubpixelPositioning(fCG, true);
862 CGContextSetShouldSubpixelPositionFonts(fCG, true);
863
864 CGContextSetTextDrawingMode(fCG, kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000865
866 // Draw white on black to create mask.
867 // TODO: Draw black on white and invert, CG has a special case codepath.
868 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
869
870 // force our checks below to happen
871 fDoAA = !doAA;
872 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -0800873
mtkleinbbd40182015-09-08 08:19:33 -0700874 if (legacy_CTFontDrawGlyphs == ctFontDrawGlyphs) {
bungeman34902632014-12-10 21:43:27 -0800875 // CTFontDrawGlyphs will apply the font, font size, and font matrix to the CGContext.
876 // Our 'fake' one does not, so set up the CGContext here.
877 CGContextSetFont(fCG, context.fCGFont);
878 CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont));
bungeman34902632014-12-10 21:43:27 -0800879 }
bungemanaae30912015-03-02 13:43:26 -0800880 CGContextSetTextMatrix(fCG, context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000881 }
882
883 if (fDoAA != doAA) {
884 CGContextSetShouldAntialias(fCG, doAA);
885 fDoAA = doAA;
886 }
887 if (fDoLCD != doLCD) {
888 CGContextSetShouldSmoothFonts(fCG, doLCD);
889 fDoLCD = doLCD;
890 }
891
892 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
893 // skip rows based on the glyph's height
894 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
895
896 // erase to black
897 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
898
899 float subX = 0;
900 float subY = 0;
901 if (context.fDoSubPosition) {
902 subX = SkFixedToFloat(glyph.getSubXFixed());
903 subY = SkFixedToFloat(glyph.getSubYFixed());
904 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000905
bungeman34902632014-12-10 21:43:27 -0800906 // CoreText and CoreGraphics always draw using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000907 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000908 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000909 context.getVerticalOffset(glyphID, &offset);
910 subX += offset.fX;
911 subY += offset.fY;
912 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000913
bungeman34902632014-12-10 21:43:27 -0800914 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -0800915 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
916 // 'positions' which are in text space. The glyph location (in device space) must be
917 // mapped into text space, so that CG can convert it back into device space.
918 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
bungeman1b5f25c2015-06-08 14:32:24 -0700919 //
bungemanaae30912015-03-02 13:43:26 -0800920 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
921 // So always make the font transform identity and place the transform on the context.
922 point = CGPointApplyAffineTransform(point, context.fInvTransform);
923
bungemanef27ce32015-10-29 09:30:32 -0700924 ctFontDrawGlyphs(context.fCTFont, &glyphID, &point, 1, fCG);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000925
926 SkASSERT(rowBytesPtr);
927 *rowBytesPtr = rowBytes;
928 return image;
929}
930
bungeman@google.comcefd9812013-05-15 15:07:32 +0000931void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
932 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
933 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
934 CGSize cgVertOffset;
935 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000936 if (isSnowLeopard()) {
bungemanef27ce32015-10-29 09:30:32 -0700937 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
bungeman@google.comcefd9812013-05-15 15:07:32 +0000938 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
939 fFUnitMatrix.mapPoints(&skVertOffset, 1);
bungemanef27ce32015-10-29 09:30:32 -0700940 *offset = skVertOffset;
941 return;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000942 }
bungemanef27ce32015-10-29 09:30:32 -0700943 cgVertOffset = CGSizeApplyAffineTransform(cgVertOffset, fTransform);
944 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
945 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
946 skVertOffset.fY = -skVertOffset.fY;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000947 *offset = skVertOffset;
948}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000949
950uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
951 if (fFBoundingBoxesGlyphOffset) {
952 return fFBoundingBoxesGlyphOffset;
953 }
954 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
955 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
956 if (hheaTable.fData) {
957 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
958 }
959 return fFBoundingBoxesGlyphOffset;
960}
reed@android.com0680d6c2008-12-19 19:46:15 +0000961
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000962bool SkScalerContext_Mac::generateBBoxes() {
963 if (fGeneratedFBoundingBoxes) {
bsalomon49f085d2014-09-05 13:34:00 -0700964 return SkToBool(fFBoundingBoxes.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000965 }
966 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000967
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000968 AutoCGTable<SkOTTableHead> headTable(fCGFont);
969 if (!headTable.fData) {
970 return false;
971 }
972
973 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
974 if (!locaTable.fData) {
975 return false;
976 }
977
978 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
979 if (!glyfTable.fData) {
980 return false;
981 }
982
983 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000984 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000985
986 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
987 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
988 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
989 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
990 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
991 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
992 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
993 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
994 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
995 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
996 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000997
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000998 return true;
999}
1000
1001unsigned SkScalerContext_Mac::generateGlyphCount(void) {
1002 return fGlyphCount;
1003}
1004
1005uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
reedd0f41732015-07-10 12:08:38 -07001006 AUTO_CG_LOCK();
1007
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001008 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001009 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001010
1011 // Get the glyph
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001012 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar);
1013 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001014
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001015 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
1016 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1017 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
1018 CTFontGetGlyphsForCharacters(fCTFont, theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001019 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001020}
1021
1022void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
1023 this->generateMetrics(glyph);
1024}
1025
bungeman@google.comcefd9812013-05-15 15:07:32 +00001026void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
reedd0f41732015-07-10 12:08:38 -07001027 AUTO_CG_LOCK();
1028
djsollen1b277042014-08-06 06:58:06 -07001029 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +00001030 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001031
bungeman@google.comcefd9812013-05-15 15:07:32 +00001032 // The following block produces cgAdvance in CG units (pixels, y up).
1033 CGSize cgAdvance;
1034 if (fVertical) {
bungemanef27ce32015-10-29 09:30:32 -07001035 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontVerticalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001036 &cgGlyph, &cgAdvance, 1);
bungeman3b4b66c2015-01-08 08:33:44 -08001037 // Vertical advances are returned as widths instead of heights.
1038 SkTSwap(cgAdvance.height, cgAdvance.width);
1039 cgAdvance.height = -cgAdvance.height;
bungeman@google.comcefd9812013-05-15 15:07:32 +00001040 } else {
bungemanef27ce32015-10-29 09:30:32 -07001041 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001042 &cgGlyph, &cgAdvance, 1);
1043 }
bungemanef27ce32015-10-29 09:30:32 -07001044 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, fTransform);
bungeman@google.comcefd9812013-05-15 15:07:32 +00001045 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
1046 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001047
bungeman@google.comcefd9812013-05-15 15:07:32 +00001048 // The following produces skBounds in SkGlyph units (pixels, y down),
1049 // or returns early if skBounds would be empty.
1050 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001051
bungeman@google.comcefd9812013-05-15 15:07:32 +00001052 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
1053 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
1054 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
1055 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
1056 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
1057 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
1058 // to center the glyph along the vertical baseline and also perform some mysterious shift
1059 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
1060 // these steps.
1061 //
1062 // It is not known which is correct (or if either is correct). However, we must always draw
1063 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
1064 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001065
bungeman@google.comcefd9812013-05-15 15:07:32 +00001066 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
1067 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001068
bungeman@google.comcefd9812013-05-15 15:07:32 +00001069 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
1070 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
1071 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
1072 // font directly.
1073 if ((isLion() || isMountainLion()) &&
1074 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1075 {
1076 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1077 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1078 return;
1079 }
1080 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1081 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1082 fFUnitMatrix.mapRect(&skBounds);
1083
1084 } else {
1085 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1086 CGRect cgBounds;
1087 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1088 &cgGlyph, &cgBounds, 1);
bungemanef27ce32015-10-29 09:30:32 -07001089 cgBounds = CGRectApplyAffineTransform(cgBounds, fTransform);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001090
bungeman@google.comcefd9812013-05-15 15:07:32 +00001091 // BUG?
1092 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1093 // it should be empty. So, if we see a zero-advance, we check if it has an
1094 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1095 // is rare, so we won't incur a big performance cost for this extra check.
1096 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
halcanary96fcdcc2015-08-27 07:41:13 -07001097 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, nullptr));
1098 if (nullptr == path || CGPathIsEmpty(path)) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001099 return;
1100 }
1101 }
1102
1103 if (CGRectIsEmpty_inline(cgBounds)) {
1104 return;
1105 }
1106
1107 // Convert cgBounds to SkGlyph units (pixels, y down).
1108 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1109 cgBounds.size.width, cgBounds.size.height);
1110 }
1111
1112 if (fVertical) {
1113 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1114 // Convert these horizontal bounds into vertical bounds.
1115 SkPoint offset;
1116 getVerticalOffset(cgGlyph, &offset);
1117 skBounds.offset(offset);
1118 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001119
bungeman@google.comcefd9812013-05-15 15:07:32 +00001120 // Currently the bounds are based on being rendered at (0,0).
1121 // The top left must not move, since that is the base from which subpixel positioning is offset.
1122 if (fDoSubPosition) {
1123 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1124 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1125 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001126
bungeman@google.comcefd9812013-05-15 15:07:32 +00001127 SkIRect skIBounds;
1128 skBounds.roundOut(&skIBounds);
1129 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1130 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1131 // is not currently known, as CG dilates the outlines by some percentage.
1132 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1133 skIBounds.outset(1, 1);
1134 glyph->fLeft = SkToS16(skIBounds.fLeft);
1135 glyph->fTop = SkToS16(skIBounds.fTop);
1136 glyph->fWidth = SkToU16(skIBounds.width());
1137 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001138}
1139
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001140#include "SkColorPriv.h"
1141
1142static void build_power_table(uint8_t table[], float ee) {
1143 for (int i = 0; i < 256; i++) {
1144 float x = i / 255.f;
1145 x = sk_float_pow(x, ee);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001146 int xx = SkScalarRoundToInt(x * 255);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001147 table[i] = SkToU8(xx);
1148 }
1149}
1150
1151/**
1152 * This will invert the gamma applied by CoreGraphics, so we can get linear
1153 * values.
1154 *
1155 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1156 * The color space used does not appear to affect this choice.
1157 */
1158static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1159 static bool gInited;
1160 static uint8_t gTableCoreGraphicsSmoothing[256];
1161 if (!gInited) {
1162 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1163 gInited = true;
1164 }
1165 return gTableCoreGraphicsSmoothing;
1166}
1167
1168static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1169 while (count > 0) {
1170 uint8_t mask = 0;
1171 for (int i = 7; i >= 0; --i) {
1172 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1173 if (0 == --count) {
1174 break;
1175 }
1176 }
1177 *dst++ = mask;
1178 }
1179}
1180
1181template<bool APPLY_PREBLEND>
1182static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1183 U8CPU r = (rgb >> 16) & 0xFF;
1184 U8CPU g = (rgb >> 8) & 0xFF;
1185 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001186 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1187#if SK_SHOW_TEXT_BLIT_COVERAGE
1188 lum = SkTMax(lum, (U8CPU)0x30);
1189#endif
1190 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001191}
1192template<bool APPLY_PREBLEND>
1193static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1194 const SkGlyph& glyph, const uint8_t* table8) {
1195 const int width = glyph.fWidth;
1196 size_t dstRB = glyph.rowBytes();
1197 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1198
1199 for (int y = 0; y < glyph.fHeight; y++) {
1200 for (int i = 0; i < width; ++i) {
1201 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1202 }
1203 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1204 dst += dstRB;
1205 }
1206}
1207
1208template<bool APPLY_PREBLEND>
1209static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1210 const uint8_t* tableG,
1211 const uint8_t* tableB) {
1212 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1213 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1214 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001215#if SK_SHOW_TEXT_BLIT_COVERAGE
1216 r = SkTMax(r, (U8CPU)0x30);
1217 g = SkTMax(g, (U8CPU)0x30);
1218 b = SkTMax(b, (U8CPU)0x30);
1219#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001220 return SkPack888ToRGB16(r, g, b);
1221}
1222template<bool APPLY_PREBLEND>
1223static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1224 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1225 const int width = glyph.fWidth;
1226 size_t dstRB = glyph.rowBytes();
1227 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1228
1229 for (int y = 0; y < glyph.fHeight; y++) {
1230 for (int i = 0; i < width; i++) {
1231 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1232 }
1233 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1234 dst = (uint16_t*)((char*)dst + dstRB);
1235 }
1236}
1237
bungeman34902632014-12-10 21:43:27 -08001238static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1239 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001240 U8CPU r = (rgb >> 16) & 0xFF;
1241 U8CPU g = (rgb >> 8) & 0xFF;
1242 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001243#if SK_SHOW_TEXT_BLIT_COVERAGE
1244 a = SkTMax(a, (U8CPU)0x30);
1245#endif
bungeman34902632014-12-10 21:43:27 -08001246 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001247}
reed@google.comf77b35d2013-05-02 20:39:44 +00001248
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001249template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1250 return (T*)((char*)ptr + byteOffset);
1251}
1252
1253void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
djsollen1b277042014-08-06 06:58:06 -07001254 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001255
1256 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1257 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1258
1259 // Draw the glyph
1260 size_t cgRowBytes;
1261 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
halcanary96fcdcc2015-08-27 07:41:13 -07001262 if (cgPixels == nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001263 return;
1264 }
1265
1266 //TODO: see if drawing black on white and inverting is faster (at least in
1267 //lcd case) as core graphics appears to have special case code for drawing
1268 //black text.
1269
1270 // Fix the glyph
1271 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1272 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1273 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1274
1275 //Note that the following cannot really be integrated into the
1276 //pre-blend, since we may not be applying the pre-blend; when we aren't
1277 //applying the pre-blend it means that a filter wants linear anyway.
1278 //Other code may also be applying the pre-blend, so we'd need another
1279 //one with this and one without.
1280 CGRGBPixel* addr = cgPixels;
1281 for (int y = 0; y < glyph.fHeight; ++y) {
1282 for (int x = 0; x < glyph.fWidth; ++x) {
1283 int r = (addr[x] >> 16) & 0xFF;
1284 int g = (addr[x] >> 8) & 0xFF;
1285 int b = (addr[x] >> 0) & 0xFF;
1286 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1287 }
1288 addr = SkTAddByteOffset(addr, cgRowBytes);
1289 }
1290 }
1291
1292 // Convert glyph to mask
1293 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001294 case SkMask::kLCD16_Format: {
1295 if (fPreBlend.isApplicable()) {
1296 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1297 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1298 } else {
1299 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1300 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1301 }
1302 } break;
1303 case SkMask::kA8_Format: {
1304 if (fPreBlend.isApplicable()) {
1305 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1306 } else {
1307 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1308 }
1309 } break;
1310 case SkMask::kBW_Format: {
1311 const int width = glyph.fWidth;
1312 size_t dstRB = glyph.rowBytes();
1313 uint8_t* dst = (uint8_t*)glyph.fImage;
1314 for (int y = 0; y < glyph.fHeight; y++) {
1315 cgpixels_to_bits(dst, cgPixels, width);
1316 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1317 dst += dstRB;
1318 }
1319 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001320 case SkMask::kARGB32_Format: {
1321 const int width = glyph.fWidth;
1322 size_t dstRB = glyph.rowBytes();
1323 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1324 for (int y = 0; y < glyph.fHeight; y++) {
1325 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001326 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001327 }
1328 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1329 dst = (SkPMColor*)((char*)dst + dstRB);
1330 }
1331 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001332 default:
1333 SkDEBUGFAIL("unexpected mask format");
1334 break;
1335 }
1336}
1337
1338/*
1339 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1340 * seems sufficient, and possibly even correct, to allow the hinted outline
1341 * to be subpixel positioned.
1342 */
1343#define kScaleForSubPixelPositionHinting (4.0f)
1344
1345void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
reedd0f41732015-07-10 12:08:38 -07001346 AUTO_CG_LOCK();
1347
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001348 SkScalar scaleX = SK_Scalar1;
1349 SkScalar scaleY = SK_Scalar1;
1350
bungemanef27ce32015-10-29 09:30:32 -07001351 CGAffineTransform xform = fTransform;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001352 /*
1353 * For subpixel positioning, we want to return an unhinted outline, so it
1354 * can be positioned nicely at fractional offsets. However, we special-case
1355 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1356 * we want to retain hinting in the direction orthogonal to the baseline.
1357 * e.g. for horizontal baseline, we want to retain hinting in Y.
1358 * The way we remove hinting is to scale the font by some value (4) in that
1359 * direction, ask for the path, and then scale the path back down.
1360 */
bungeman7cbeaae2015-09-22 09:54:56 -07001361 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001362 SkMatrix m;
1363 fRec.getSingleMatrix(&m);
1364
1365 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001366 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001367 // now see if we need to restore hinting for axis-aligned baselines
1368 switch (SkComputeAxisAlignmentForHText(m)) {
1369 case kX_SkAxisAlignment:
1370 scaleY = SK_Scalar1; // want hinting in the Y direction
1371 break;
1372 case kY_SkAxisAlignment:
1373 scaleX = SK_Scalar1; // want hinting in the X direction
1374 break;
1375 default:
1376 break;
1377 }
1378
bungemanef27ce32015-10-29 09:30:32 -07001379 CGAffineTransform scale(CGAffineTransformMakeScale(ScalarToCG(scaleX), ScalarToCG(scaleY)));
1380 xform = CGAffineTransformConcat(fTransform, scale);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001381 }
1382
djsollen1b277042014-08-06 06:58:06 -07001383 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID();
bungemanef27ce32015-10-29 09:30:32 -07001384 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(fCTFont, cgGlyph, &xform));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001385
1386 path->reset();
halcanary96fcdcc2015-08-27 07:41:13 -07001387 if (cgPath != nullptr) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001388 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1389 }
1390
bungeman@google.comcefd9812013-05-15 15:07:32 +00001391 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001392 SkMatrix m;
1393 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1394 path->transform(m);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001395 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001396 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001397 SkPoint offset;
1398 getVerticalOffset(cgGlyph, &offset);
1399 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001400 }
1401}
1402
bungeman41078062014-07-07 08:16:37 -07001403void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
halcanary96fcdcc2015-08-27 07:41:13 -07001404 if (nullptr == metrics) {
bungeman41078062014-07-07 08:16:37 -07001405 return;
1406 }
1407
reedd0f41732015-07-10 12:08:38 -07001408 AUTO_CG_LOCK();
1409
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001410 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1411
bungeman41078062014-07-07 08:16:37 -07001412 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1413 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1414 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1415 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1416 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1417 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1418 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1419 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
bungeman6bd8d1c2015-06-09 08:40:51 -07001420 metrics->fMaxCharWidth = metrics->fXMax - metrics->fXMin;
bungeman41078062014-07-07 08:16:37 -07001421 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
bungeman6bd8d1c2015-06-09 08:40:51 -07001422 metrics->fCapHeight = CGToScalar( CTFontGetCapHeight(fCTFont));
bungeman41078062014-07-07 08:16:37 -07001423 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont));
1424 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001425
bungeman41078062014-07-07 08:16:37 -07001426 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
1427 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001428}
1429
1430void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1431 SkPath* skPath = (SkPath*)info;
1432
1433 // Process the path element
1434 switch (element->type) {
1435 case kCGPathElementMoveToPoint:
1436 skPath->moveTo(element->points[0].x, -element->points[0].y);
1437 break;
1438
1439 case kCGPathElementAddLineToPoint:
1440 skPath->lineTo(element->points[0].x, -element->points[0].y);
1441 break;
1442
1443 case kCGPathElementAddQuadCurveToPoint:
1444 skPath->quadTo(element->points[0].x, -element->points[0].y,
1445 element->points[1].x, -element->points[1].y);
1446 break;
1447
1448 case kCGPathElementAddCurveToPoint:
1449 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1450 element->points[1].x, -element->points[1].y,
1451 element->points[2].x, -element->points[2].y);
1452 break;
1453
1454 case kCGPathElementCloseSubpath:
1455 skPath->close();
1456 break;
1457
1458 default:
1459 SkDEBUGFAIL("Unknown path element!");
1460 break;
1461 }
1462}
1463
1464
1465///////////////////////////////////////////////////////////////////////////////
1466
halcanary96fcdcc2015-08-27 07:41:13 -07001467// Returns nullptr on failure
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001468// Call must still manage its ownership of provider
1469static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1470 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
halcanary96fcdcc2015-08-27 07:41:13 -07001471 if (nullptr == cg) {
1472 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001473 }
halcanary96fcdcc2015-08-27 07:41:13 -07001474 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, nullptr, nullptr);
1475 return ct ? NewFromFontRef(ct, nullptr, nullptr, true) : nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001476}
1477
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001478// Web fonts added to the the CTFont registry do not return their character set.
1479// Iterate through the font in this case. The existing caller caches the result,
1480// so the performance impact isn't too bad.
1481static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1482 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001483 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001484 SkUnichar* out = glyphToUnicode->begin();
1485 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1486 UniChar unichar = 0;
1487 while (glyphCount > 0) {
1488 CGGlyph glyph;
1489 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1490 out[glyph] = unichar;
1491 --glyphCount;
1492 }
1493 if (++unichar == 0) {
1494 break;
1495 }
1496 }
1497}
1498
1499// Construct Glyph to Unicode table.
1500// Unicode code points that require conjugate pairs in utf16 are not
1501// supported.
1502static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1503 SkTDArray<SkUnichar>* glyphToUnicode) {
1504 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1505 if (!charSet) {
1506 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1507 return;
1508 }
1509
1510 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1511 charSet));
1512 if (!bitmap) {
1513 return;
1514 }
1515 CFIndex length = CFDataGetLength(bitmap);
1516 if (!length) {
1517 return;
1518 }
1519 if (length > 8192) {
1520 // TODO: Add support for Unicode above 0xFFFF
1521 // Consider only the BMP portion of the Unicode character points.
1522 // The bitmap may contain other planes, up to plane 16.
1523 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1524 length = 8192;
1525 }
1526 const UInt8* bits = CFDataGetBytePtr(bitmap);
reed@google.com7fa2a652014-01-27 13:42:58 +00001527 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001528 SkUnichar* out = glyphToUnicode->begin();
1529 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1530 for (int i = 0; i < length; i++) {
1531 int mask = bits[i];
1532 if (!mask) {
1533 continue;
1534 }
1535 for (int j = 0; j < 8; j++) {
1536 CGGlyph glyph;
1537 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1538 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1539 out[glyph] = unichar;
1540 }
1541 }
1542 }
1543}
1544
1545static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1546 CGSize advance;
1547 advance.width = 0;
1548 CGGlyph glyph = gId;
1549 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1550 *data = sk_float_round2int(advance.width);
1551 return true;
1552}
1553
halcanary96fcdcc2015-08-27 07:41:13 -07001554/** Assumes src and dst are not nullptr. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001555static void CFStringToSkString(CFStringRef src, SkString* dst) {
1556 // Reserve enough room for the worst-case string,
1557 // plus 1 byte for the trailing null.
1558 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1559 kCFStringEncodingUTF8) + 1;
1560 dst->resize(length);
1561 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1562 // Resize to the actual UTF-8 length used, stripping the null character.
1563 dst->resize(strlen(dst->c_str()));
1564}
1565
reed@google.com2689f612013-03-20 20:01:47 +00001566SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
reed39a9a502015-05-12 09:50:04 -07001567 PerGlyphInfo perGlyphInfo,
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001568 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001569 uint32_t glyphIDsCount) const {
1570
reedd0f41732015-07-10 12:08:38 -07001571 AUTO_CG_LOCK();
1572
reed@google.com2689f612013-03-20 20:01:47 +00001573 CTFontRef originalCTFont = fFontRef.get();
bungeman7cbeaae2015-09-22 09:54:56 -07001574 AutoCFRelease<CTFontRef> ctFont(ctfont_create_exact_copy(
bungeman05846312015-09-23 12:51:28 -07001575 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), nullptr));
bungeman7cbeaae2015-09-22 09:54:56 -07001576
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001577 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1578
1579 {
1580 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
bungeman256b3512014-07-02 07:57:59 -07001581 if (fontName.get()) {
1582 CFStringToSkString(fontName, &info->fFontName);
1583 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001584 }
1585
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001586 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1587 info->fLastGlyphID = SkToU16(glyphCount - 1);
1588 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1589
reed39a9a502015-05-12 09:50:04 -07001590 if (perGlyphInfo & kToUnicode_PerGlyphInfo) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001591 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1592 }
1593
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001594 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1595 // fonts always have both glyf and loca tables. At the least, this is what
1596 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1597 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001598 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001599 return info;
1600 }
1601
1602 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1603 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1604 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1605 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1606 }
1607 if (symbolicTraits & kCTFontItalicTrait) {
1608 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1609 }
1610 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001611 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1612 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1613 } else if (stylisticClass & kCTFontScriptsClass) {
1614 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1615 }
1616 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1617 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1618 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1619 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1620 CGRect bbox = CTFontGetBoundingBox(ctFont);
1621
1622 SkRect r;
1623 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1624 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1625 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1626 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1627
1628 r.roundOut(&(info->fBBox));
1629
1630 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1631 // This probably isn't very good with an italic font.
1632 int16_t min_width = SHRT_MAX;
1633 info->fStemV = 0;
1634 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1635 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1636 CGGlyph glyphs[count];
1637 CGRect boundingRects[count];
1638 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1639 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1640 glyphs, boundingRects, count);
1641 for (size_t i = 0; i < count; i++) {
1642 int16_t width = (int16_t) boundingRects[i].size.width;
1643 if (width > 0 && width < min_width) {
1644 min_width = width;
1645 info->fStemV = min_width;
1646 }
1647 }
1648 }
1649
reed39a9a502015-05-12 09:50:04 -07001650 if (perGlyphInfo & kHAdvance_PerGlyphInfo) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001651 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1652 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1653 info->fGlyphWidths->fAdvance.append(1, &min_width);
1654 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1655 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1656 } else {
1657 info->fGlyphWidths.reset(
1658 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
reed@google.com7fa2a652014-01-27 13:42:58 +00001659 SkToInt(glyphCount),
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001660 glyphIDs,
1661 glyphIDsCount,
1662 &getWidthAdvance));
1663 }
1664 }
1665 return info;
1666}
1667
1668///////////////////////////////////////////////////////////////////////////////
1669
reed@google.comcc9aad52013-03-21 19:28:10 +00001670static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1671 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001672 AutoCFRelease<CFNumberRef> fontFormatRef(
1673 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1674 if (!fontFormatRef) {
1675 return 0;
1676 }
1677
1678 SInt32 fontFormatValue;
1679 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1680 return 0;
1681 }
1682
1683 switch (fontFormatValue) {
1684 case kCTFontFormatOpenTypePostScript:
1685 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1686 case kCTFontFormatOpenTypeTrueType:
1687 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1688 case kCTFontFormatTrueType:
1689 return SkSFNTHeader::fontType_MacTrueType::TAG;
1690 case kCTFontFormatPostScript:
1691 return SkSFNTHeader::fontType_PostScript::TAG;
1692 case kCTFontFormatBitmap:
1693 return SkSFNTHeader::fontType_MacTrueType::TAG;
1694 case kCTFontFormatUnrecognized:
1695 default:
1696 //CT seems to be unreliable in being able to obtain the type,
1697 //even if all we want is the first four bytes of the font resource.
1698 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1699 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1700 }
1701}
1702
bungeman5f213d92015-01-27 05:39:10 -08001703SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
reed@google.comcc9aad52013-03-21 19:28:10 +00001704 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001705 if (0 == fontType) {
halcanary96fcdcc2015-08-27 07:41:13 -07001706 return nullptr;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001707 }
1708
1709 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001710 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001711 SkTDArray<SkFontTableTag> tableTags;
1712 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001713 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001714
1715 // calc total size for font, save sizes
1716 SkTDArray<size_t> tableSizes;
1717 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1718 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001719 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001720 totalSize += (tableSize + 3) & ~3;
1721 *tableSizes.append() = tableSize;
1722 }
1723
1724 // reserve memory for stream, and zero it (tables must be zero padded)
1725 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1726 char* dataStart = (char*)stream->getMemoryBase();
1727 sk_bzero(dataStart, totalSize);
1728 char* dataPtr = dataStart;
1729
1730 // compute font header entries
1731 uint16_t entrySelector = 0;
1732 uint16_t searchRange = 1;
1733 while (searchRange < numTables >> 1) {
1734 entrySelector++;
1735 searchRange <<= 1;
1736 }
1737 searchRange <<= 4;
1738 uint16_t rangeShift = (numTables << 4) - searchRange;
1739
1740 // write font header
1741 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1742 header->fontType = fontType;
1743 header->numTables = SkEndian_SwapBE16(numTables);
1744 header->searchRange = SkEndian_SwapBE16(searchRange);
1745 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1746 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1747 dataPtr += sizeof(SkSFNTHeader);
1748
1749 // write tables
1750 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1751 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1752 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1753 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001754 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001755 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1756 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1757 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001758 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1759 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001760
1761 dataPtr += (tableSize + 3) & ~3;
1762 ++entry;
1763 }
1764
bungemanb3310c22015-03-02 09:05:36 -08001765 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001766 return stream;
1767}
1768
bungeman41868fe2015-05-20 09:21:04 -07001769struct NonDefaultAxesContext {
1770 SkFixed* axisValue;
1771 CFArrayRef cgAxes;
1772};
1773static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
1774 NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
1775
1776 if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
1777 return;
1778 }
1779
1780 // The key is a CFString which is a string from the 'name' table.
1781 // Search the cgAxes for an axis with this name, and use its index to store the value.
1782 CFIndex keyIndex = -1;
1783 CFStringRef keyString = static_cast<CFStringRef>(key);
1784 for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
1785 CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
1786 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1787 continue;
1788 }
1789
1790 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1791 CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
1792 if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
1793 continue;
1794 }
1795 CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
1796 if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
1797 keyIndex = i;
1798 break;
1799 }
1800 }
1801 if (keyIndex == -1) {
1802 return;
1803 }
1804
1805 CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
1806 double valueDouble;
1807 if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
1808 valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
1809 {
1810 return;
1811 }
1812 self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
1813}
1814static bool get_variations(CTFontRef fFontRef, CFIndex* cgAxisCount,
1815 SkAutoSTMalloc<4, SkFixed>* axisValues)
1816{
1817 // CTFontCopyVariationAxes and CTFontCopyVariation do not work when applied to fonts which
halcanary96fcdcc2015-08-27 07:41:13 -07001818 // started life with CGFontCreateWithDataProvider (they simply always return nullptr).
bungeman41868fe2015-05-20 09:21:04 -07001819 // As a result, we are limited to CGFontCopyVariationAxes and CGFontCopyVariations.
halcanary96fcdcc2015-08-27 07:41:13 -07001820 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, nullptr));
bungeman41868fe2015-05-20 09:21:04 -07001821
1822 AutoCFRelease<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont));
halcanary96fcdcc2015-08-27 07:41:13 -07001823 // If a font has no variations CGFontCopyVariations returns nullptr (instead of an empty dict).
bungeman41868fe2015-05-20 09:21:04 -07001824 if (!cgVariations.get()) {
1825 return false;
1826 }
1827
1828 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont));
1829 *cgAxisCount = CFArrayGetCount(cgAxes);
1830 axisValues->reset(*cgAxisCount);
1831
1832 // Set all of the axes to their default values.
1833 // Fail if any default value cannot be determined.
1834 for (CFIndex i = 0; i < *cgAxisCount; ++i) {
1835 CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
1836 if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
1837 return false;
1838 }
1839
1840 CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
1841 CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
1842 kCGFontVariationAxisDefaultValue);
1843 if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
1844 return false;
1845 }
1846 CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
1847 double axisDefaultValueDouble;
1848 if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
1849 {
1850 return false;
1851 }
1852 if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
1853 SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
1854 {
1855 return false;
1856 }
1857 (*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
1858 }
1859
1860 // Override the default values with the given font's stated axis values.
1861 NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
1862 CFDictionaryApplyFunction(cgVariations, set_non_default_axes, &c);
1863
1864 return true;
1865}
1866SkFontData* SkTypeface_Mac::onCreateFontData() const {
1867 int index;
1868 SkAutoTDelete<SkStreamAsset> stream(this->onOpenStream(&index));
1869
1870 CFIndex cgAxisCount;
1871 SkAutoSTMalloc<4, SkFixed> axisValues;
1872 if (get_variations(fFontRef, &cgAxisCount, &axisValues)) {
1873 return new SkFontData(stream.detach(), index, axisValues.get(), cgAxisCount);
1874 }
halcanary96fcdcc2015-08-27 07:41:13 -07001875 return new SkFontData(stream.detach(), index, nullptr, 0);
bungeman41868fe2015-05-20 09:21:04 -07001876}
1877
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001878///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001879///////////////////////////////////////////////////////////////////////////////
1880
1881int SkTypeface_Mac::onGetUPEM() const {
halcanary96fcdcc2015-08-27 07:41:13 -07001882 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001883 return CGFontGetUnitsPerEm(cgFont);
1884}
1885
bungeman@google.com839702b2013-08-07 17:09:22 +00001886SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001887 SkTypeface::LocalizedStrings* nameIter =
1888 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
halcanary96fcdcc2015-08-27 07:41:13 -07001889 if (nullptr == nameIter) {
bungeman@google.coma9802692013-08-07 02:45:25 +00001890 AutoCFRelease<CFStringRef> cfLanguage;
1891 AutoCFRelease<CFStringRef> cfFamilyName(
1892 CTFontCopyLocalizedName(fFontRef, kCTFontFamilyNameKey, &cfLanguage));
1893
1894 SkString skLanguage;
1895 SkString skFamilyName;
1896 if (cfLanguage.get()) {
1897 CFStringToSkString(cfLanguage.get(), &skLanguage);
1898 } else {
1899 skLanguage = "und"; //undetermined
1900 }
1901 if (cfFamilyName.get()) {
1902 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1903 }
1904
1905 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1906 }
1907 return nameIter;
1908}
1909
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001910// If, as is the case with web fonts, the CTFont data isn't available,
1911// the CGFont data may work. While the CGFont may always provide the
1912// right result, leave the CTFont code path to minimize disruption.
1913static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1914 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1915 kCTFontTableOptionNoOptions);
halcanary96fcdcc2015-08-27 07:41:13 -07001916 if (nullptr == data) {
1917 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, nullptr));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001918 data = CGFontCopyTableForTag(cgFont, tag);
1919 }
1920 return data;
1921}
1922
1923int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1924 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1925 kCTFontTableOptionNoOptions));
halcanary96fcdcc2015-08-27 07:41:13 -07001926 if (nullptr == cfArray) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001927 return 0;
1928 }
reed@google.com7fa2a652014-01-27 13:42:58 +00001929 int count = SkToInt(CFArrayGetCount(cfArray));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001930 if (tags) {
1931 for (int i = 0; i < count; ++i) {
1932 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1933 tags[i] = static_cast<SkFontTableTag>(fontTag);
1934 }
1935 }
1936 return count;
1937}
1938
1939size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1940 size_t length, void* dstData) const {
1941 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
halcanary96fcdcc2015-08-27 07:41:13 -07001942 if (nullptr == srcData) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001943 return 0;
1944 }
1945
1946 size_t srcSize = CFDataGetLength(srcData);
1947 if (offset >= srcSize) {
1948 return 0;
1949 }
1950 if (length > srcSize - offset) {
1951 length = srcSize - offset;
1952 }
1953 if (dstData) {
1954 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1955 }
1956 return length;
1957}
1958
1959SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001960 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001961}
1962
1963void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001964 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1965 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1966 {
1967 rec->fMaskFormat = SkMask::kA8_Format;
1968 // Render the glyphs as close as possible to what was requested.
1969 // The above turns off subpixel rendering, but the user requested it.
1970 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1971 // See comments below for more details.
1972 rec->setHinting(SkPaint::kNormal_Hinting);
1973 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001974
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001975 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00001976 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001977 SkScalerContext::kLCD_BGROrder_Flag |
1978 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001979
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001980 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001981
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001982 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001983
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001984 // Only two levels of hinting are supported.
1985 // kNo_Hinting means avoid CoreGraphics outline dilation.
1986 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1987 // If there is no lcd support, hinting (dilation) cannot be supported.
1988 SkPaint::Hinting hinting = rec->getHinting();
1989 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1990 hinting = SkPaint::kNo_Hinting;
1991 } else if (SkPaint::kFull_Hinting == hinting) {
1992 hinting = SkPaint::kNormal_Hinting;
1993 }
1994 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001995
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001996 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1997 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1998 // There is no current means to honor a request for unhinted lcd,
1999 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002000
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002001 // Hinting and smoothing should be orthogonal, but currently they are not.
2002 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
2003 // output is drawn from auto-dilated outlines (the amount of which is
2004 // determined by AppleFontSmoothing). Its regular anti-aliased output is
2005 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002006
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002007 // The behavior of Skia is as follows:
2008 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
2009 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
2010 // channel. This matches [LCD][yes-hint] in weight.
2011 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
2012 // Currenly side with LCD, effectively ignoring the hinting setting.
2013 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002014
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002015 if (isLCDFormat(rec->fMaskFormat)) {
2016 if (lcdSupport) {
2017 //CoreGraphics creates 555 masks for smoothed text anyway.
2018 rec->fMaskFormat = SkMask::kLCD16_Format;
2019 rec->setHinting(SkPaint::kNormal_Hinting);
2020 } else {
2021 rec->fMaskFormat = SkMask::kA8_Format;
2022 }
2023 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002024
bungeman34902632014-12-10 21:43:27 -08002025 // CoreText provides no information as to whether a glyph will be color or not.
2026 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
2027 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08002028 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08002029 rec->fMaskFormat = SkMask::kARGB32_Format;
2030 }
2031
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002032 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
2033 // All other masks can use regular gamma.
2034 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
2035#ifndef SK_GAMMA_APPLY_TO_A8
2036 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00002037#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002038 } else {
2039 //CoreGraphics dialates smoothed text as needed.
2040 rec->setContrast(0);
2041 }
2042}
2043
2044// we take ownership of the ref
2045static const char* get_str(CFStringRef ref, SkString* str) {
halcanary96fcdcc2015-08-27 07:41:13 -07002046 if (nullptr == ref) {
2047 return nullptr;
bungeman256b3512014-07-02 07:57:59 -07002048 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002049 CFStringToSkString(ref, str);
2050 CFSafeRelease(ref);
2051 return str->c_str();
2052}
2053
bungemanb374d6a2014-09-17 07:48:59 -07002054void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
2055 get_str(CTFontCopyFamilyName(fFontRef), familyName);
2056}
2057
reed@google.com5526ede2013-03-25 13:03:37 +00002058void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
2059 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002060 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00002061
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002062 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
2063 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
2064 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
caseq26337e92014-06-30 12:14:52 -07002065 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00002066}
reed@google.com5526ede2013-03-25 13:03:37 +00002067
reed@google.combcb42ae2013-07-02 13:56:39 +00002068int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002069 uint16_t glyphs[], int glyphCount) const
2070{
2071 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
2072 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
2073 // 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 +00002074
reed@google.combcb42ae2013-07-02 13:56:39 +00002075 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002076 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
2077 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00002078 switch (encoding) {
2079 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002080 const char* utf8 = reinterpret_cast<const char*>(chars);
2081 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2082 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00002083 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002084 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
2085 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00002086 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002087 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002088 break;
2089 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002090 case kUTF16_Encoding: {
2091 src = reinterpret_cast<const UniChar*>(chars);
2092 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00002093 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002094 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2095 ++extra;
2096 }
reed@google.combcb42ae2013-07-02 13:56:39 +00002097 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002098 srcCount = glyphCount + extra;
2099 break;
2100 }
2101 case kUTF32_Encoding: {
2102 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
2103 UniChar* utf16 = charStorage.reset(2 * glyphCount);
2104 src = utf16;
2105 for (int i = 0; i < glyphCount; ++i) {
2106 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
2107 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002108 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00002109 break;
2110 }
2111 }
2112
halcanary96fcdcc2015-08-27 07:41:13 -07002113 // If glyphs is nullptr, CT still needs glyph storage for finding the first failure.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002114 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00002115 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
2116 uint16_t* macGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002117 if (nullptr == macGlyphs || srcCount > glyphCount) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002118 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00002119 }
2120
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002121 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, srcCount);
2122
2123 // If there were any non-bmp, then copy and compact.
halcanary96fcdcc2015-08-27 07:41:13 -07002124 // If 'glyphs' is nullptr, then compact glyphStorage in-place.
2125 // If all are bmp and 'glyphs' is non-nullptr, 'glyphs' already contains the compact glyphs.
2126 // If some are non-bmp and 'glyphs' is non-nullptr, copy and compact into 'glyphs'.
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002127 uint16_t* compactedGlyphs = glyphs;
halcanary96fcdcc2015-08-27 07:41:13 -07002128 if (nullptr == compactedGlyphs) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002129 compactedGlyphs = macGlyphs;
2130 }
2131 if (srcCount > glyphCount) {
2132 int extra = 0;
2133 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002134 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002135 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2136 ++extra;
2137 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002138 }
2139 }
2140
2141 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002142 return glyphCount;
2143 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002144
2145 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002146 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002147 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002148 return i;
2149 }
2150 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002151 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002152 return glyphCount;
2153}
2154
2155int SkTypeface_Mac::onCountGlyphs() const {
reed@google.com7fa2a652014-01-27 13:42:58 +00002156 return SkToInt(CTFontGetGlyphCount(fFontRef));
reed@google.combcb42ae2013-07-02 13:56:39 +00002157}
2158
reed@google.com95625db2013-03-25 20:44:02 +00002159///////////////////////////////////////////////////////////////////////////////
2160///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002161
2162static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
2163 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
halcanary96fcdcc2015-08-27 07:41:13 -07002164 if (nullptr == ref.get()) {
reed@google.com83787c52013-03-26 17:19:15 +00002165 return false;
2166 }
2167 CFStringToSkString(ref, value);
2168 return true;
2169}
2170
reed@google.com95625db2013-03-25 20:44:02 +00002171#include "SkFontMgr.h"
2172
reed@google.com964988f2013-03-29 14:57:22 +00002173static inline int sqr(int value) {
2174 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2175 return value * value;
2176}
2177
2178// We normalize each axis (weight, width, italic) to be base-900
2179static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2180 return sqr(a.weight() - b.weight()) +
2181 sqr((a.width() - b.width()) * 100) +
2182 sqr((a.isItalic() != b.isItalic()) * 900);
2183}
2184
bungemana4c4a2d2014-10-20 13:33:19 -07002185static SkTypeface* createFromDesc(CFStringRef cfFamilyName, CTFontDescriptorRef desc) {
bungeman967937c2014-10-30 11:49:27 -07002186 NameStyle cacheRequest;
2187 SkString skFamilyName;
2188 CFStringToSkString(cfFamilyName, &skFamilyName);
2189 cacheRequest.fName = skFamilyName.c_str();
bungeman336fdf22014-11-10 07:48:55 -08002190 cacheRequest.fStyle = fontstyle_from_descriptor(desc);
reed@google.comdea7ee02013-03-28 14:12:10 +00002191
bungeman967937c2014-10-30 11:49:27 -07002192 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_NameStyle, &cacheRequest);
reed@google.comdea7ee02013-03-28 14:12:10 +00002193 if (face) {
2194 return face;
2195 }
2196
halcanary96fcdcc2015-08-27 07:41:13 -07002197 AutoCFRelease<CTFontRef> ctFont(CTFontCreateWithFontDescriptor(desc, 0, nullptr));
bungeman64fb51d2015-05-04 12:03:50 -07002198 if (!ctFont) {
halcanary96fcdcc2015-08-27 07:41:13 -07002199 return nullptr;
reed@google.comce8b3de2013-03-26 19:30:16 +00002200 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002201
reed@google.comce8b3de2013-03-26 19:30:16 +00002202 bool isFixedPitch;
bungeman336fdf22014-11-10 07:48:55 -08002203 (void)computeStyleBits(ctFont, &isFixedPitch);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002204
halcanary96fcdcc2015-08-27 07:41:13 -07002205 face = new SkTypeface_Mac(ctFont.detach(), nullptr, cacheRequest.fStyle, isFixedPitch,
halcanary385fe4d2015-08-26 13:07:48 -07002206 skFamilyName.c_str(), false);
bungemana4c4a2d2014-10-20 13:33:19 -07002207 SkTypefaceCache::Add(face, face->fontStyle());
reed@google.comdea7ee02013-03-28 14:12:10 +00002208 return face;
reed@google.comce8b3de2013-03-26 19:30:16 +00002209}
2210
reed@google.com83787c52013-03-26 17:19:15 +00002211class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002212public:
reed@google.com83787c52013-03-26 17:19:15 +00002213 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
halcanary96fcdcc2015-08-27 07:41:13 -07002214 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, nullptr))
reed@google.comdea7ee02013-03-28 14:12:10 +00002215 , fFamilyName(familyName)
2216 , fCount(0) {
reed@google.com83787c52013-03-26 17:19:15 +00002217 CFRetain(familyName);
halcanary96fcdcc2015-08-27 07:41:13 -07002218 if (nullptr == fArray) {
2219 fArray = CFArrayCreate(nullptr, nullptr, 0, nullptr);
reed@google.comdea7ee02013-03-28 14:12:10 +00002220 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002221 fCount = SkToInt(CFArrayGetCount(fArray));
reed@google.com83787c52013-03-26 17:19:15 +00002222 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002223
reed@google.com83787c52013-03-26 17:19:15 +00002224 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002225 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002226 CFRelease(fFamilyName);
2227 }
2228
mtklein36352bf2015-03-25 18:17:31 -07002229 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002230 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002231 }
2232
mtklein36352bf2015-03-25 18:17:31 -07002233 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002234 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002235 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2236 if (style) {
bungeman336fdf22014-11-10 07:48:55 -08002237 *style = fontstyle_from_descriptor(desc);
reed@google.com83787c52013-03-26 17:19:15 +00002238 }
2239 if (name) {
2240 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2241 name->reset();
2242 }
2243 }
2244 }
2245
mtklein36352bf2015-03-25 18:17:31 -07002246 SkTypeface* createTypeface(int index) override {
reed@google.com83787c52013-03-26 17:19:15 +00002247 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2248 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002249
reed@google.com83787c52013-03-26 17:19:15 +00002250 return createFromDesc(fFamilyName, desc);
2251 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002252
mtklein36352bf2015-03-25 18:17:31 -07002253 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002254 if (0 == fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002255 return nullptr;
reed@google.com964988f2013-03-29 14:57:22 +00002256 }
2257 return createFromDesc(fFamilyName, findMatchingDesc(pattern));
2258 }
2259
reed@google.com83787c52013-03-26 17:19:15 +00002260private:
2261 CFArrayRef fArray;
2262 CFStringRef fFamilyName;
reed@google.comdea7ee02013-03-28 14:12:10 +00002263 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002264
2265 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2266 int bestMetric = SK_MaxS32;
halcanary96fcdcc2015-08-27 07:41:13 -07002267 CTFontDescriptorRef bestDesc = nullptr;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002268
reed@google.com964988f2013-03-29 14:57:22 +00002269 for (int i = 0; i < fCount; ++i) {
2270 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
bungeman336fdf22014-11-10 07:48:55 -08002271 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc));
reed@google.com964988f2013-03-29 14:57:22 +00002272 if (0 == metric) {
2273 return desc;
2274 }
2275 if (metric < bestMetric) {
2276 bestMetric = metric;
2277 bestDesc = desc;
2278 }
2279 }
2280 SkASSERT(bestDesc);
2281 return bestDesc;
2282 }
reed@google.com83787c52013-03-26 17:19:15 +00002283};
2284
2285class SkFontMgr_Mac : public SkFontMgr {
reed@google.com83787c52013-03-26 17:19:15 +00002286 CFArrayRef fNames;
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002287 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002288
2289 CFStringRef stringAt(int index) const {
2290 SkASSERT((unsigned)index < (unsigned)fCount);
2291 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2292 }
2293
reed@google.com964988f2013-03-29 14:57:22 +00002294 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2295 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2296 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2297 &kCFTypeDictionaryKeyCallBacks,
2298 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002299
reed@google.com964988f2013-03-29 14:57:22 +00002300 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002301
reed@google.com964988f2013-03-29 14:57:22 +00002302 AutoCFRelease<CTFontDescriptorRef> desc(
2303 CTFontDescriptorCreateWithAttributes(cfAttr));
halcanary385fe4d2015-08-26 13:07:48 -07002304 return new SkFontStyleSet_Mac(cfFamilyName, desc);
reed@google.com964988f2013-03-29 14:57:22 +00002305 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002306
reed@google.com83787c52013-03-26 17:19:15 +00002307public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002308 SkFontMgr_Mac()
2309 : fNames(SkCTFontManagerCopyAvailableFontFamilyNames())
2310 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames)) : 0) {}
reed@google.com83787c52013-03-26 17:19:15 +00002311
2312 virtual ~SkFontMgr_Mac() {
2313 CFSafeRelease(fNames);
2314 }
reed@google.com95625db2013-03-25 20:44:02 +00002315
2316protected:
mtklein36352bf2015-03-25 18:17:31 -07002317 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002318 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002319 }
2320
mtklein36352bf2015-03-25 18:17:31 -07002321 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002322 if ((unsigned)index < (unsigned)fCount) {
2323 CFStringToSkString(this->stringAt(index), familyName);
2324 } else {
2325 familyName->reset();
2326 }
reed@google.com95625db2013-03-25 20:44:02 +00002327 }
2328
mtklein36352bf2015-03-25 18:17:31 -07002329 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002330 if ((unsigned)index >= (unsigned)fCount) {
halcanary96fcdcc2015-08-27 07:41:13 -07002331 return nullptr;
reed@google.com83787c52013-03-26 17:19:15 +00002332 }
reed@google.com964988f2013-03-29 14:57:22 +00002333 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002334 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002335
mtklein36352bf2015-03-25 18:17:31 -07002336 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
reed@google.com964988f2013-03-29 14:57:22 +00002337 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2338 return CreateSet(cfName);
2339 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002340
reed@google.com95625db2013-03-25 20:44:02 +00002341 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002342 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002343 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002344 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002345
djsollen33068c12014-11-14 10:52:53 -08002346 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2347 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002348 SkUnichar character) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002349 return nullptr;
djsollen33068c12014-11-14 10:52:53 -08002350 }
2351
reed@google.com95625db2013-03-25 20:44:02 +00002352 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002353 const SkFontStyle&) const override {
halcanary96fcdcc2015-08-27 07:41:13 -07002354 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002355 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002356
mtklein36352bf2015-03-25 18:17:31 -07002357 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002358 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
halcanary96fcdcc2015-08-27 07:41:13 -07002359 if (nullptr == pr) {
2360 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002361 }
2362 return create_from_dataProvider(pr);
2363 }
2364
mtklein36352bf2015-03-25 18:17:31 -07002365 SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002366 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
halcanary96fcdcc2015-08-27 07:41:13 -07002367 if (nullptr == pr) {
2368 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002369 }
2370 return create_from_dataProvider(pr);
2371 }
2372
bungeman41868fe2015-05-20 09:21:04 -07002373 static CFDictionaryRef get_axes(CGFontRef cg, SkFontData* fontData) {
2374 AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
2375 if (!cgAxes) {
halcanary96fcdcc2015-08-27 07:41:13 -07002376 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002377 }
2378
2379 CFIndex axisCount = CFArrayGetCount(cgAxes);
2380 if (0 == axisCount || axisCount != fontData->getAxisCount()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002381 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002382 }
2383
2384 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
2385 &kCFTypeDictionaryKeyCallBacks,
2386 &kCFTypeDictionaryValueCallBacks);
2387 for (int i = 0; i < fontData->getAxisCount(); ++i) {
2388 CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i);
2389 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07002390 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002391 }
2392 CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
2393
2394 CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
2395 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002396 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002397 }
2398
2399 // The variation axes can be set to any value, but cg will effectively pin them.
2400 // Pin them here to normalize.
2401 CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
2402 CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
2403 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
2404 !max || CFGetTypeID(max) != CFNumberGetTypeID())
2405 {
halcanary96fcdcc2015-08-27 07:41:13 -07002406 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002407 }
2408 CFNumberRef minNumber = static_cast<CFNumberRef>(min);
2409 CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
2410 double minDouble;
2411 double maxDouble;
2412 if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
2413 !CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
2414 {
halcanary96fcdcc2015-08-27 07:41:13 -07002415 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002416 }
2417 double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
2418 CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType,
2419 &value);
2420
2421 CFDictionaryAddValue(dict, axisName, valueNumber);
2422 CFRelease(valueNumber);
2423 }
2424 return dict;
2425 }
2426 SkTypeface* onCreateFromFontData(SkFontData* data) const override {
2427 SkAutoTDelete<SkFontData> fontData(data);
2428 SkStreamAsset* stream = fontData->detachStream();
2429 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
halcanary96fcdcc2015-08-27 07:41:13 -07002430 if (nullptr == provider) {
2431 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002432 }
2433 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
halcanary96fcdcc2015-08-27 07:41:13 -07002434 if (nullptr == cg) {
2435 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002436 }
2437
2438 AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, fontData));
2439 // The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
2440 // created from a data provider does not appear to have any ownership of the underlying
2441 // data. The original CGFontRef must be kept alive until the copy will no longer be used.
2442 AutoCFRelease<CGFontRef> cgVariant;
2443 if (cgVariations) {
2444 cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations));
2445 } else {
2446 cgVariant.reset(cg.detach());
2447 }
2448
halcanary96fcdcc2015-08-27 07:41:13 -07002449 CTFontRef ct = CTFontCreateWithGraphicsFont(cgVariant, 0, nullptr, nullptr);
bungeman41868fe2015-05-20 09:21:04 -07002450 if (!ct) {
halcanary96fcdcc2015-08-27 07:41:13 -07002451 return nullptr;
bungeman41868fe2015-05-20 09:21:04 -07002452 }
halcanary96fcdcc2015-08-27 07:41:13 -07002453 return NewFromFontRef(ct, cg.detach(), nullptr, true);
bungeman41868fe2015-05-20 09:21:04 -07002454 }
2455
mtklein36352bf2015-03-25 18:17:31 -07002456 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002457 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
halcanary96fcdcc2015-08-27 07:41:13 -07002458 if (nullptr == pr) {
2459 return nullptr;
reed@google.com95625db2013-03-25 20:44:02 +00002460 }
2461 return create_from_dataProvider(pr);
2462 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002463
reed@google.com7fdcd442013-07-30 21:25:49 +00002464 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002465 unsigned styleBits) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002466
2467 SkFontStyle style = SkFontStyle((SkTypeface::Style)styleBits);
2468 if (familyName) {
2469 familyName = map_css_names(familyName);
2470 }
2471
2472 if (!familyName || !*familyName) {
2473 familyName = FONT_DEFAULT_NAME;
2474 }
2475
bungeman967937c2014-10-30 11:49:27 -07002476 NameStyle cacheRequest = { familyName, style };
2477 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_NameStyle, &cacheRequest);
bungemana4c4a2d2014-10-20 13:33:19 -07002478
halcanary96fcdcc2015-08-27 07:41:13 -07002479 if (nullptr == face) {
bungemana4c4a2d2014-10-20 13:33:19 -07002480 face = NewFromName(familyName, style);
2481 if (face) {
2482 SkTypefaceCache::Add(face, style);
2483 } else {
2484 face = GetDefaultFace();
2485 face->ref();
2486 }
2487 }
2488 return face;
reed@google.com7fdcd442013-07-30 21:25:49 +00002489 }
reed@google.com95625db2013-03-25 20:44:02 +00002490};
2491
reed@google.com7fdcd442013-07-30 21:25:49 +00002492///////////////////////////////////////////////////////////////////////////////
2493
halcanary385fe4d2015-08-26 13:07:48 -07002494SkFontMgr* SkFontMgr::Factory() { return new SkFontMgr_Mac; }
mtklein1ee76512015-11-02 10:20:27 -08002495
2496#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)