blob: 5be7cc1f9781ea301b21bc529f5b50a50627f157 [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 ...
10
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000011#ifdef SK_BUILD_FOR_MAC
12#import <ApplicationServices/ApplicationServices.h>
13#endif
reed@android.com0680d6c2008-12-19 19:46:15 +000014
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000015#ifdef SK_BUILD_FOR_IOS
16#include <CoreText/CoreText.h>
reed@google.com3dcbd462013-03-27 13:56:34 +000017#include <CoreText/CTFontManager.h>
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000018#include <CoreGraphics/CoreGraphics.h>
19#include <CoreFoundation/CoreFoundation.h>
20#endif
21
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000022#include "SkCGUtils.h"
23#include "SkColorPriv.h"
24#include "SkDescriptor.h"
25#include "SkEndian.h"
26#include "SkFontDescriptor.h"
27#include "SkFloatingPoint.h"
28#include "SkGlyph.h"
bungeman34902632014-12-10 21:43:27 -080029#include "SkLazyFnPtr.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000030#include "SkMaskGamma.h"
31#include "SkSFNTHeader.h"
32#include "SkOTTable_glyf.h"
33#include "SkOTTable_head.h"
34#include "SkOTTable_hhea.h"
35#include "SkOTTable_loca.h"
36#include "SkOTUtils.h"
37#include "SkPaint.h"
38#include "SkPath.h"
39#include "SkString.h"
40#include "SkStream.h"
41#include "SkThread.h"
42#include "SkTypeface_mac.h"
43#include "SkUtils.h"
44#include "SkTypefaceCache.h"
reed@google.comce8b3de2013-03-26 19:30:16 +000045#include "SkFontMgr.h"
reed@google.combcb42ae2013-07-02 13:56:39 +000046#include "SkUtils.h"
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000047
bungeman34902632014-12-10 21:43:27 -080048#include <dlfcn.h>
reed@google.comf77b35d2013-05-02 20:39:44 +000049
bungeman3b4b66c2015-01-08 08:33:44 -080050// Set to make glyph bounding boxes visible.
51#define SK_SHOW_TEXT_BLIT_COVERAGE 0
52
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000053class SkScalerContext_Mac;
54
reed@google.com3dcbd462013-03-27 13:56:34 +000055// CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we
56// provide a wrapper here that will return an empty array if need be.
57static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() {
58#ifdef SK_BUILD_FOR_IOS
59 return CFArrayCreate(NULL, NULL, 0, NULL);
60#else
61 return CTFontManagerCopyAvailableFontFamilyNames();
62#endif
63}
64
65
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000066// Being templated and taking const T* prevents calling
67// CFSafeRelease(autoCFRelease) through implicit conversion.
68template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
69 if (cfTypeRef) {
70 CFRelease(cfTypeRef);
71 }
72}
73
74// Being templated and taking const T* prevents calling
75// CFSafeRetain(autoCFRelease) through implicit conversion.
76template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
77 if (cfTypeRef) {
78 CFRetain(cfTypeRef);
79 }
80}
81
82/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
83template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
84public:
85 explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
86 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
87
88 void reset(CFRef that = NULL) {
ljagielskie9d2d092014-07-15 20:02:04 -070089 if (that != fCFRef) {
90 CFSafeRelease(fCFRef);
91 fCFRef = that;
92 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000093 }
94
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000095 operator CFRef() const { return fCFRef; }
96 CFRef get() const { return fCFRef; }
97
bungeman@google.coma9802692013-08-07 02:45:25 +000098 CFRef* operator&() { SkASSERT(fCFRef == NULL); return &fCFRef; }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000099private:
100 CFRef fCFRef;
101};
102
reed@google.com964988f2013-03-29 14:57:22 +0000103static CFStringRef make_CFString(const char str[]) {
104 return CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8);
105}
106
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000107template<typename T> class AutoCGTable : SkNoncopyable {
108public:
109 AutoCGTable(CGFontRef font)
110 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
111 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
112 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
113 { }
114
115 const T* operator->() const { return fData; }
116
117private:
118 AutoCFRelease<CFDataRef> fCFData;
119public:
120 const T* fData;
121};
122
123// inline versions of these rect helpers
124
125static bool CGRectIsEmpty_inline(const CGRect& rect) {
126 return rect.size.width <= 0 || rect.size.height <= 0;
127}
128
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000129static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
130 return rect.origin.x;
131}
132
133static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
134 return rect.origin.x + rect.size.width;
135}
136
137static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
138 return rect.origin.y;
139}
140
141static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
142 return rect.origin.y + rect.size.height;
143}
144
145static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
146 return rect.size.width;
147}
148
149///////////////////////////////////////////////////////////////////////////////
150
151static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
reed@google.com7fa2a652014-01-27 13:42:58 +0000152 int width, int height, size_t rowBytes) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000153 SkASSERT(width);
154 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
155
156 if (width >= 32) {
157 while (height) {
158 sk_memset32(ptr, value, width);
159 ptr = (uint32_t*)((char*)ptr + rowBytes);
160 height -= 1;
161 }
162 return;
163 }
164
165 rowBytes -= width * sizeof(uint32_t);
166
167 if (width >= 8) {
168 while (height) {
169 int w = width;
170 do {
171 *ptr++ = value; *ptr++ = value;
172 *ptr++ = value; *ptr++ = value;
173 *ptr++ = value; *ptr++ = value;
174 *ptr++ = value; *ptr++ = value;
175 w -= 8;
176 } while (w >= 8);
177 while (--w >= 0) {
178 *ptr++ = value;
179 }
180 ptr = (uint32_t*)((char*)ptr + rowBytes);
181 height -= 1;
182 }
183 } else {
184 while (height) {
185 int w = width;
186 do {
187 *ptr++ = value;
188 } while (--w > 0);
189 ptr = (uint32_t*)((char*)ptr + rowBytes);
190 height -= 1;
191 }
192 }
193}
194
195#include <sys/utsname.h>
196
197typedef uint32_t CGRGBPixel;
198
199static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
200 return pixel & 0xFF;
201}
202
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000203static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
204
205// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
206static int readVersion() {
207 struct utsname info;
208 if (uname(&info) != 0) {
209 SkDebugf("uname failed\n");
210 return 0;
211 }
212 if (strcmp(info.sysname, "Darwin") != 0) {
213 SkDebugf("unexpected uname sysname %s\n", info.sysname);
214 return 0;
215 }
216 char* dot = strchr(info.release, '.');
217 if (!dot) {
218 SkDebugf("expected dot in uname release %s\n", info.release);
219 return 0;
220 }
221 int version = atoi(info.release);
222 if (version == 0) {
223 SkDebugf("could not parse uname release %s\n", info.release);
224 }
225 return version;
226}
227
228static int darwinVersion() {
229 static int darwin_version = readVersion();
230 return darwin_version;
231}
232
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000233static bool isSnowLeopard() {
234 return darwinVersion() == 10;
235}
236
237static bool isLion() {
238 return darwinVersion() == 11;
239}
240
241static bool isMountainLion() {
242 return darwinVersion() == 12;
243}
244
245static bool isLCDFormat(unsigned format) {
reedd54d3fc2014-11-13 14:39:58 -0800246 return SkMask::kLCD16_Format == format;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000247}
248
249static CGFloat ScalarToCG(SkScalar scalar) {
250 if (sizeof(CGFloat) == sizeof(float)) {
251 return SkScalarToFloat(scalar);
252 } else {
253 SkASSERT(sizeof(CGFloat) == sizeof(double));
254 return (CGFloat) SkScalarToDouble(scalar);
255 }
256}
257
258static SkScalar CGToScalar(CGFloat cgFloat) {
259 if (sizeof(CGFloat) == sizeof(float)) {
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000260 return cgFloat;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000261 } else {
262 SkASSERT(sizeof(CGFloat) == sizeof(double));
263 return SkDoubleToScalar(cgFloat);
264 }
265}
266
267static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
268 SkScalar sx = SK_Scalar1,
269 SkScalar sy = SK_Scalar1) {
270 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
271 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
272 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
273 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
274 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
275 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
276}
277
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000278///////////////////////////////////////////////////////////////////////////////
279
280#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000281
282/**
283 * There does not appear to be a publicly accessable API for determining if lcd
284 * font smoothing will be applied if we request it. The main issue is that if
285 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
286 */
287static bool supports_LCD() {
288 static int gSupportsLCD = -1;
289 if (gSupportsLCD >= 0) {
290 return (bool) gSupportsLCD;
291 }
292 uint32_t rgb = 0;
293 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
294 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
295 colorspace, BITMAP_INFO_RGB));
296 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
297 CGContextSetShouldSmoothFonts(cgContext, true);
298 CGContextSetShouldAntialias(cgContext, true);
299 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
300 CGContextSetGrayFillColor(cgContext, 1, 1);
301 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
302 uint32_t r = (rgb >> 16) & 0xFF;
303 uint32_t g = (rgb >> 8) & 0xFF;
304 uint32_t b = (rgb >> 0) & 0xFF;
305 gSupportsLCD = (r != g || r != b);
306 return (bool) gSupportsLCD;
307}
308
309class Offscreen {
310public:
bungeman34902632014-12-10 21:43:27 -0800311 Offscreen()
312 : fRGBSpace(NULL)
313 , fCG(NULL)
314 , fDoAA(false)
315 , fDoLCD(false)
316 {
317 fSize.set(0, 0);
318 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000319
320 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
321 CGGlyph glyphID, size_t* rowBytesPtr,
322 bool generateA8FromLCD);
323
324private:
325 enum {
326 kSize = 32 * 32 * sizeof(CGRGBPixel)
327 };
328 SkAutoSMalloc<kSize> fImageStorage;
329 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
330
331 // cached state
332 AutoCFRelease<CGContextRef> fCG;
333 SkISize fSize;
334 bool fDoAA;
335 bool fDoLCD;
336
337 static int RoundSize(int dimension) {
338 return SkNextPow2(dimension);
339 }
340};
341
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000342///////////////////////////////////////////////////////////////////////////////
343
bungemana4c4a2d2014-10-20 13:33:19 -0700344static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
345 CFNumberRef num;
346 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
347 && CFNumberIsFloatType(num)
348 && CFNumberGetValue(num, kCFNumberFloatType, value);
349}
350
351static int unit_weight_to_fontstyle(float unit) {
352 float value;
353 if (unit < 0) {
354 value = 100 + (1 + unit) * 300;
355 } else {
356 value = 400 + unit * 500;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000357 }
bungemana4c4a2d2014-10-20 13:33:19 -0700358 return sk_float_round2int(value);
359}
360
361static int unit_width_to_fontstyle(float unit) {
362 float value;
363 if (unit < 0) {
364 value = 1 + (1 + unit) * 4;
365 } else {
366 value = 5 + unit * 4;
367 }
368 return sk_float_round2int(value);
369}
370
bungeman336fdf22014-11-10 07:48:55 -0800371static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc) {
bungemana4c4a2d2014-10-20 13:33:19 -0700372 AutoCFRelease<CFDictionaryRef> dict(
373 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute));
374 if (NULL == dict.get()) {
375 return SkFontStyle();
376 }
377
bungemana4c4a2d2014-10-20 13:33:19 -0700378 float weight, width, slant;
379 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
bungeman336fdf22014-11-10 07:48:55 -0800380 weight = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700381 }
382 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
383 width = 0;
384 }
385 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
bungeman336fdf22014-11-10 07:48:55 -0800386 slant = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700387 }
388
389 return SkFontStyle(unit_weight_to_fontstyle(weight),
390 unit_width_to_fontstyle(width),
391 slant ? SkFontStyle::kItalic_Slant
392 : SkFontStyle::kUpright_Slant);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000393}
394
bungeman336fdf22014-11-10 07:48:55 -0800395static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) {
396 unsigned style = SkTypeface::kNormal;
397 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
398
399 if (traits & kCTFontBoldTrait) {
400 style |= SkTypeface::kBold;
401 }
402 if (traits & kCTFontItalicTrait) {
403 style |= SkTypeface::kItalic;
404 }
405 if (isFixedPitch) {
406 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0;
407 }
408 return (SkTypeface::Style)style;
409}
410
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000411static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
412 SkFontID id = 0;
413// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
414// bracket this to be Mac only.
415#ifdef SK_BUILD_FOR_MAC
416 ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
417 id = (SkFontID)ats;
418 if (id != 0) {
419 id &= 0x3FFFFFFF; // make top two bits 00
420 return id;
421 }
422#endif
423 // CTFontGetPlatformFont returns NULL if the font is local
424 // (e.g., was created by a CSS3 @font-face rule).
425 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
426 AutoCGTable<SkOTTableHead> headTable(cgFont);
427 if (headTable.fData) {
428 id = (SkFontID) headTable->checksumAdjustment;
429 id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
430 }
431 // well-formed fonts have checksums, but as a last resort, use the pointer.
432 if (id == 0) {
433 id = (SkFontID) (uintptr_t) fontRef;
434 id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
435 }
436 return id;
437}
438
reed@google.comce8b3de2013-03-26 19:30:16 +0000439#define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2)
440
bungeman994e8182015-02-23 16:17:43 -0800441// kCTFontColorGlyphsTrait was added in the Mac 10.7 and iPhone 4.3 SDKs.
442// Being an enum value it is not guarded by version macros, but old SDKs must still be supported.
443#if defined(__MAC_10_7) || defined(__IPHONE_4_3)
444static const uint32_t SkCTFontColorGlyphsTrait = kCTFontColorGlyphsTrait;
445#else
446static const uint32_t SkCTFontColorGlyphsTrait = (1 << 13);
447#endif
448
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000449class SkTypeface_Mac : public SkTypeface {
450public:
mtklein802ad832014-10-20 12:54:31 -0700451 SkTypeface_Mac(const SkFontStyle& fs, SkFontID fontID, bool isFixedPitch,
bungeman967937c2014-10-30 11:49:27 -0700452 CTFontRef fontRef, const char requestedName[], bool isLocalStream)
bungemana4c4a2d2014-10-20 13:33:19 -0700453 : SkTypeface(fs, fontID, isFixedPitch)
bungeman967937c2014-10-30 11:49:27 -0700454 , fRequestedName(requestedName)
mtklein802ad832014-10-20 12:54:31 -0700455 , fFontRef(fontRef) // caller has already called CFRetain for us
caseq26337e92014-06-30 12:14:52 -0700456 , fIsLocalStream(isLocalStream)
bungeman994e8182015-02-23 16:17:43 -0800457 , fHasColorGlyphs(CTFontGetSymbolicTraits(fFontRef) & SkCTFontColorGlyphsTrait)
reed@google.comce8b3de2013-03-26 19:30:16 +0000458 {
459 SkASSERT(fontRef);
460 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +0000461
bungeman967937c2014-10-30 11:49:27 -0700462 SkString fRequestedName;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000463 AutoCFRelease<CTFontRef> fFontRef;
464
465protected:
mtklein36352bf2015-03-25 18:17:31 -0700466 int onGetUPEM() const override;
467 SkStreamAsset* onOpenStream(int* ttcIndex) const override;
468 void onGetFamilyName(SkString* familyName) const override;
469 SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
470 int onGetTableTags(SkFontTableTag tags[]) const override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000471 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
mtklein36352bf2015-03-25 18:17:31 -0700472 size_t length, void* data) const override;
473 SkScalerContext* onCreateScalerContext(const SkDescriptor*) const override;
474 void onFilterRec(SkScalerContextRec*) const override;
475 void onGetFontDescriptor(SkFontDescriptor*, bool*) const override;
reed@google.com2689f612013-03-20 20:01:47 +0000476 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics(
477 SkAdvancedTypefaceMetrics::PerGlyphInfo,
mtklein36352bf2015-03-25 18:17:31 -0700478 const uint32_t*, uint32_t) const override;
reed@google.combcb42ae2013-07-02 13:56:39 +0000479 virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
mtklein36352bf2015-03-25 18:17:31 -0700480 int glyphCount) const override;
481 int onCountGlyphs() const override;
skia.committer@gmail.comc1641fc2013-03-21 07:01:39 +0000482
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000483private:
caseq26337e92014-06-30 12:14:52 -0700484 bool fIsLocalStream;
bungeman98c251b2015-02-23 14:24:04 -0800485 bool fHasColorGlyphs;
reed@google.comce8b3de2013-03-26 19:30:16 +0000486
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000487 typedef SkTypeface INHERITED;
488};
489
caseq26337e92014-06-30 12:14:52 -0700490static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[], bool isLocalStream) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000491 SkASSERT(fontRef);
bungeman@google.comfe747652013-03-25 19:36:11 +0000492 bool isFixedPitch;
bungeman336fdf22014-11-10 07:48:55 -0800493 SkFontStyle style = SkFontStyle(computeStyleBits(fontRef, &isFixedPitch));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000494 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
495
caseq26337e92014-06-30 12:14:52 -0700496 return new SkTypeface_Mac(style, fontID, isFixedPitch, fontRef, name, isLocalStream);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000497}
498
bungemana4c4a2d2014-10-20 13:33:19 -0700499static SkTypeface* NewFromName(const char familyName[], const SkFontStyle& theStyle) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000500 CTFontRef ctFont = NULL;
501
502 CTFontSymbolicTraits ctFontTraits = 0;
bungemana4c4a2d2014-10-20 13:33:19 -0700503 if (theStyle.weight() >= SkFontStyle::kBold_Weight) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000504 ctFontTraits |= kCTFontBoldTrait;
505 }
bungemana4c4a2d2014-10-20 13:33:19 -0700506 if (theStyle.slant() != SkFontStyle::kUpright_Slant) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000507 ctFontTraits |= kCTFontItalicTrait;
508 }
509
bungemana4c4a2d2014-10-20 13:33:19 -0700510 //TODO: add weight width slant
511
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000512 // Create the font info
reed@google.com964988f2013-03-29 14:57:22 +0000513 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000514
515 AutoCFRelease<CFNumberRef> cfFontTraits(
516 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
517
518 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
519 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
520 &kCFTypeDictionaryKeyCallBacks,
521 &kCFTypeDictionaryValueCallBacks));
522
523 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
524 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
525 &kCFTypeDictionaryKeyCallBacks,
526 &kCFTypeDictionaryValueCallBacks));
527
528 // Create the font
529 if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
530 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
531
532 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
533 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
534
535 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
536 CTFontDescriptorCreateWithAttributes(cfAttributes));
537
538 if (ctFontDesc != NULL) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000539 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000540 }
541 }
542
caseq26337e92014-06-30 12:14:52 -0700543 return ctFont ? NewFromFontRef(ctFont, familyName, false) : NULL;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000544}
545
bungemand6aeb6d2014-07-25 11:52:47 -0700546SK_DECLARE_STATIC_MUTEX(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000547static SkTypeface* GetDefaultFace() {
bungemand6aeb6d2014-07-25 11:52:47 -0700548 SkAutoMutexAcquire ma(gGetDefaultFaceMutex);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000549
550 static SkTypeface* gDefaultFace;
551
552 if (NULL == gDefaultFace) {
bungemana4c4a2d2014-10-20 13:33:19 -0700553 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkFontStyle());
554 SkTypefaceCache::Add(gDefaultFace, SkFontStyle());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000555 }
556 return gDefaultFace;
557}
558
559///////////////////////////////////////////////////////////////////////////////
560
561extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
562CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
563 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
564 return macface ? macface->fFontRef.get() : NULL;
565}
566
567/* This function is visible on the outside. It first searches the cache, and if
568 * not found, returns a new entry (after adding it to the cache).
569 */
570SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
571 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
572 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
573 if (face) {
574 face->ref();
575 } else {
caseq26337e92014-06-30 12:14:52 -0700576 face = NewFromFontRef(fontRef, NULL, false);
bungemana4c4a2d2014-10-20 13:33:19 -0700577 SkTypefaceCache::Add(face, face->fontStyle());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000578 // NewFromFontRef doesn't retain the parameter, but the typeface it
579 // creates does release it in its destructor, so we balance that with
580 // a retain call here.
581 CFRetain(fontRef);
582 }
583 SkASSERT(face->getRefCnt() > 1);
584 return face;
585}
586
bungeman967937c2014-10-30 11:49:27 -0700587struct NameStyle {
588 const char* fName;
589 SkFontStyle fStyle;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000590};
591
bungeman967937c2014-10-30 11:49:27 -0700592static bool find_by_NameStyle(SkTypeface* cachedFace, const SkFontStyle& cachedStyle, void* ctx) {
593 const SkTypeface_Mac* cachedMacFace = static_cast<SkTypeface_Mac*>(cachedFace);
594 const NameStyle* requested = static_cast<const NameStyle*>(ctx);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000595
bungeman967937c2014-10-30 11:49:27 -0700596 return cachedStyle == requested->fStyle
597 && cachedMacFace->fRequestedName.equals(requested->fName);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000598}
599
600static const char* map_css_names(const char* name) {
601 static const struct {
602 const char* fFrom; // name the caller specified
603 const char* fTo; // "canonical" name we map to
604 } gPairs[] = {
605 { "sans-serif", "Helvetica" },
606 { "serif", "Times" },
607 { "monospace", "Courier" }
608 };
609
610 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
611 if (strcmp(name, gPairs[i].fFrom) == 0) {
612 return gPairs[i].fTo;
613 }
614 }
615 return name; // no change
616}
617
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000618///////////////////////////////////////////////////////////////////////////////
619
bungeman@google.comcefd9812013-05-15 15:07:32 +0000620/** GlyphRect is in FUnits (em space, y up). */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000621struct GlyphRect {
622 int16_t fMinX;
623 int16_t fMinY;
624 int16_t fMaxX;
625 int16_t fMaxY;
626};
627
628class SkScalerContext_Mac : public SkScalerContext {
629public:
reed@google.com0da48612013-03-19 16:06:52 +0000630 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000631
632protected:
mtklein36352bf2015-03-25 18:17:31 -0700633 unsigned generateGlyphCount(void) override;
634 uint16_t generateCharToGlyph(SkUnichar uni) override;
635 void generateAdvance(SkGlyph* glyph) override;
636 void generateMetrics(SkGlyph* glyph) override;
637 void generateImage(const SkGlyph& glyph) override;
638 void generatePath(const SkGlyph& glyph, SkPath* path) override;
639 void generateFontMetrics(SkPaint::FontMetrics*) override;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000640
641private:
642 static void CTPathElement(void *info, const CGPathElement *element);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000643
bungeman@google.comcefd9812013-05-15 15:07:32 +0000644 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */
645 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000646
647 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset.
648 *
649 * For use with (and must be called before) generateBBoxes.
650 */
651 uint16_t getFBoundingBoxesGlyphOffset();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000652
bungeman@google.comcefd9812013-05-15 15:07:32 +0000653 /** Initializes fFBoundingBoxes and returns true on success.
654 *
655 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
656 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
657 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
658 * font directly.
659 *
660 * This routine initializes fFBoundingBoxes to an array of
661 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits
662 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount).
663 *
664 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly
665 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables.
666 *
667 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont.
668 */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000669 bool generateBBoxes();
670
bungeman@google.comcefd9812013-05-15 15:07:32 +0000671 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down).
672 *
673 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs.
674 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs.
675 */
676 SkMatrix fFUnitMatrix;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000677
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000678 Offscreen fOffscreen;
679 AutoCFRelease<CTFontRef> fCTFont;
bungemanaae30912015-03-02 13:43:26 -0800680 CGAffineTransform fTransform;
bungeman34902632014-12-10 21:43:27 -0800681 CGAffineTransform fInvTransform;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000682
bungeman3b4b66c2015-01-08 08:33:44 -0800683 /** Unrotated variant of fCTFont.
684 *
685 * In 10.10.1 CTFontGetAdvancesForGlyphs applies the font transform to the width of the
686 * advances, but always sets the height to 0. This font is used to get the advances of the
687 * unrotated glyph, and then the rotation is applied separately.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000688 *
689 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise.
690 * This makes kCTFontDefaultOrientation dangerous, because the metrics from
691 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation.
bungeman3b4b66c2015-01-08 08:33:44 -0800692 * With kCTFontVerticalOrientation the advances must be unrotated.
bungeman@google.comcefd9812013-05-15 15:07:32 +0000693 */
bungeman3b4b66c2015-01-08 08:33:44 -0800694 AutoCFRelease<CTFontRef> fCTUnrotatedFont;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000695
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000696 AutoCFRelease<CGFontRef> fCGFont;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000697 SkAutoTMalloc<GlyphRect> fFBoundingBoxes;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000698 uint16_t fFBoundingBoxesGlyphOffset;
699 uint16_t fGlyphCount;
700 bool fGeneratedFBoundingBoxes;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000701 const bool fDoSubPosition;
702 const bool fVertical;
703
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000704 friend class Offscreen;
reed@google.com0da48612013-03-19 16:06:52 +0000705
706 typedef SkScalerContext INHERITED;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000707};
708
reed@google.com0da48612013-03-19 16:06:52 +0000709SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface,
710 const SkDescriptor* desc)
711 : INHERITED(typeface, desc)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000712 , fFBoundingBoxes()
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000713 , fFBoundingBoxesGlyphOffset(0)
714 , fGeneratedFBoundingBoxes(false)
bungeman@google.comcefd9812013-05-15 15:07:32 +0000715 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag))
716 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag))
717
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000718{
reed@google.com2689f612013-03-20 20:01:47 +0000719 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000720 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000721 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
722 fGlyphCount = SkToU16(numGlyphs);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000723
bungeman34902632014-12-10 21:43:27 -0800724 // CT on (at least) 10.9 will size color glyphs down from the requested size, but not up.
725 // As a result, it is necessary to know the actual device size and request that.
726 SkVector scale;
bungemanbe2284d2014-11-25 08:08:09 -0800727 SkMatrix skTransform;
bungeman34902632014-12-10 21:43:27 -0800728 fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale, &scale, &skTransform,
729 NULL, NULL, &fFUnitMatrix);
bungemanaae30912015-03-02 13:43:26 -0800730 fTransform = MatrixToCGAffineTransform(skTransform);
731 fInvTransform = CGAffineTransformInvert(fTransform);
bungeman@google.comcefd9812013-05-15 15:07:32 +0000732
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000733 AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
734 if (fVertical) {
bungemanaae30912015-03-02 13:43:26 -0800735 // Setting the vertical orientation here is required for vertical metrics on some versions.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000736 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
737 kCFAllocatorDefault, 0,
738 &kCFTypeDictionaryKeyCallBacks,
739 &kCFTypeDictionaryValueCallBacks));
740 if (cfAttributes) {
741 CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
742 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
743 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
744 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
bungemane194c492014-07-16 13:46:06 -0700745 ctFontDesc.reset(CTFontDescriptorCreateWithAttributes(cfAttributes));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000746 }
747 }
bungemanbe2284d2014-11-25 08:08:09 -0800748
749 // The transform contains everything except the requested text size.
750 // Some properties, like 'trak', are based on the text size (before applying the matrix).
bungeman34902632014-12-10 21:43:27 -0800751 CGFloat textSize = ScalarToCG(scale.y());
bungemanbe2284d2014-11-25 08:08:09 -0800752
bungemanaae30912015-03-02 13:43:26 -0800753 fCTFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize, &fTransform, ctFontDesc));
bungemane194c492014-07-16 13:46:06 -0700754 fCGFont.reset(CTFontCopyGraphicsFont(fCTFont, NULL));
bungeman3b4b66c2015-01-08 08:33:44 -0800755 fCTUnrotatedFont.reset(CTFontCreateCopyWithAttributes(ctFont, textSize,
756 &CGAffineTransformIdentity, NULL));
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000757
bungemanbe2284d2014-11-25 08:08:09 -0800758 // 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 +0000759 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont)));
760 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000761}
762
bungeman34902632014-12-10 21:43:27 -0800763extern "C" {
764
765/** CTFontDrawGlyphs was introduced in 10.7. */
766typedef void (*CTFontDrawGlyphsProc)(CTFontRef, const CGGlyph[], const CGPoint[],
767 size_t, CGContextRef);
768
769/** This is an implementation of CTFontDrawGlyphs for 10.6. */
770static void sk_legacy_CTFontDrawGlyphs(CTFontRef, const CGGlyph glyphs[], const CGPoint points[],
771 size_t count, CGContextRef cg)
772{
773 CGContextShowGlyphsAtPositions(cg, glyphs, points, count);
774}
775
776}
777
778CTFontDrawGlyphsProc SkChooseCTFontDrawGlyphs() {
779 CTFontDrawGlyphsProc realCTFontDrawGlyphs;
780 *reinterpret_cast<void**>(&realCTFontDrawGlyphs) = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
781 return realCTFontDrawGlyphs ? realCTFontDrawGlyphs : sk_legacy_CTFontDrawGlyphs;
782};
783
784SK_DECLARE_STATIC_LAZY_FN_PTR(CTFontDrawGlyphsProc, gCTFontDrawGlyphs, SkChooseCTFontDrawGlyphs);
785
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000786CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
787 CGGlyph glyphID, size_t* rowBytesPtr,
bungeman34902632014-12-10 21:43:27 -0800788 bool generateA8FromLCD)
789{
790 CTFontDrawGlyphsProc ctFontDrawGlyphs = gCTFontDrawGlyphs.get();
791
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000792 if (!fRGBSpace) {
793 //It doesn't appear to matter what color space is specified.
794 //Regular blends and antialiased text are always (s*a + d*(1-a))
795 //and smoothed text is always g=2.0.
bungemane194c492014-07-16 13:46:06 -0700796 fRGBSpace.reset(CGColorSpaceCreateDeviceRGB());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000797 }
798
799 // default to kBW_Format
800 bool doAA = false;
801 bool doLCD = false;
802
803 if (SkMask::kBW_Format != glyph.fMaskFormat) {
804 doLCD = true;
805 doAA = true;
806 }
807
808 // FIXME: lcd smoothed un-hinted rasterization unsupported.
809 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
810 doLCD = false;
811 doAA = true;
812 }
813
bungeman34902632014-12-10 21:43:27 -0800814 // If this font might have color glyphs, disable LCD as there's no way to support it.
815 // CoreText doesn't tell us which format it ended up using, so we can't detect it.
816 // A8 will be ugly too (white on transparent), but TODO: we can detect gray and set to A8.
817 if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
818 doLCD = false;
819 }
820
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000821 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
822 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
823 if (fSize.fWidth < glyph.fWidth) {
824 fSize.fWidth = RoundSize(glyph.fWidth);
825 }
826 if (fSize.fHeight < glyph.fHeight) {
827 fSize.fHeight = RoundSize(glyph.fHeight);
828 }
829
830 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
831 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
bungeman34902632014-12-10 21:43:27 -0800832 const CGImageAlphaInfo alpha = (SkMask::kARGB32_Format == glyph.fMaskFormat)
833 ? kCGImageAlphaPremultipliedFirst
834 : kCGImageAlphaNoneSkipFirst;
835 const CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | alpha;
bungemane194c492014-07-16 13:46:06 -0700836 fCG.reset(CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
bungeman34902632014-12-10 21:43:27 -0800837 rowBytes, fRGBSpace, bitmapInfo));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000838
bungeman34902632014-12-10 21:43:27 -0800839 // Skia handles quantization and subpixel positioning,
840 // so disable quantization and enabe subpixel positioning in CG.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000841 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
842 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
843
bungeman@google.comcefd9812013-05-15 15:07:32 +0000844 // Because CG always draws from the horizontal baseline,
845 // if there is a non-integral translation from the horizontal origin to the vertical origin,
846 // then CG cannot draw the glyph in the correct location without subpixel positioning.
bungeman34902632014-12-10 21:43:27 -0800847 CGContextSetAllowsFontSubpixelPositioning(fCG, true);
848 CGContextSetShouldSubpixelPositionFonts(fCG, true);
849
850 CGContextSetTextDrawingMode(fCG, kCGTextFill);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000851
852 // Draw white on black to create mask.
853 // TODO: Draw black on white and invert, CG has a special case codepath.
854 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
855
856 // force our checks below to happen
857 fDoAA = !doAA;
858 fDoLCD = !doLCD;
bungeman34902632014-12-10 21:43:27 -0800859
860 if (sk_legacy_CTFontDrawGlyphs == ctFontDrawGlyphs) {
861 // CTFontDrawGlyphs will apply the font, font size, and font matrix to the CGContext.
862 // Our 'fake' one does not, so set up the CGContext here.
863 CGContextSetFont(fCG, context.fCGFont);
864 CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont));
bungeman34902632014-12-10 21:43:27 -0800865 }
bungemanaae30912015-03-02 13:43:26 -0800866 CGContextSetTextMatrix(fCG, context.fTransform);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000867 }
868
869 if (fDoAA != doAA) {
870 CGContextSetShouldAntialias(fCG, doAA);
871 fDoAA = doAA;
872 }
873 if (fDoLCD != doLCD) {
874 CGContextSetShouldSmoothFonts(fCG, doLCD);
875 fDoLCD = doLCD;
876 }
877
878 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
879 // skip rows based on the glyph's height
880 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
881
882 // erase to black
883 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
884
885 float subX = 0;
886 float subY = 0;
887 if (context.fDoSubPosition) {
888 subX = SkFixedToFloat(glyph.getSubXFixed());
889 subY = SkFixedToFloat(glyph.getSubYFixed());
890 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000891
bungeman34902632014-12-10 21:43:27 -0800892 // CoreText and CoreGraphics always draw using the horizontal baseline origin.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000893 if (context.fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +0000894 SkPoint offset;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000895 context.getVerticalOffset(glyphID, &offset);
896 subX += offset.fX;
897 subY += offset.fY;
898 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000899
bungeman34902632014-12-10 21:43:27 -0800900 CGPoint point = CGPointMake(-glyph.fLeft + subX, glyph.fTop + glyph.fHeight - subY);
bungemanaae30912015-03-02 13:43:26 -0800901 // Prior to 10.10, CTFontDrawGlyphs acted like CGContextShowGlyphsAtPositions and took
902 // 'positions' which are in text space. The glyph location (in device space) must be
903 // mapped into text space, so that CG can convert it back into device space.
904 // In 10.10.1, this is handled directly in CTFontDrawGlyphs.
905
906 // However, in 10.10.2 color glyphs no longer rotate based on the font transform.
907 // So always make the font transform identity and place the transform on the context.
908 point = CGPointApplyAffineTransform(point, context.fInvTransform);
909
910 ctFontDrawGlyphs(context.fCTUnrotatedFont, &glyphID, &point, 1, fCG);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000911
912 SkASSERT(rowBytesPtr);
913 *rowBytesPtr = rowBytes;
914 return image;
915}
916
bungeman@google.comcefd9812013-05-15 15:07:32 +0000917void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const {
918 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up).
919 // Lion and Leopard return cgVertOffset in CG units (pixels, y up).
920 CGSize cgVertOffset;
921 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1);
922
923 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) };
924 if (isSnowLeopard()) {
925 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
926 fFUnitMatrix.mapPoints(&skVertOffset, 1);
927 } else {
928 // From CG units (pixels, y up) to SkGlyph units (pixels, y down).
929 skVertOffset.fY = -skVertOffset.fY;
930 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +0000931
bungeman@google.comcefd9812013-05-15 15:07:32 +0000932 *offset = skVertOffset;
933}
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000934
935uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
936 if (fFBoundingBoxesGlyphOffset) {
937 return fFBoundingBoxesGlyphOffset;
938 }
939 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
940 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
941 if (hheaTable.fData) {
942 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
943 }
944 return fFBoundingBoxesGlyphOffset;
945}
reed@android.com0680d6c2008-12-19 19:46:15 +0000946
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000947bool SkScalerContext_Mac::generateBBoxes() {
948 if (fGeneratedFBoundingBoxes) {
bsalomon49f085d2014-09-05 13:34:00 -0700949 return SkToBool(fFBoundingBoxes.get());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000950 }
951 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000952
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000953 AutoCGTable<SkOTTableHead> headTable(fCGFont);
954 if (!headTable.fData) {
955 return false;
956 }
957
958 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
959 if (!locaTable.fData) {
960 return false;
961 }
962
963 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
964 if (!glyfTable.fData) {
965 return false;
966 }
967
968 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
bungeman@google.comcefd9812013-05-15 15:07:32 +0000969 fFBoundingBoxes.reset(entries);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000970
971 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
972 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
973 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
974 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
975 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
976 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
977 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
978 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
979 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
980 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
981 }
commit-bot@chromium.org371add82013-06-26 21:08:11 +0000982
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000983 return true;
984}
985
986unsigned SkScalerContext_Mac::generateGlyphCount(void) {
987 return fGlyphCount;
988}
989
990uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000991 CGGlyph cgGlyph[2];
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000992 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit.
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000993
994 // Get the glyph
bungeman@google.comfb1663a2013-10-24 22:32:43 +0000995 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar);
996 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000997
bungeman@google.com72b8cb22013-10-25 17:49:08 +0000998 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
999 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1000 // It is documented that if a mapping is unavailable, the glyph will be set to 0.
1001 CTFontGetGlyphsForCharacters(fCTFont, theChar, cgGlyph, numUniChar);
bungeman@google.comfb1663a2013-10-24 22:32:43 +00001002 return cgGlyph[0];
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001003}
1004
1005void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
1006 this->generateMetrics(glyph);
1007}
1008
bungeman@google.comcefd9812013-05-15 15:07:32 +00001009void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
djsollen1b277042014-08-06 06:58:06 -07001010 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID();
bungeman@google.comcefd9812013-05-15 15:07:32 +00001011 glyph->zeroMetrics();
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001012
bungeman@google.comcefd9812013-05-15 15:07:32 +00001013 // The following block produces cgAdvance in CG units (pixels, y up).
1014 CGSize cgAdvance;
1015 if (fVertical) {
bungeman3b4b66c2015-01-08 08:33:44 -08001016 CTFontGetAdvancesForGlyphs(fCTUnrotatedFont, kCTFontVerticalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001017 &cgGlyph, &cgAdvance, 1);
bungeman3b4b66c2015-01-08 08:33:44 -08001018 // Vertical advances are returned as widths instead of heights.
1019 SkTSwap(cgAdvance.height, cgAdvance.width);
1020 cgAdvance.height = -cgAdvance.height;
bungeman@google.comcefd9812013-05-15 15:07:32 +00001021 } else {
bungeman3b4b66c2015-01-08 08:33:44 -08001022 CTFontGetAdvancesForGlyphs(fCTUnrotatedFont, kCTFontHorizontalOrientation,
bungeman@google.comcefd9812013-05-15 15:07:32 +00001023 &cgGlyph, &cgAdvance, 1);
1024 }
bungeman3b4b66c2015-01-08 08:33:44 -08001025 cgAdvance = CGSizeApplyAffineTransform(cgAdvance, CTFontGetMatrix(fCTFont));
bungeman@google.comcefd9812013-05-15 15:07:32 +00001026 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width);
1027 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001028
bungeman@google.comcefd9812013-05-15 15:07:32 +00001029 // The following produces skBounds in SkGlyph units (pixels, y down),
1030 // or returns early if skBounds would be empty.
1031 SkRect skBounds;
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001032
bungeman@google.comcefd9812013-05-15 15:07:32 +00001033 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and
1034 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts.
1035 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get
1036 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph
1037 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that
1038 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries
1039 // to center the glyph along the vertical baseline and also perform some mysterious shift
1040 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform
1041 // these steps.
1042 //
1043 // It is not known which is correct (or if either is correct). However, we must always draw
1044 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw.
1045 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001046
bungeman@google.comcefd9812013-05-15 15:07:32 +00001047 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and
1048 // returns horizontal bounds.
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001049
bungeman@google.comcefd9812013-05-15 15:07:32 +00001050 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to
1051 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is
1052 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the
1053 // font directly.
1054 if ((isLion() || isMountainLion()) &&
1055 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()))
1056 {
1057 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1058 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) {
1059 return;
1060 }
1061 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1062 // From FUnits (em space, y up) to SkGlyph units (pixels, y down).
1063 fFUnitMatrix.mapRect(&skBounds);
1064
1065 } else {
1066 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up).
1067 CGRect cgBounds;
1068 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation,
1069 &cgGlyph, &cgBounds, 1);
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001070
bungeman@google.comcefd9812013-05-15 15:07:32 +00001071 // BUG?
1072 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
1073 // it should be empty. So, if we see a zero-advance, we check if it has an
1074 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
1075 // is rare, so we won't incur a big performance cost for this extra check.
1076 if (0 == cgAdvance.width && 0 == cgAdvance.height) {
1077 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
1078 if (NULL == path || CGPathIsEmpty(path)) {
1079 return;
1080 }
1081 }
1082
1083 if (CGRectIsEmpty_inline(cgBounds)) {
1084 return;
1085 }
1086
1087 // Convert cgBounds to SkGlyph units (pixels, y down).
1088 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height,
1089 cgBounds.size.width, cgBounds.size.height);
1090 }
1091
1092 if (fVertical) {
1093 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds.
1094 // Convert these horizontal bounds into vertical bounds.
1095 SkPoint offset;
1096 getVerticalOffset(cgGlyph, &offset);
1097 skBounds.offset(offset);
1098 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001099
bungeman@google.comcefd9812013-05-15 15:07:32 +00001100 // Currently the bounds are based on being rendered at (0,0).
1101 // The top left must not move, since that is the base from which subpixel positioning is offset.
1102 if (fDoSubPosition) {
1103 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed());
1104 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed());
1105 }
skia.committer@gmail.com539f3642013-05-16 07:01:00 +00001106
bungeman@google.comcefd9812013-05-15 15:07:32 +00001107 SkIRect skIBounds;
1108 skBounds.roundOut(&skIBounds);
1109 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing.
1110 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset
1111 // is not currently known, as CG dilates the outlines by some percentage.
1112 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset.
1113 skIBounds.outset(1, 1);
1114 glyph->fLeft = SkToS16(skIBounds.fLeft);
1115 glyph->fTop = SkToS16(skIBounds.fTop);
1116 glyph->fWidth = SkToU16(skIBounds.width());
1117 glyph->fHeight = SkToU16(skIBounds.height());
bungeman@google.comcefd9812013-05-15 15:07:32 +00001118}
1119
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001120#include "SkColorPriv.h"
1121
1122static void build_power_table(uint8_t table[], float ee) {
1123 for (int i = 0; i < 256; i++) {
1124 float x = i / 255.f;
1125 x = sk_float_pow(x, ee);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001126 int xx = SkScalarRoundToInt(x * 255);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001127 table[i] = SkToU8(xx);
1128 }
1129}
1130
1131/**
1132 * This will invert the gamma applied by CoreGraphics, so we can get linear
1133 * values.
1134 *
1135 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1136 * The color space used does not appear to affect this choice.
1137 */
1138static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1139 static bool gInited;
1140 static uint8_t gTableCoreGraphicsSmoothing[256];
1141 if (!gInited) {
1142 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1143 gInited = true;
1144 }
1145 return gTableCoreGraphicsSmoothing;
1146}
1147
1148static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1149 while (count > 0) {
1150 uint8_t mask = 0;
1151 for (int i = 7; i >= 0; --i) {
1152 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1153 if (0 == --count) {
1154 break;
1155 }
1156 }
1157 *dst++ = mask;
1158 }
1159}
1160
1161template<bool APPLY_PREBLEND>
1162static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1163 U8CPU r = (rgb >> 16) & 0xFF;
1164 U8CPU g = (rgb >> 8) & 0xFF;
1165 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001166 U8CPU lum = sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1167#if SK_SHOW_TEXT_BLIT_COVERAGE
1168 lum = SkTMax(lum, (U8CPU)0x30);
1169#endif
1170 return lum;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001171}
1172template<bool APPLY_PREBLEND>
1173static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1174 const SkGlyph& glyph, const uint8_t* table8) {
1175 const int width = glyph.fWidth;
1176 size_t dstRB = glyph.rowBytes();
1177 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1178
1179 for (int y = 0; y < glyph.fHeight; y++) {
1180 for (int i = 0; i < width; ++i) {
1181 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1182 }
1183 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1184 dst += dstRB;
1185 }
1186}
1187
1188template<bool APPLY_PREBLEND>
1189static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1190 const uint8_t* tableG,
1191 const uint8_t* tableB) {
1192 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1193 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1194 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
bungeman3b4b66c2015-01-08 08:33:44 -08001195#if SK_SHOW_TEXT_BLIT_COVERAGE
1196 r = SkTMax(r, (U8CPU)0x30);
1197 g = SkTMax(g, (U8CPU)0x30);
1198 b = SkTMax(b, (U8CPU)0x30);
1199#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001200 return SkPack888ToRGB16(r, g, b);
1201}
1202template<bool APPLY_PREBLEND>
1203static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1204 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1205 const int width = glyph.fWidth;
1206 size_t dstRB = glyph.rowBytes();
1207 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1208
1209 for (int y = 0; y < glyph.fHeight; y++) {
1210 for (int i = 0; i < width; i++) {
1211 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1212 }
1213 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1214 dst = (uint16_t*)((char*)dst + dstRB);
1215 }
1216}
1217
bungeman34902632014-12-10 21:43:27 -08001218static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb) {
1219 U8CPU a = (rgb >> 24) & 0xFF;
reed@google.comf77b35d2013-05-02 20:39:44 +00001220 U8CPU r = (rgb >> 16) & 0xFF;
1221 U8CPU g = (rgb >> 8) & 0xFF;
1222 U8CPU b = (rgb >> 0) & 0xFF;
bungeman3b4b66c2015-01-08 08:33:44 -08001223#if SK_SHOW_TEXT_BLIT_COVERAGE
1224 a = SkTMax(a, (U8CPU)0x30);
1225#endif
bungeman34902632014-12-10 21:43:27 -08001226 return SkPackARGB32(a, r, g, b);
reed@google.comf77b35d2013-05-02 20:39:44 +00001227}
reed@google.comf77b35d2013-05-02 20:39:44 +00001228
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001229template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1230 return (T*)((char*)ptr + byteOffset);
1231}
1232
1233void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
djsollen1b277042014-08-06 06:58:06 -07001234 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001235
1236 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1237 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1238
1239 // Draw the glyph
1240 size_t cgRowBytes;
1241 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
1242 if (cgPixels == NULL) {
1243 return;
1244 }
1245
1246 //TODO: see if drawing black on white and inverting is faster (at least in
1247 //lcd case) as core graphics appears to have special case code for drawing
1248 //black text.
1249
1250 // Fix the glyph
1251 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1252 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1253 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1254
1255 //Note that the following cannot really be integrated into the
1256 //pre-blend, since we may not be applying the pre-blend; when we aren't
1257 //applying the pre-blend it means that a filter wants linear anyway.
1258 //Other code may also be applying the pre-blend, so we'd need another
1259 //one with this and one without.
1260 CGRGBPixel* addr = cgPixels;
1261 for (int y = 0; y < glyph.fHeight; ++y) {
1262 for (int x = 0; x < glyph.fWidth; ++x) {
1263 int r = (addr[x] >> 16) & 0xFF;
1264 int g = (addr[x] >> 8) & 0xFF;
1265 int b = (addr[x] >> 0) & 0xFF;
1266 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1267 }
1268 addr = SkTAddByteOffset(addr, cgRowBytes);
1269 }
1270 }
1271
1272 // Convert glyph to mask
1273 switch (glyph.fMaskFormat) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001274 case SkMask::kLCD16_Format: {
1275 if (fPreBlend.isApplicable()) {
1276 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1277 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1278 } else {
1279 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1280 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1281 }
1282 } break;
1283 case SkMask::kA8_Format: {
1284 if (fPreBlend.isApplicable()) {
1285 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1286 } else {
1287 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1288 }
1289 } break;
1290 case SkMask::kBW_Format: {
1291 const int width = glyph.fWidth;
1292 size_t dstRB = glyph.rowBytes();
1293 uint8_t* dst = (uint8_t*)glyph.fImage;
1294 for (int y = 0; y < glyph.fHeight; y++) {
1295 cgpixels_to_bits(dst, cgPixels, width);
1296 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1297 dst += dstRB;
1298 }
1299 } break;
reed@google.comf77b35d2013-05-02 20:39:44 +00001300 case SkMask::kARGB32_Format: {
1301 const int width = glyph.fWidth;
1302 size_t dstRB = glyph.rowBytes();
1303 SkPMColor* dst = (SkPMColor*)glyph.fImage;
1304 for (int y = 0; y < glyph.fHeight; y++) {
1305 for (int x = 0; x < width; ++x) {
bungeman34902632014-12-10 21:43:27 -08001306 dst[x] = cgpixels_to_pmcolor(cgPixels[x]);
reed@google.comf77b35d2013-05-02 20:39:44 +00001307 }
1308 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1309 dst = (SkPMColor*)((char*)dst + dstRB);
1310 }
1311 } break;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001312 default:
1313 SkDEBUGFAIL("unexpected mask format");
1314 break;
1315 }
1316}
1317
1318/*
1319 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1320 * seems sufficient, and possibly even correct, to allow the hinted outline
1321 * to be subpixel positioned.
1322 */
1323#define kScaleForSubPixelPositionHinting (4.0f)
1324
1325void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
1326 CTFontRef font = fCTFont;
1327 SkScalar scaleX = SK_Scalar1;
1328 SkScalar scaleY = SK_Scalar1;
1329
1330 /*
1331 * For subpixel positioning, we want to return an unhinted outline, so it
1332 * can be positioned nicely at fractional offsets. However, we special-case
1333 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1334 * we want to retain hinting in the direction orthogonal to the baseline.
1335 * e.g. for horizontal baseline, we want to retain hinting in Y.
1336 * The way we remove hinting is to scale the font by some value (4) in that
1337 * direction, ask for the path, and then scale the path back down.
1338 */
1339 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1340 SkMatrix m;
1341 fRec.getSingleMatrix(&m);
1342
1343 // start out by assuming that we want no hining in X and Y
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +00001344 scaleX = scaleY = kScaleForSubPixelPositionHinting;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001345 // now see if we need to restore hinting for axis-aligned baselines
1346 switch (SkComputeAxisAlignmentForHText(m)) {
1347 case kX_SkAxisAlignment:
1348 scaleY = SK_Scalar1; // want hinting in the Y direction
1349 break;
1350 case kY_SkAxisAlignment:
1351 scaleX = SK_Scalar1; // want hinting in the X direction
1352 break;
1353 default:
1354 break;
1355 }
1356
1357 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
1358 // need to release font when we're done
1359 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
1360 }
1361
djsollen1b277042014-08-06 06:58:06 -07001362 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001363 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
1364
1365 path->reset();
1366 if (cgPath != NULL) {
1367 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1368 }
1369
bungeman@google.comcefd9812013-05-15 15:07:32 +00001370 if (fDoSubPosition) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001371 SkMatrix m;
1372 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1373 path->transform(m);
1374 // balance the call to CTFontCreateCopyWithAttributes
1375 CFSafeRelease(font);
1376 }
bungeman@google.comcefd9812013-05-15 15:07:32 +00001377 if (fVertical) {
bungeman@google.comcefd9812013-05-15 15:07:32 +00001378 SkPoint offset;
1379 getVerticalOffset(cgGlyph, &offset);
1380 path->offset(offset.fX, offset.fY);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001381 }
1382}
1383
bungeman41078062014-07-07 08:16:37 -07001384void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* metrics) {
1385 if (NULL == metrics) {
1386 return;
1387 }
1388
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001389 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1390
bungeman41078062014-07-07 08:16:37 -07001391 metrics->fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1392 metrics->fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1393 metrics->fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1394 metrics->fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1395 metrics->fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1396 metrics->fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1397 metrics->fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1398 metrics->fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
1399 metrics->fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
1400 metrics->fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont));
1401 metrics->fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont));
commit-bot@chromium.org0bc406d2014-03-01 20:12:26 +00001402
bungeman41078062014-07-07 08:16:37 -07001403 metrics->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag;
1404 metrics->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001405}
1406
1407void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1408 SkPath* skPath = (SkPath*)info;
1409
1410 // Process the path element
1411 switch (element->type) {
1412 case kCGPathElementMoveToPoint:
1413 skPath->moveTo(element->points[0].x, -element->points[0].y);
1414 break;
1415
1416 case kCGPathElementAddLineToPoint:
1417 skPath->lineTo(element->points[0].x, -element->points[0].y);
1418 break;
1419
1420 case kCGPathElementAddQuadCurveToPoint:
1421 skPath->quadTo(element->points[0].x, -element->points[0].y,
1422 element->points[1].x, -element->points[1].y);
1423 break;
1424
1425 case kCGPathElementAddCurveToPoint:
1426 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1427 element->points[1].x, -element->points[1].y,
1428 element->points[2].x, -element->points[2].y);
1429 break;
1430
1431 case kCGPathElementCloseSubpath:
1432 skPath->close();
1433 break;
1434
1435 default:
1436 SkDEBUGFAIL("Unknown path element!");
1437 break;
1438 }
1439}
1440
1441
1442///////////////////////////////////////////////////////////////////////////////
1443
1444// Returns NULL on failure
1445// Call must still manage its ownership of provider
1446static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1447 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
1448 if (NULL == cg) {
1449 return NULL;
1450 }
1451 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
caseq26337e92014-06-30 12:14:52 -07001452 return ct ? NewFromFontRef(ct, NULL, true) : NULL;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001453}
1454
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001455// Web fonts added to the the CTFont registry do not return their character set.
1456// Iterate through the font in this case. The existing caller caches the result,
1457// so the performance impact isn't too bad.
1458static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1459 SkTDArray<SkUnichar>* glyphToUnicode) {
reed@google.com7fa2a652014-01-27 13:42:58 +00001460 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001461 SkUnichar* out = glyphToUnicode->begin();
1462 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1463 UniChar unichar = 0;
1464 while (glyphCount > 0) {
1465 CGGlyph glyph;
1466 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1467 out[glyph] = unichar;
1468 --glyphCount;
1469 }
1470 if (++unichar == 0) {
1471 break;
1472 }
1473 }
1474}
1475
1476// Construct Glyph to Unicode table.
1477// Unicode code points that require conjugate pairs in utf16 are not
1478// supported.
1479static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1480 SkTDArray<SkUnichar>* glyphToUnicode) {
1481 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1482 if (!charSet) {
1483 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1484 return;
1485 }
1486
1487 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1488 charSet));
1489 if (!bitmap) {
1490 return;
1491 }
1492 CFIndex length = CFDataGetLength(bitmap);
1493 if (!length) {
1494 return;
1495 }
1496 if (length > 8192) {
1497 // TODO: Add support for Unicode above 0xFFFF
1498 // Consider only the BMP portion of the Unicode character points.
1499 // The bitmap may contain other planes, up to plane 16.
1500 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1501 length = 8192;
1502 }
1503 const UInt8* bits = CFDataGetBytePtr(bitmap);
reed@google.com7fa2a652014-01-27 13:42:58 +00001504 glyphToUnicode->setCount(SkToInt(glyphCount));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001505 SkUnichar* out = glyphToUnicode->begin();
1506 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1507 for (int i = 0; i < length; i++) {
1508 int mask = bits[i];
1509 if (!mask) {
1510 continue;
1511 }
1512 for (int j = 0; j < 8; j++) {
1513 CGGlyph glyph;
1514 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1515 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1516 out[glyph] = unichar;
1517 }
1518 }
1519 }
1520}
1521
1522static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1523 CGSize advance;
1524 advance.width = 0;
1525 CGGlyph glyph = gId;
1526 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1527 *data = sk_float_round2int(advance.width);
1528 return true;
1529}
1530
bungeman256b3512014-07-02 07:57:59 -07001531/** Assumes src and dst are not NULL. */
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001532static void CFStringToSkString(CFStringRef src, SkString* dst) {
1533 // Reserve enough room for the worst-case string,
1534 // plus 1 byte for the trailing null.
1535 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1536 kCFStringEncodingUTF8) + 1;
1537 dst->resize(length);
1538 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1539 // Resize to the actual UTF-8 length used, stripping the null character.
1540 dst->resize(strlen(dst->c_str()));
1541}
1542
reed@google.com2689f612013-03-20 20:01:47 +00001543SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001544 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1545 const uint32_t* glyphIDs,
reed@google.com2689f612013-03-20 20:01:47 +00001546 uint32_t glyphIDsCount) const {
1547
1548 CTFontRef originalCTFont = fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001549 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
1550 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
1551 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1552
1553 {
1554 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
bungeman256b3512014-07-02 07:57:59 -07001555 if (fontName.get()) {
1556 CFStringToSkString(fontName, &info->fFontName);
1557 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001558 }
1559
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001560 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1561 info->fLastGlyphID = SkToU16(glyphCount - 1);
1562 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
vandebo0f9bad02014-06-19 11:05:39 -07001563 info->fFlags = SkAdvancedTypefaceMetrics::kEmpty_FontFlag;
1564 info->fStyle = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001565
1566 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1567 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1568 }
1569
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001570 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1571 // fonts always have both glyf and loca tables. At the least, this is what
1572 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1573 // succeed in determining this directly.
reed@google.com2689f612013-03-20 20:01:47 +00001574 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001575 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1576 info->fItalicAngle = 0;
1577 info->fAscent = 0;
1578 info->fDescent = 0;
1579 info->fStemV = 0;
1580 info->fCapHeight = 0;
1581 info->fBBox = SkIRect::MakeEmpty();
1582 return info;
1583 }
1584
1585 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1586 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1587 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1588 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1589 }
1590 if (symbolicTraits & kCTFontItalicTrait) {
1591 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1592 }
1593 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001594 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1595 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1596 } else if (stylisticClass & kCTFontScriptsClass) {
1597 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1598 }
1599 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1600 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1601 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1602 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1603 CGRect bbox = CTFontGetBoundingBox(ctFont);
1604
1605 SkRect r;
1606 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1607 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1608 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1609 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1610
1611 r.roundOut(&(info->fBBox));
1612
1613 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1614 // This probably isn't very good with an italic font.
1615 int16_t min_width = SHRT_MAX;
1616 info->fStemV = 0;
1617 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1618 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1619 CGGlyph glyphs[count];
1620 CGRect boundingRects[count];
1621 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1622 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1623 glyphs, boundingRects, count);
1624 for (size_t i = 0; i < count; i++) {
1625 int16_t width = (int16_t) boundingRects[i].size.width;
1626 if (width > 0 && width < min_width) {
1627 min_width = width;
1628 info->fStemV = min_width;
1629 }
1630 }
1631 }
1632
vandebo0f9bad02014-06-19 11:05:39 -07001633 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001634 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1635 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1636 info->fGlyphWidths->fAdvance.append(1, &min_width);
1637 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1638 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1639 } else {
1640 info->fGlyphWidths.reset(
1641 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
reed@google.com7fa2a652014-01-27 13:42:58 +00001642 SkToInt(glyphCount),
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001643 glyphIDs,
1644 glyphIDsCount,
1645 &getWidthAdvance));
1646 }
1647 }
1648 return info;
1649}
1650
1651///////////////////////////////////////////////////////////////////////////////
1652
reed@google.comcc9aad52013-03-21 19:28:10 +00001653static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
1654 CTFontRef ctFont = typeface->fFontRef.get();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001655 AutoCFRelease<CFNumberRef> fontFormatRef(
1656 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1657 if (!fontFormatRef) {
1658 return 0;
1659 }
1660
1661 SInt32 fontFormatValue;
1662 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1663 return 0;
1664 }
1665
1666 switch (fontFormatValue) {
1667 case kCTFontFormatOpenTypePostScript:
1668 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1669 case kCTFontFormatOpenTypeTrueType:
1670 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1671 case kCTFontFormatTrueType:
1672 return SkSFNTHeader::fontType_MacTrueType::TAG;
1673 case kCTFontFormatPostScript:
1674 return SkSFNTHeader::fontType_PostScript::TAG;
1675 case kCTFontFormatBitmap:
1676 return SkSFNTHeader::fontType_MacTrueType::TAG;
1677 case kCTFontFormatUnrecognized:
1678 default:
1679 //CT seems to be unreliable in being able to obtain the type,
1680 //even if all we want is the first four bytes of the font resource.
1681 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1682 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1683 }
1684}
1685
bungeman5f213d92015-01-27 05:39:10 -08001686SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
reed@google.comcc9aad52013-03-21 19:28:10 +00001687 SK_SFNT_ULONG fontType = get_font_type_tag(this);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001688 if (0 == fontType) {
1689 return NULL;
1690 }
1691
1692 // get table tags
reed@google.comcc9aad52013-03-21 19:28:10 +00001693 int numTables = this->countTables();
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001694 SkTDArray<SkFontTableTag> tableTags;
1695 tableTags.setCount(numTables);
reed@google.comcc9aad52013-03-21 19:28:10 +00001696 this->getTableTags(tableTags.begin());
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001697
1698 // calc total size for font, save sizes
1699 SkTDArray<size_t> tableSizes;
1700 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1701 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
reed@google.comcc9aad52013-03-21 19:28:10 +00001702 size_t tableSize = this->getTableSize(tableTags[tableIndex]);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001703 totalSize += (tableSize + 3) & ~3;
1704 *tableSizes.append() = tableSize;
1705 }
1706
1707 // reserve memory for stream, and zero it (tables must be zero padded)
1708 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1709 char* dataStart = (char*)stream->getMemoryBase();
1710 sk_bzero(dataStart, totalSize);
1711 char* dataPtr = dataStart;
1712
1713 // compute font header entries
1714 uint16_t entrySelector = 0;
1715 uint16_t searchRange = 1;
1716 while (searchRange < numTables >> 1) {
1717 entrySelector++;
1718 searchRange <<= 1;
1719 }
1720 searchRange <<= 4;
1721 uint16_t rangeShift = (numTables << 4) - searchRange;
1722
1723 // write font header
1724 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1725 header->fontType = fontType;
1726 header->numTables = SkEndian_SwapBE16(numTables);
1727 header->searchRange = SkEndian_SwapBE16(searchRange);
1728 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1729 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1730 dataPtr += sizeof(SkSFNTHeader);
1731
1732 // write tables
1733 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1734 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1735 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1736 size_t tableSize = tableSizes[tableIndex];
reed@google.comcc9aad52013-03-21 19:28:10 +00001737 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001738 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1739 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1740 tableSize));
reed@google.com7fa2a652014-01-27 13:42:58 +00001741 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart));
1742 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001743
1744 dataPtr += (tableSize + 3) & ~3;
1745 ++entry;
1746 }
1747
bungemanb3310c22015-03-02 09:05:36 -08001748 *ttcIndex = 0;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001749 return stream;
1750}
1751
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001752///////////////////////////////////////////////////////////////////////////////
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001753///////////////////////////////////////////////////////////////////////////////
1754
1755int SkTypeface_Mac::onGetUPEM() const {
1756 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1757 return CGFontGetUnitsPerEm(cgFont);
1758}
1759
bungeman@google.com839702b2013-08-07 17:09:22 +00001760SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const {
bungeman@google.coma9802692013-08-07 02:45:25 +00001761 SkTypeface::LocalizedStrings* nameIter =
1762 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this);
1763 if (NULL == nameIter) {
1764 AutoCFRelease<CFStringRef> cfLanguage;
1765 AutoCFRelease<CFStringRef> cfFamilyName(
1766 CTFontCopyLocalizedName(fFontRef, kCTFontFamilyNameKey, &cfLanguage));
1767
1768 SkString skLanguage;
1769 SkString skFamilyName;
1770 if (cfLanguage.get()) {
1771 CFStringToSkString(cfLanguage.get(), &skLanguage);
1772 } else {
1773 skLanguage = "und"; //undetermined
1774 }
1775 if (cfFamilyName.get()) {
1776 CFStringToSkString(cfFamilyName.get(), &skFamilyName);
1777 }
1778
1779 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage);
1780 }
1781 return nameIter;
1782}
1783
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001784// If, as is the case with web fonts, the CTFont data isn't available,
1785// the CGFont data may work. While the CGFont may always provide the
1786// right result, leave the CTFont code path to minimize disruption.
1787static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1788 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1789 kCTFontTableOptionNoOptions);
1790 if (NULL == data) {
1791 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
1792 data = CGFontCopyTableForTag(cgFont, tag);
1793 }
1794 return data;
1795}
1796
1797int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1798 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1799 kCTFontTableOptionNoOptions));
1800 if (NULL == cfArray) {
1801 return 0;
1802 }
reed@google.com7fa2a652014-01-27 13:42:58 +00001803 int count = SkToInt(CFArrayGetCount(cfArray));
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001804 if (tags) {
1805 for (int i = 0; i < count; ++i) {
1806 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1807 tags[i] = static_cast<SkFontTableTag>(fontTag);
1808 }
1809 }
1810 return count;
1811}
1812
1813size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1814 size_t length, void* dstData) const {
1815 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
1816 if (NULL == srcData) {
1817 return 0;
1818 }
1819
1820 size_t srcSize = CFDataGetLength(srcData);
1821 if (offset >= srcSize) {
1822 return 0;
1823 }
1824 if (length > srcSize - offset) {
1825 length = srcSize - offset;
1826 }
1827 if (dstData) {
1828 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1829 }
1830 return length;
1831}
1832
1833SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
reed@google.com0da48612013-03-19 16:06:52 +00001834 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc);
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001835}
1836
1837void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001838 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag ||
1839 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag)
1840 {
1841 rec->fMaskFormat = SkMask::kA8_Format;
1842 // Render the glyphs as close as possible to what was requested.
1843 // The above turns off subpixel rendering, but the user requested it.
1844 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks.
1845 // See comments below for more details.
1846 rec->setHinting(SkPaint::kNormal_Hinting);
1847 }
skia.committer@gmail.come944de72013-05-07 07:01:03 +00001848
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001849 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
bungeman@google.comf6f56872014-01-23 19:01:36 +00001850 SkScalerContext::kForceAutohinting_Flag |
commit-bot@chromium.orgc5fd4612013-05-06 22:23:08 +00001851 SkScalerContext::kLCD_BGROrder_Flag |
1852 SkScalerContext::kLCD_Vertical_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001853
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001854 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001855
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001856 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001857
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001858 // Only two levels of hinting are supported.
1859 // kNo_Hinting means avoid CoreGraphics outline dilation.
1860 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1861 // If there is no lcd support, hinting (dilation) cannot be supported.
1862 SkPaint::Hinting hinting = rec->getHinting();
1863 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1864 hinting = SkPaint::kNo_Hinting;
1865 } else if (SkPaint::kFull_Hinting == hinting) {
1866 hinting = SkPaint::kNormal_Hinting;
1867 }
1868 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001869
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001870 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1871 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1872 // There is no current means to honor a request for unhinted lcd,
1873 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001874
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001875 // Hinting and smoothing should be orthogonal, but currently they are not.
1876 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
1877 // output is drawn from auto-dilated outlines (the amount of which is
1878 // determined by AppleFontSmoothing). Its regular anti-aliased output is
1879 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001880
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001881 // The behavior of Skia is as follows:
1882 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
1883 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
1884 // channel. This matches [LCD][yes-hint] in weight.
1885 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
1886 // Currenly side with LCD, effectively ignoring the hinting setting.
1887 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001888
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001889 if (isLCDFormat(rec->fMaskFormat)) {
1890 if (lcdSupport) {
1891 //CoreGraphics creates 555 masks for smoothed text anyway.
1892 rec->fMaskFormat = SkMask::kLCD16_Format;
1893 rec->setHinting(SkPaint::kNormal_Hinting);
1894 } else {
1895 rec->fMaskFormat = SkMask::kA8_Format;
1896 }
1897 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001898
bungeman34902632014-12-10 21:43:27 -08001899 // CoreText provides no information as to whether a glyph will be color or not.
1900 // Fonts may mix outlines and bitmaps, so information is needed on a glyph by glyph basis.
1901 // If a font contains an 'sbix' table, consider it to be a color font, and disable lcd.
bungeman98c251b2015-02-23 14:24:04 -08001902 if (fHasColorGlyphs) {
bungeman34902632014-12-10 21:43:27 -08001903 rec->fMaskFormat = SkMask::kARGB32_Format;
1904 }
1905
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001906 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1907 // All other masks can use regular gamma.
1908 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
1909#ifndef SK_GAMMA_APPLY_TO_A8
1910 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00001911#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001912 } else {
1913 //CoreGraphics dialates smoothed text as needed.
1914 rec->setContrast(0);
1915 }
1916}
1917
1918// we take ownership of the ref
1919static const char* get_str(CFStringRef ref, SkString* str) {
bungeman256b3512014-07-02 07:57:59 -07001920 if (NULL == ref) {
1921 return NULL;
1922 }
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001923 CFStringToSkString(ref, str);
1924 CFSafeRelease(ref);
1925 return str->c_str();
1926}
1927
bungemanb374d6a2014-09-17 07:48:59 -07001928void SkTypeface_Mac::onGetFamilyName(SkString* familyName) const {
1929 get_str(CTFontCopyFamilyName(fFontRef), familyName);
1930}
1931
reed@google.com5526ede2013-03-25 13:03:37 +00001932void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc,
1933 bool* isLocalStream) const {
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001934 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001935
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001936 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
1937 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
1938 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
caseq26337e92014-06-30 12:14:52 -07001939 *isLocalStream = fIsLocalStream;
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001940}
reed@google.com5526ede2013-03-25 13:03:37 +00001941
reed@google.combcb42ae2013-07-02 13:56:39 +00001942int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding,
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001943 uint16_t glyphs[], int glyphCount) const
1944{
1945 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points:
1946 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate.
1947 // 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 +00001948
reed@google.combcb42ae2013-07-02 13:56:39 +00001949 SkAutoSTMalloc<1024, UniChar> charStorage;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001950 const UniChar* src; // UniChar is a UTF-16 16-bit code unit.
1951 int srcCount;
reed@google.combcb42ae2013-07-02 13:56:39 +00001952 switch (encoding) {
1953 case kUTF8_Encoding: {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001954 const char* utf8 = reinterpret_cast<const char*>(chars);
1955 UniChar* utf16 = charStorage.reset(2 * glyphCount);
1956 src = utf16;
reed@google.combcb42ae2013-07-02 13:56:39 +00001957 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001958 SkUnichar uni = SkUTF8_NextUnichar(&utf8);
1959 utf16 += SkUTF16_FromUnichar(uni, utf16);
reed@google.combcb42ae2013-07-02 13:56:39 +00001960 }
reed@google.com7fa2a652014-01-27 13:42:58 +00001961 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00001962 break;
1963 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001964 case kUTF16_Encoding: {
1965 src = reinterpret_cast<const UniChar*>(chars);
1966 int extra = 0;
reed@google.combcb42ae2013-07-02 13:56:39 +00001967 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001968 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
1969 ++extra;
1970 }
reed@google.combcb42ae2013-07-02 13:56:39 +00001971 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001972 srcCount = glyphCount + extra;
1973 break;
1974 }
1975 case kUTF32_Encoding: {
1976 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars);
1977 UniChar* utf16 = charStorage.reset(2 * glyphCount);
1978 src = utf16;
1979 for (int i = 0; i < glyphCount; ++i) {
1980 utf16 += SkUTF16_FromUnichar(utf32[i], utf16);
1981 }
reed@google.com7fa2a652014-01-27 13:42:58 +00001982 srcCount = SkToInt(utf16 - src);
reed@google.combcb42ae2013-07-02 13:56:39 +00001983 break;
1984 }
1985 }
1986
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001987 // If glyphs is NULL, CT still needs glyph storage for finding the first failure.
1988 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate.
reed@google.combcb42ae2013-07-02 13:56:39 +00001989 SkAutoSTMalloc<1024, uint16_t> glyphStorage;
1990 uint16_t* macGlyphs = glyphs;
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001991 if (NULL == macGlyphs || srcCount > glyphCount) {
1992 macGlyphs = glyphStorage.reset(srcCount);
reed@google.combcb42ae2013-07-02 13:56:39 +00001993 }
1994
bungeman@google.com72b8cb22013-10-25 17:49:08 +00001995 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, srcCount);
1996
1997 // If there were any non-bmp, then copy and compact.
1998 // If 'glyphs' is NULL, then compact glyphStorage in-place.
1999 // If all are bmp and 'glyphs' is non-NULL, 'glyphs' already contains the compact glyphs.
2000 // If some are non-bmp and 'glyphs' is non-NULL, copy and compact into 'glyphs'.
2001 uint16_t* compactedGlyphs = glyphs;
2002 if (NULL == compactedGlyphs) {
2003 compactedGlyphs = macGlyphs;
2004 }
2005 if (srcCount > glyphCount) {
2006 int extra = 0;
2007 for (int i = 0; i < glyphCount; ++i) {
bungeman7b09aab2014-09-24 11:04:41 -07002008 compactedGlyphs[i] = macGlyphs[i + extra];
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002009 if (SkUTF16_IsHighSurrogate(src[i + extra])) {
2010 ++extra;
2011 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002012 }
2013 }
2014
2015 if (allEncoded) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002016 return glyphCount;
2017 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002018
2019 // If we got false, then we need to manually look for first failure.
reed@google.combcb42ae2013-07-02 13:56:39 +00002020 for (int i = 0; i < glyphCount; ++i) {
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002021 if (0 == compactedGlyphs[i]) {
reed@google.combcb42ae2013-07-02 13:56:39 +00002022 return i;
2023 }
2024 }
bungeman@google.com72b8cb22013-10-25 17:49:08 +00002025 // Odd to get here, as we expected CT to have returned true up front.
reed@google.combcb42ae2013-07-02 13:56:39 +00002026 return glyphCount;
2027}
2028
2029int SkTypeface_Mac::onCountGlyphs() const {
reed@google.com7fa2a652014-01-27 13:42:58 +00002030 return SkToInt(CTFontGetGlyphCount(fFontRef));
reed@google.combcb42ae2013-07-02 13:56:39 +00002031}
2032
reed@google.com95625db2013-03-25 20:44:02 +00002033///////////////////////////////////////////////////////////////////////////////
2034///////////////////////////////////////////////////////////////////////////////
reed@google.com83787c52013-03-26 17:19:15 +00002035
2036static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) {
2037 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name));
2038 if (NULL == ref.get()) {
2039 return false;
2040 }
2041 CFStringToSkString(ref, value);
2042 return true;
2043}
2044
reed@google.com95625db2013-03-25 20:44:02 +00002045#include "SkFontMgr.h"
2046
reed@google.com964988f2013-03-29 14:57:22 +00002047static inline int sqr(int value) {
2048 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow
2049 return value * value;
2050}
2051
2052// We normalize each axis (weight, width, italic) to be base-900
2053static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) {
2054 return sqr(a.weight() - b.weight()) +
2055 sqr((a.width() - b.width()) * 100) +
2056 sqr((a.isItalic() != b.isItalic()) * 900);
2057}
2058
bungemana4c4a2d2014-10-20 13:33:19 -07002059static SkTypeface* createFromDesc(CFStringRef cfFamilyName, CTFontDescriptorRef desc) {
bungeman967937c2014-10-30 11:49:27 -07002060 NameStyle cacheRequest;
2061 SkString skFamilyName;
2062 CFStringToSkString(cfFamilyName, &skFamilyName);
2063 cacheRequest.fName = skFamilyName.c_str();
bungeman336fdf22014-11-10 07:48:55 -08002064 cacheRequest.fStyle = fontstyle_from_descriptor(desc);
reed@google.comdea7ee02013-03-28 14:12:10 +00002065
bungeman967937c2014-10-30 11:49:27 -07002066 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_NameStyle, &cacheRequest);
reed@google.comdea7ee02013-03-28 14:12:10 +00002067 if (face) {
2068 return face;
2069 }
2070
commit-bot@chromium.org45dfe6b2013-12-10 17:59:04 +00002071 AutoCFRelease<CFDictionaryRef> fontFamilyNameDictionary(
2072 CFDictionaryCreate(kCFAllocatorDefault,
2073 (const void**)&kCTFontFamilyNameAttribute, (const void**)&cfFamilyName,
2074 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
2075 AutoCFRelease<CTFontDescriptorRef> fontDescriptor(
2076 CTFontDescriptorCreateWithAttributes(fontFamilyNameDictionary));
2077 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithFontDescriptor(fontDescriptor, 0, NULL));
reed@google.comce8b3de2013-03-26 19:30:16 +00002078 CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc);
2079 if (NULL == ctFont) {
2080 return NULL;
2081 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002082
reed@google.comce8b3de2013-03-26 19:30:16 +00002083 bool isFixedPitch;
bungeman336fdf22014-11-10 07:48:55 -08002084 (void)computeStyleBits(ctFont, &isFixedPitch);
reed@google.comce8b3de2013-03-26 19:30:16 +00002085 SkFontID fontID = CTFontRef_to_SkFontID(ctFont);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002086
bungeman967937c2014-10-30 11:49:27 -07002087 face = SkNEW_ARGS(SkTypeface_Mac, (cacheRequest.fStyle, fontID, isFixedPitch,
2088 ctFont, skFamilyName.c_str(), false));
bungemana4c4a2d2014-10-20 13:33:19 -07002089 SkTypefaceCache::Add(face, face->fontStyle());
reed@google.comdea7ee02013-03-28 14:12:10 +00002090 return face;
reed@google.comce8b3de2013-03-26 19:30:16 +00002091}
2092
reed@google.com83787c52013-03-26 17:19:15 +00002093class SkFontStyleSet_Mac : public SkFontStyleSet {
reed@google.com95625db2013-03-25 20:44:02 +00002094public:
reed@google.com83787c52013-03-26 17:19:15 +00002095 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc)
2096 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, NULL))
reed@google.comdea7ee02013-03-28 14:12:10 +00002097 , fFamilyName(familyName)
2098 , fCount(0) {
reed@google.com83787c52013-03-26 17:19:15 +00002099 CFRetain(familyName);
reed@google.comdea7ee02013-03-28 14:12:10 +00002100 if (NULL == fArray) {
2101 fArray = CFArrayCreate(NULL, NULL, 0, NULL);
2102 }
reed@google.com7fa2a652014-01-27 13:42:58 +00002103 fCount = SkToInt(CFArrayGetCount(fArray));
reed@google.com83787c52013-03-26 17:19:15 +00002104 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002105
reed@google.com83787c52013-03-26 17:19:15 +00002106 virtual ~SkFontStyleSet_Mac() {
reed@google.comdea7ee02013-03-28 14:12:10 +00002107 CFRelease(fArray);
reed@google.com83787c52013-03-26 17:19:15 +00002108 CFRelease(fFamilyName);
2109 }
2110
mtklein36352bf2015-03-25 18:17:31 -07002111 int count() override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002112 return fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002113 }
2114
mtklein36352bf2015-03-25 18:17:31 -07002115 void getStyle(int index, SkFontStyle* style, SkString* name) override {
reed@google.comdea7ee02013-03-28 14:12:10 +00002116 SkASSERT((unsigned)index < (unsigned)fCount);
reed@google.com83787c52013-03-26 17:19:15 +00002117 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
2118 if (style) {
bungeman336fdf22014-11-10 07:48:55 -08002119 *style = fontstyle_from_descriptor(desc);
reed@google.com83787c52013-03-26 17:19:15 +00002120 }
2121 if (name) {
2122 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) {
2123 name->reset();
2124 }
2125 }
2126 }
2127
mtklein36352bf2015-03-25 18:17:31 -07002128 SkTypeface* createTypeface(int index) override {
reed@google.com83787c52013-03-26 17:19:15 +00002129 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray));
2130 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index);
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002131
reed@google.com83787c52013-03-26 17:19:15 +00002132 return createFromDesc(fFamilyName, desc);
2133 }
skia.committer@gmail.com37cbc7f2013-03-27 07:01:04 +00002134
mtklein36352bf2015-03-25 18:17:31 -07002135 SkTypeface* matchStyle(const SkFontStyle& pattern) override {
reed@google.com964988f2013-03-29 14:57:22 +00002136 if (0 == fCount) {
2137 return NULL;
2138 }
2139 return createFromDesc(fFamilyName, findMatchingDesc(pattern));
2140 }
2141
reed@google.com83787c52013-03-26 17:19:15 +00002142private:
2143 CFArrayRef fArray;
2144 CFStringRef fFamilyName;
reed@google.comdea7ee02013-03-28 14:12:10 +00002145 int fCount;
reed@google.com964988f2013-03-29 14:57:22 +00002146
2147 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const {
2148 int bestMetric = SK_MaxS32;
2149 CTFontDescriptorRef bestDesc = NULL;
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002150
reed@google.com964988f2013-03-29 14:57:22 +00002151 for (int i = 0; i < fCount; ++i) {
2152 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i);
bungeman336fdf22014-11-10 07:48:55 -08002153 int metric = compute_metric(pattern, fontstyle_from_descriptor(desc));
reed@google.com964988f2013-03-29 14:57:22 +00002154 if (0 == metric) {
2155 return desc;
2156 }
2157 if (metric < bestMetric) {
2158 bestMetric = metric;
2159 bestDesc = desc;
2160 }
2161 }
2162 SkASSERT(bestDesc);
2163 return bestDesc;
2164 }
reed@google.com83787c52013-03-26 17:19:15 +00002165};
2166
2167class SkFontMgr_Mac : public SkFontMgr {
reed@google.com83787c52013-03-26 17:19:15 +00002168 CFArrayRef fNames;
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002169 int fCount;
reed@google.com83787c52013-03-26 17:19:15 +00002170
2171 CFStringRef stringAt(int index) const {
2172 SkASSERT((unsigned)index < (unsigned)fCount);
2173 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index);
2174 }
2175
reed@google.com964988f2013-03-29 14:57:22 +00002176 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) {
2177 AutoCFRelease<CFMutableDictionaryRef> cfAttr(
2178 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
2179 &kCFTypeDictionaryKeyCallBacks,
2180 &kCFTypeDictionaryValueCallBacks));
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002181
reed@google.com964988f2013-03-29 14:57:22 +00002182 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName);
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002183
reed@google.com964988f2013-03-29 14:57:22 +00002184 AutoCFRelease<CTFontDescriptorRef> desc(
2185 CTFontDescriptorCreateWithAttributes(cfAttr));
2186 return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc));
2187 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002188
reed@google.com83787c52013-03-26 17:19:15 +00002189public:
commit-bot@chromium.org967dee32014-02-04 22:35:01 +00002190 SkFontMgr_Mac()
2191 : fNames(SkCTFontManagerCopyAvailableFontFamilyNames())
2192 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames)) : 0) {}
reed@google.com83787c52013-03-26 17:19:15 +00002193
2194 virtual ~SkFontMgr_Mac() {
2195 CFSafeRelease(fNames);
2196 }
reed@google.com95625db2013-03-25 20:44:02 +00002197
2198protected:
mtklein36352bf2015-03-25 18:17:31 -07002199 int onCountFamilies() const override {
reed@google.com83787c52013-03-26 17:19:15 +00002200 return fCount;
reed@google.com95625db2013-03-25 20:44:02 +00002201 }
2202
mtklein36352bf2015-03-25 18:17:31 -07002203 void onGetFamilyName(int index, SkString* familyName) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002204 if ((unsigned)index < (unsigned)fCount) {
2205 CFStringToSkString(this->stringAt(index), familyName);
2206 } else {
2207 familyName->reset();
2208 }
reed@google.com95625db2013-03-25 20:44:02 +00002209 }
2210
mtklein36352bf2015-03-25 18:17:31 -07002211 SkFontStyleSet* onCreateStyleSet(int index) const override {
reed@google.com83787c52013-03-26 17:19:15 +00002212 if ((unsigned)index >= (unsigned)fCount) {
2213 return NULL;
2214 }
reed@google.com964988f2013-03-29 14:57:22 +00002215 return CreateSet(this->stringAt(index));
reed@google.com95625db2013-03-25 20:44:02 +00002216 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002217
mtklein36352bf2015-03-25 18:17:31 -07002218 SkFontStyleSet* onMatchFamily(const char familyName[]) const override {
reed@google.com964988f2013-03-29 14:57:22 +00002219 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName));
2220 return CreateSet(cfName);
2221 }
skia.committer@gmail.comd55846d2013-03-30 07:01:27 +00002222
reed@google.com95625db2013-03-25 20:44:02 +00002223 virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002224 const SkFontStyle&) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002225 return NULL;
2226 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002227
djsollen33068c12014-11-14 10:52:53 -08002228 virtual SkTypeface* onMatchFamilyStyleCharacter(const char familyName[], const SkFontStyle&,
2229 const char* bcp47[], int bcp47Count,
mtklein36352bf2015-03-25 18:17:31 -07002230 SkUnichar character) const override {
djsollen33068c12014-11-14 10:52:53 -08002231 return NULL;
2232 }
2233
reed@google.com95625db2013-03-25 20:44:02 +00002234 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember,
mtklein36352bf2015-03-25 18:17:31 -07002235 const SkFontStyle&) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002236 return NULL;
2237 }
skia.committer@gmail.come60ed082013-03-26 07:01:04 +00002238
mtklein36352bf2015-03-25 18:17:31 -07002239 SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002240 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data));
2241 if (NULL == pr) {
2242 return NULL;
2243 }
2244 return create_from_dataProvider(pr);
2245 }
2246
mtklein36352bf2015-03-25 18:17:31 -07002247 SkTypeface* onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002248 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream));
2249 if (NULL == pr) {
2250 return NULL;
2251 }
2252 return create_from_dataProvider(pr);
2253 }
2254
mtklein36352bf2015-03-25 18:17:31 -07002255 SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
reed@google.com95625db2013-03-25 20:44:02 +00002256 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
2257 if (NULL == pr) {
2258 return NULL;
2259 }
2260 return create_from_dataProvider(pr);
2261 }
skia.committer@gmail.com76015b02013-07-31 07:01:00 +00002262
reed@google.com7fdcd442013-07-30 21:25:49 +00002263 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
mtklein36352bf2015-03-25 18:17:31 -07002264 unsigned styleBits) const override {
bungemana4c4a2d2014-10-20 13:33:19 -07002265
2266 SkFontStyle style = SkFontStyle((SkTypeface::Style)styleBits);
2267 if (familyName) {
2268 familyName = map_css_names(familyName);
2269 }
2270
2271 if (!familyName || !*familyName) {
2272 familyName = FONT_DEFAULT_NAME;
2273 }
2274
bungeman967937c2014-10-30 11:49:27 -07002275 NameStyle cacheRequest = { familyName, style };
2276 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_by_NameStyle, &cacheRequest);
bungemana4c4a2d2014-10-20 13:33:19 -07002277
2278 if (NULL == face) {
2279 face = NewFromName(familyName, style);
2280 if (face) {
2281 SkTypefaceCache::Add(face, style);
2282 } else {
2283 face = GetDefaultFace();
2284 face->ref();
2285 }
2286 }
2287 return face;
reed@google.com7fdcd442013-07-30 21:25:49 +00002288 }
reed@google.com95625db2013-03-25 20:44:02 +00002289};
2290
reed@google.com7fdcd442013-07-30 21:25:49 +00002291///////////////////////////////////////////////////////////////////////////////
2292
reed@google.com95625db2013-03-25 20:44:02 +00002293SkFontMgr* SkFontMgr::Factory() {
2294 return SkNEW(SkFontMgr_Mac);
2295}