|
1 |
|
2 /* |
|
3 * 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 |
|
9 #include <vector> |
|
10 #ifdef SK_BUILD_FOR_MAC |
|
11 #import <ApplicationServices/ApplicationServices.h> |
|
12 #endif |
|
13 |
|
14 #ifdef SK_BUILD_FOR_IOS |
|
15 #include <CoreText/CoreText.h> |
|
16 #include <CoreText/CTFontManager.h> |
|
17 #include <CoreGraphics/CoreGraphics.h> |
|
18 #include <CoreFoundation/CoreFoundation.h> |
|
19 #endif |
|
20 |
|
21 #include "SkFontHost.h" |
|
22 #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" |
|
29 #include "SkMaskGamma.h" |
|
30 #include "SkSFNTHeader.h" |
|
31 #include "SkOTTable_glyf.h" |
|
32 #include "SkOTTable_head.h" |
|
33 #include "SkOTTable_hhea.h" |
|
34 #include "SkOTTable_loca.h" |
|
35 #include "SkOTUtils.h" |
|
36 #include "SkPaint.h" |
|
37 #include "SkPath.h" |
|
38 #include "SkString.h" |
|
39 #include "SkStream.h" |
|
40 #include "SkThread.h" |
|
41 #include "SkTypeface_mac.h" |
|
42 #include "SkUtils.h" |
|
43 #include "SkTypefaceCache.h" |
|
44 #include "SkFontMgr.h" |
|
45 #include "SkUtils.h" |
|
46 |
|
47 //#define HACK_COLORGLYPHS |
|
48 |
|
49 class SkScalerContext_Mac; |
|
50 |
|
51 // CTFontManagerCopyAvailableFontFamilyNames() is not always available, so we |
|
52 // provide a wrapper here that will return an empty array if need be. |
|
53 static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() { |
|
54 #ifdef SK_BUILD_FOR_IOS |
|
55 return CFArrayCreate(NULL, NULL, 0, NULL); |
|
56 #else |
|
57 return CTFontManagerCopyAvailableFontFamilyNames(); |
|
58 #endif |
|
59 } |
|
60 |
|
61 |
|
62 // Being templated and taking const T* prevents calling |
|
63 // CFSafeRelease(autoCFRelease) through implicit conversion. |
|
64 template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) { |
|
65 if (cfTypeRef) { |
|
66 CFRelease(cfTypeRef); |
|
67 } |
|
68 } |
|
69 |
|
70 // Being templated and taking const T* prevents calling |
|
71 // CFSafeRetain(autoCFRelease) through implicit conversion. |
|
72 template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) { |
|
73 if (cfTypeRef) { |
|
74 CFRetain(cfTypeRef); |
|
75 } |
|
76 } |
|
77 |
|
78 /** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */ |
|
79 template<typename CFRef> class AutoCFRelease : private SkNoncopyable { |
|
80 public: |
|
81 explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { } |
|
82 ~AutoCFRelease() { CFSafeRelease(fCFRef); } |
|
83 |
|
84 void reset(CFRef that = NULL) { |
|
85 CFSafeRetain(that); |
|
86 CFSafeRelease(fCFRef); |
|
87 fCFRef = that; |
|
88 } |
|
89 |
|
90 AutoCFRelease& operator =(CFRef that) { |
|
91 reset(that); |
|
92 return *this; |
|
93 } |
|
94 |
|
95 operator CFRef() const { return fCFRef; } |
|
96 CFRef get() const { return fCFRef; } |
|
97 |
|
98 CFRef* operator&() { SkASSERT(fCFRef == NULL); return &fCFRef; } |
|
99 private: |
|
100 CFRef fCFRef; |
|
101 }; |
|
102 |
|
103 static CFStringRef make_CFString(const char str[]) { |
|
104 return CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8); |
|
105 } |
|
106 |
|
107 template<typename T> class AutoCGTable : SkNoncopyable { |
|
108 public: |
|
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 |
|
117 private: |
|
118 AutoCFRelease<CFDataRef> fCFData; |
|
119 public: |
|
120 const T* fData; |
|
121 }; |
|
122 |
|
123 // inline versions of these rect helpers |
|
124 |
|
125 static bool CGRectIsEmpty_inline(const CGRect& rect) { |
|
126 return rect.size.width <= 0 || rect.size.height <= 0; |
|
127 } |
|
128 |
|
129 static CGFloat CGRectGetMinX_inline(const CGRect& rect) { |
|
130 return rect.origin.x; |
|
131 } |
|
132 |
|
133 static CGFloat CGRectGetMaxX_inline(const CGRect& rect) { |
|
134 return rect.origin.x + rect.size.width; |
|
135 } |
|
136 |
|
137 static CGFloat CGRectGetMinY_inline(const CGRect& rect) { |
|
138 return rect.origin.y; |
|
139 } |
|
140 |
|
141 static CGFloat CGRectGetMaxY_inline(const CGRect& rect) { |
|
142 return rect.origin.y + rect.size.height; |
|
143 } |
|
144 |
|
145 static CGFloat CGRectGetWidth_inline(const CGRect& rect) { |
|
146 return rect.size.width; |
|
147 } |
|
148 |
|
149 /////////////////////////////////////////////////////////////////////////////// |
|
150 |
|
151 static void sk_memset_rect32(uint32_t* ptr, uint32_t value, |
|
152 int width, int height, size_t rowBytes) { |
|
153 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 |
|
197 typedef uint32_t CGRGBPixel; |
|
198 |
|
199 static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) { |
|
200 return pixel & 0xFF; |
|
201 } |
|
202 |
|
203 // The calls to support subpixel are present in 10.5, but are not included in |
|
204 // the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are |
|
205 // included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for |
|
206 // instance, is present in the 10.5 CoreGraphics libary, use: |
|
207 // cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/ |
|
208 // cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/ |
|
209 // nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts |
|
210 |
|
211 #if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6) |
|
212 CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value); |
|
213 CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value); |
|
214 CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value); |
|
215 CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value); |
|
216 CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value); |
|
217 #endif |
|
218 |
|
219 static const char FONT_DEFAULT_NAME[] = "Lucida Sans"; |
|
220 |
|
221 // See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source. |
|
222 static int readVersion() { |
|
223 struct utsname info; |
|
224 if (uname(&info) != 0) { |
|
225 SkDebugf("uname failed\n"); |
|
226 return 0; |
|
227 } |
|
228 if (strcmp(info.sysname, "Darwin") != 0) { |
|
229 SkDebugf("unexpected uname sysname %s\n", info.sysname); |
|
230 return 0; |
|
231 } |
|
232 char* dot = strchr(info.release, '.'); |
|
233 if (!dot) { |
|
234 SkDebugf("expected dot in uname release %s\n", info.release); |
|
235 return 0; |
|
236 } |
|
237 int version = atoi(info.release); |
|
238 if (version == 0) { |
|
239 SkDebugf("could not parse uname release %s\n", info.release); |
|
240 } |
|
241 return version; |
|
242 } |
|
243 |
|
244 static int darwinVersion() { |
|
245 static int darwin_version = readVersion(); |
|
246 return darwin_version; |
|
247 } |
|
248 |
|
249 static bool isSnowLeopard() { |
|
250 return darwinVersion() == 10; |
|
251 } |
|
252 |
|
253 static bool isLion() { |
|
254 return darwinVersion() == 11; |
|
255 } |
|
256 |
|
257 static bool isMountainLion() { |
|
258 return darwinVersion() == 12; |
|
259 } |
|
260 |
|
261 static bool isLCDFormat(unsigned format) { |
|
262 return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format; |
|
263 } |
|
264 |
|
265 static CGFloat ScalarToCG(SkScalar scalar) { |
|
266 if (sizeof(CGFloat) == sizeof(float)) { |
|
267 return SkScalarToFloat(scalar); |
|
268 } else { |
|
269 SkASSERT(sizeof(CGFloat) == sizeof(double)); |
|
270 return (CGFloat) SkScalarToDouble(scalar); |
|
271 } |
|
272 } |
|
273 |
|
274 static SkScalar CGToScalar(CGFloat cgFloat) { |
|
275 if (sizeof(CGFloat) == sizeof(float)) { |
|
276 return cgFloat; |
|
277 } else { |
|
278 SkASSERT(sizeof(CGFloat) == sizeof(double)); |
|
279 return SkDoubleToScalar(cgFloat); |
|
280 } |
|
281 } |
|
282 |
|
283 static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix, |
|
284 SkScalar sx = SK_Scalar1, |
|
285 SkScalar sy = SK_Scalar1) { |
|
286 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx), |
|
287 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy), |
|
288 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx), |
|
289 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy), |
|
290 ScalarToCG(matrix[SkMatrix::kMTransX] * sx), |
|
291 ScalarToCG(matrix[SkMatrix::kMTransY] * sy)); |
|
292 } |
|
293 |
|
294 /////////////////////////////////////////////////////////////////////////////// |
|
295 |
|
296 #define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) |
|
297 #define BITMAP_INFO_GRAY (kCGImageAlphaNone) |
|
298 |
|
299 /** |
|
300 * There does not appear to be a publicly accessable API for determining if lcd |
|
301 * font smoothing will be applied if we request it. The main issue is that if |
|
302 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0. |
|
303 */ |
|
304 static bool supports_LCD() { |
|
305 static int gSupportsLCD = -1; |
|
306 if (gSupportsLCD >= 0) { |
|
307 return (bool) gSupportsLCD; |
|
308 } |
|
309 uint32_t rgb = 0; |
|
310 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB()); |
|
311 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4, |
|
312 colorspace, BITMAP_INFO_RGB)); |
|
313 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman); |
|
314 CGContextSetShouldSmoothFonts(cgContext, true); |
|
315 CGContextSetShouldAntialias(cgContext, true); |
|
316 CGContextSetTextDrawingMode(cgContext, kCGTextFill); |
|
317 CGContextSetGrayFillColor(cgContext, 1, 1); |
|
318 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1); |
|
319 uint32_t r = (rgb >> 16) & 0xFF; |
|
320 uint32_t g = (rgb >> 8) & 0xFF; |
|
321 uint32_t b = (rgb >> 0) & 0xFF; |
|
322 gSupportsLCD = (r != g || r != b); |
|
323 return (bool) gSupportsLCD; |
|
324 } |
|
325 |
|
326 class Offscreen { |
|
327 public: |
|
328 Offscreen(); |
|
329 |
|
330 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph, |
|
331 CGGlyph glyphID, size_t* rowBytesPtr, |
|
332 bool generateA8FromLCD); |
|
333 |
|
334 private: |
|
335 enum { |
|
336 kSize = 32 * 32 * sizeof(CGRGBPixel) |
|
337 }; |
|
338 SkAutoSMalloc<kSize> fImageStorage; |
|
339 AutoCFRelease<CGColorSpaceRef> fRGBSpace; |
|
340 |
|
341 // cached state |
|
342 AutoCFRelease<CGContextRef> fCG; |
|
343 SkISize fSize; |
|
344 bool fDoAA; |
|
345 bool fDoLCD; |
|
346 |
|
347 static int RoundSize(int dimension) { |
|
348 return SkNextPow2(dimension); |
|
349 } |
|
350 }; |
|
351 |
|
352 Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL), |
|
353 fDoAA(false), fDoLCD(false) { |
|
354 fSize.set(0, 0); |
|
355 } |
|
356 |
|
357 /////////////////////////////////////////////////////////////////////////////// |
|
358 |
|
359 static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isFixedPitch) { |
|
360 unsigned style = SkTypeface::kNormal; |
|
361 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font); |
|
362 |
|
363 if (traits & kCTFontBoldTrait) { |
|
364 style |= SkTypeface::kBold; |
|
365 } |
|
366 if (traits & kCTFontItalicTrait) { |
|
367 style |= SkTypeface::kItalic; |
|
368 } |
|
369 if (isFixedPitch) { |
|
370 *isFixedPitch = (traits & kCTFontMonoSpaceTrait) != 0; |
|
371 } |
|
372 return (SkTypeface::Style)style; |
|
373 } |
|
374 |
|
375 static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) { |
|
376 SkFontID id = 0; |
|
377 // CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to |
|
378 // bracket this to be Mac only. |
|
379 #ifdef SK_BUILD_FOR_MAC |
|
380 ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL); |
|
381 id = (SkFontID)ats; |
|
382 if (id != 0) { |
|
383 id &= 0x3FFFFFFF; // make top two bits 00 |
|
384 return id; |
|
385 } |
|
386 #endif |
|
387 // CTFontGetPlatformFont returns NULL if the font is local |
|
388 // (e.g., was created by a CSS3 @font-face rule). |
|
389 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL)); |
|
390 AutoCGTable<SkOTTableHead> headTable(cgFont); |
|
391 if (headTable.fData) { |
|
392 id = (SkFontID) headTable->checksumAdjustment; |
|
393 id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01 |
|
394 } |
|
395 // well-formed fonts have checksums, but as a last resort, use the pointer. |
|
396 if (id == 0) { |
|
397 id = (SkFontID) (uintptr_t) fontRef; |
|
398 id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10 |
|
399 } |
|
400 return id; |
|
401 } |
|
402 |
|
403 static SkFontStyle stylebits2fontstyle(SkTypeface::Style styleBits) { |
|
404 return SkFontStyle((styleBits & SkTypeface::kBold) |
|
405 ? SkFontStyle::kBold_Weight |
|
406 : SkFontStyle::kNormal_Weight, |
|
407 SkFontStyle::kNormal_Width, |
|
408 (styleBits & SkTypeface::kItalic) |
|
409 ? SkFontStyle::kItalic_Slant |
|
410 : SkFontStyle::kUpright_Slant); |
|
411 } |
|
412 |
|
413 #define WEIGHT_THRESHOLD ((SkFontStyle::kNormal_Weight + SkFontStyle::kBold_Weight)/2) |
|
414 |
|
415 static SkTypeface::Style fontstyle2stylebits(const SkFontStyle& fs) { |
|
416 unsigned style = 0; |
|
417 if (fs.width() >= WEIGHT_THRESHOLD) { |
|
418 style |= SkTypeface::kBold; |
|
419 } |
|
420 if (fs.isItalic()) { |
|
421 style |= SkTypeface::kItalic; |
|
422 } |
|
423 return (SkTypeface::Style)style; |
|
424 } |
|
425 |
|
426 class SkTypeface_Mac : public SkTypeface { |
|
427 public: |
|
428 SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isFixedPitch, |
|
429 CTFontRef fontRef, const char name[]) |
|
430 : SkTypeface(style, fontID, isFixedPitch) |
|
431 , fName(name) |
|
432 , fFontRef(fontRef) // caller has already called CFRetain for us |
|
433 , fFontStyle(stylebits2fontstyle(style)) |
|
434 { |
|
435 SkASSERT(fontRef); |
|
436 } |
|
437 |
|
438 SkTypeface_Mac(const SkFontStyle& fs, SkFontID fontID, bool isFixedPitch, |
|
439 CTFontRef fontRef, const char name[]) |
|
440 : SkTypeface(fontstyle2stylebits(fs), fontID, isFixedPitch) |
|
441 , fName(name) |
|
442 , fFontRef(fontRef) // caller has already called CFRetain for us |
|
443 , fFontStyle(fs) |
|
444 { |
|
445 SkASSERT(fontRef); |
|
446 } |
|
447 |
|
448 SkString fName; |
|
449 AutoCFRelease<CTFontRef> fFontRef; |
|
450 SkFontStyle fFontStyle; |
|
451 |
|
452 protected: |
|
453 friend class SkFontHost; // to access our protected members for deprecated methods |
|
454 |
|
455 virtual int onGetUPEM() const SK_OVERRIDE; |
|
456 virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE; |
|
457 virtual SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE; |
|
458 virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE; |
|
459 virtual size_t onGetTableData(SkFontTableTag, size_t offset, |
|
460 size_t length, void* data) const SK_OVERRIDE; |
|
461 virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE; |
|
462 virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE; |
|
463 virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE; |
|
464 virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics( |
|
465 SkAdvancedTypefaceMetrics::PerGlyphInfo, |
|
466 const uint32_t*, uint32_t) const SK_OVERRIDE; |
|
467 virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[], |
|
468 int glyphCount) const SK_OVERRIDE; |
|
469 virtual int onCountGlyphs() const SK_OVERRIDE; |
|
470 |
|
471 private: |
|
472 |
|
473 typedef SkTypeface INHERITED; |
|
474 }; |
|
475 |
|
476 static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) { |
|
477 SkASSERT(fontRef); |
|
478 bool isFixedPitch; |
|
479 SkTypeface::Style style = computeStyleBits(fontRef, &isFixedPitch); |
|
480 SkFontID fontID = CTFontRef_to_SkFontID(fontRef); |
|
481 |
|
482 return new SkTypeface_Mac(style, fontID, isFixedPitch, fontRef, name); |
|
483 } |
|
484 |
|
485 static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) { |
|
486 CTFontRef ctFont = NULL; |
|
487 |
|
488 CTFontSymbolicTraits ctFontTraits = 0; |
|
489 if (theStyle & SkTypeface::kBold) { |
|
490 ctFontTraits |= kCTFontBoldTrait; |
|
491 } |
|
492 if (theStyle & SkTypeface::kItalic) { |
|
493 ctFontTraits |= kCTFontItalicTrait; |
|
494 } |
|
495 |
|
496 // Create the font info |
|
497 AutoCFRelease<CFStringRef> cfFontName(make_CFString(familyName)); |
|
498 |
|
499 AutoCFRelease<CFNumberRef> cfFontTraits( |
|
500 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits)); |
|
501 |
|
502 AutoCFRelease<CFMutableDictionaryRef> cfAttributes( |
|
503 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
|
504 &kCFTypeDictionaryKeyCallBacks, |
|
505 &kCFTypeDictionaryValueCallBacks)); |
|
506 |
|
507 AutoCFRelease<CFMutableDictionaryRef> cfTraits( |
|
508 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
|
509 &kCFTypeDictionaryKeyCallBacks, |
|
510 &kCFTypeDictionaryValueCallBacks)); |
|
511 |
|
512 // Create the font |
|
513 if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) { |
|
514 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits); |
|
515 |
|
516 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName); |
|
517 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits); |
|
518 |
|
519 AutoCFRelease<CTFontDescriptorRef> ctFontDesc( |
|
520 CTFontDescriptorCreateWithAttributes(cfAttributes)); |
|
521 |
|
522 if (ctFontDesc != NULL) { |
|
523 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL); |
|
524 } |
|
525 } |
|
526 |
|
527 return ctFont ? NewFromFontRef(ctFont, familyName) : NULL; |
|
528 } |
|
529 |
|
530 static SkTypeface* GetDefaultFace() { |
|
531 SK_DECLARE_STATIC_MUTEX(gMutex); |
|
532 SkAutoMutexAcquire ma(gMutex); |
|
533 |
|
534 static SkTypeface* gDefaultFace; |
|
535 |
|
536 if (NULL == gDefaultFace) { |
|
537 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal); |
|
538 SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal); |
|
539 } |
|
540 return gDefaultFace; |
|
541 } |
|
542 |
|
543 /////////////////////////////////////////////////////////////////////////////// |
|
544 |
|
545 extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face); |
|
546 CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) { |
|
547 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face; |
|
548 return macface ? macface->fFontRef.get() : NULL; |
|
549 } |
|
550 |
|
551 /* This function is visible on the outside. It first searches the cache, and if |
|
552 * not found, returns a new entry (after adding it to the cache). |
|
553 */ |
|
554 SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) { |
|
555 SkFontID fontID = CTFontRef_to_SkFontID(fontRef); |
|
556 SkTypeface* face = SkTypefaceCache::FindByID(fontID); |
|
557 if (face) { |
|
558 face->ref(); |
|
559 } else { |
|
560 face = NewFromFontRef(fontRef, NULL); |
|
561 SkTypefaceCache::Add(face, face->style()); |
|
562 // NewFromFontRef doesn't retain the parameter, but the typeface it |
|
563 // creates does release it in its destructor, so we balance that with |
|
564 // a retain call here. |
|
565 CFRetain(fontRef); |
|
566 } |
|
567 SkASSERT(face->getRefCnt() > 1); |
|
568 return face; |
|
569 } |
|
570 |
|
571 struct NameStyleRec { |
|
572 const char* fName; |
|
573 SkTypeface::Style fStyle; |
|
574 }; |
|
575 |
|
576 static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style, |
|
577 void* ctx) { |
|
578 const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face); |
|
579 const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx); |
|
580 |
|
581 return rec->fStyle == style && mface->fName.equals(rec->fName); |
|
582 } |
|
583 |
|
584 static const char* map_css_names(const char* name) { |
|
585 static const struct { |
|
586 const char* fFrom; // name the caller specified |
|
587 const char* fTo; // "canonical" name we map to |
|
588 } gPairs[] = { |
|
589 { "sans-serif", "Helvetica" }, |
|
590 { "serif", "Times" }, |
|
591 { "monospace", "Courier" } |
|
592 }; |
|
593 |
|
594 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) { |
|
595 if (strcmp(name, gPairs[i].fFrom) == 0) { |
|
596 return gPairs[i].fTo; |
|
597 } |
|
598 } |
|
599 return name; // no change |
|
600 } |
|
601 |
|
602 static SkTypeface* create_typeface(const SkTypeface* familyFace, |
|
603 const char familyName[], |
|
604 SkTypeface::Style style) { |
|
605 if (familyName) { |
|
606 familyName = map_css_names(familyName); |
|
607 } |
|
608 |
|
609 // Clone an existing typeface |
|
610 // TODO: only clone if style matches the familyFace's style... |
|
611 if (familyName == NULL && familyFace != NULL) { |
|
612 familyFace->ref(); |
|
613 return const_cast<SkTypeface*>(familyFace); |
|
614 } |
|
615 |
|
616 if (!familyName || !*familyName) { |
|
617 familyName = FONT_DEFAULT_NAME; |
|
618 } |
|
619 |
|
620 NameStyleRec rec = { familyName, style }; |
|
621 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec); |
|
622 |
|
623 if (NULL == face) { |
|
624 face = NewFromName(familyName, style); |
|
625 if (face) { |
|
626 SkTypefaceCache::Add(face, style); |
|
627 } else { |
|
628 face = GetDefaultFace(); |
|
629 face->ref(); |
|
630 } |
|
631 } |
|
632 return face; |
|
633 } |
|
634 |
|
635 /////////////////////////////////////////////////////////////////////////////// |
|
636 |
|
637 /** GlyphRect is in FUnits (em space, y up). */ |
|
638 struct GlyphRect { |
|
639 int16_t fMinX; |
|
640 int16_t fMinY; |
|
641 int16_t fMaxX; |
|
642 int16_t fMaxY; |
|
643 }; |
|
644 |
|
645 class SkScalerContext_Mac : public SkScalerContext { |
|
646 public: |
|
647 SkScalerContext_Mac(SkTypeface_Mac*, const SkDescriptor*); |
|
648 |
|
649 protected: |
|
650 unsigned generateGlyphCount(void) SK_OVERRIDE; |
|
651 uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE; |
|
652 void generateAdvance(SkGlyph* glyph) SK_OVERRIDE; |
|
653 void generateMetrics(SkGlyph* glyph) SK_OVERRIDE; |
|
654 void generateImage(const SkGlyph& glyph) SK_OVERRIDE; |
|
655 void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE; |
|
656 void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE; |
|
657 |
|
658 private: |
|
659 static void CTPathElement(void *info, const CGPathElement *element); |
|
660 |
|
661 /** Returns the offset from the horizontal origin to the vertical origin in SkGlyph units. */ |
|
662 void getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const; |
|
663 |
|
664 /** Initializes and returns the value of fFBoundingBoxesGlyphOffset. |
|
665 * |
|
666 * For use with (and must be called before) generateBBoxes. |
|
667 */ |
|
668 uint16_t getFBoundingBoxesGlyphOffset(); |
|
669 |
|
670 /** Initializes fFBoundingBoxes and returns true on success. |
|
671 * |
|
672 * On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to |
|
673 * return a bad value in bounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is |
|
674 * less than its maxp::numGlyphs. When this is the case we try to read the bounds from the |
|
675 * font directly. |
|
676 * |
|
677 * This routine initializes fFBoundingBoxes to an array of |
|
678 * fGlyphCount - fFBoundingBoxesGlyphOffset GlyphRects which contain the bounds in FUnits |
|
679 * (em space, y up) of glyphs with ids in the range [fFBoundingBoxesGlyphOffset, fGlyphCount). |
|
680 * |
|
681 * Returns true if fFBoundingBoxes is properly initialized. The table can only be properly |
|
682 * initialized for a TrueType font with 'head', 'loca', and 'glyf' tables. |
|
683 * |
|
684 * TODO: A future optimization will compute fFBoundingBoxes once per fCTFont. |
|
685 */ |
|
686 bool generateBBoxes(); |
|
687 |
|
688 /** Converts from FUnits (em space, y up) to SkGlyph units (pixels, y down). |
|
689 * |
|
690 * Used on Snow Leopard to correct CTFontGetVerticalTranslationsForGlyphs. |
|
691 * Used on Lion to correct CTFontGetBoundingRectsForGlyphs. |
|
692 */ |
|
693 SkMatrix fFUnitMatrix; |
|
694 |
|
695 Offscreen fOffscreen; |
|
696 AutoCFRelease<CTFontRef> fCTFont; |
|
697 |
|
698 /** Vertical variant of fCTFont. |
|
699 * |
|
700 * CT vertical metrics are pre-rotated (in em space, before transform) 90deg clock-wise. |
|
701 * This makes kCTFontDefaultOrientation dangerous, because the metrics from |
|
702 * kCTFontHorizontalOrientation are in a different space from kCTFontVerticalOrientation. |
|
703 * Use fCTVerticalFont with kCTFontVerticalOrientation to get metrics in the same space. |
|
704 */ |
|
705 AutoCFRelease<CTFontRef> fCTVerticalFont; |
|
706 |
|
707 AutoCFRelease<CGFontRef> fCGFont; |
|
708 SkAutoTMalloc<GlyphRect> fFBoundingBoxes; |
|
709 uint16_t fFBoundingBoxesGlyphOffset; |
|
710 uint16_t fGlyphCount; |
|
711 bool fGeneratedFBoundingBoxes; |
|
712 const bool fDoSubPosition; |
|
713 const bool fVertical; |
|
714 |
|
715 friend class Offscreen; |
|
716 |
|
717 typedef SkScalerContext INHERITED; |
|
718 }; |
|
719 |
|
720 SkScalerContext_Mac::SkScalerContext_Mac(SkTypeface_Mac* typeface, |
|
721 const SkDescriptor* desc) |
|
722 : INHERITED(typeface, desc) |
|
723 , fFBoundingBoxes() |
|
724 , fFBoundingBoxesGlyphOffset(0) |
|
725 , fGeneratedFBoundingBoxes(false) |
|
726 , fDoSubPosition(SkToBool(fRec.fFlags & kSubpixelPositioning_Flag)) |
|
727 , fVertical(SkToBool(fRec.fFlags & kVertical_Flag)) |
|
728 |
|
729 { |
|
730 CTFontRef ctFont = typeface->fFontRef.get(); |
|
731 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont); |
|
732 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF); |
|
733 fGlyphCount = SkToU16(numGlyphs); |
|
734 |
|
735 fRec.getSingleMatrix(&fFUnitMatrix); |
|
736 CGAffineTransform transform = MatrixToCGAffineTransform(fFUnitMatrix); |
|
737 |
|
738 AutoCFRelease<CTFontDescriptorRef> ctFontDesc; |
|
739 if (fVertical) { |
|
740 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable( |
|
741 kCFAllocatorDefault, 0, |
|
742 &kCFTypeDictionaryKeyCallBacks, |
|
743 &kCFTypeDictionaryValueCallBacks)); |
|
744 if (cfAttributes) { |
|
745 CTFontOrientation ctOrientation = kCTFontVerticalOrientation; |
|
746 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate( |
|
747 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation)); |
|
748 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical); |
|
749 ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes); |
|
750 } |
|
751 } |
|
752 // Since our matrix includes everything, we pass 1 for size. |
|
753 fCTFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, ctFontDesc); |
|
754 fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL); |
|
755 if (fVertical) { |
|
756 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0); |
|
757 transform = CGAffineTransformConcat(rotateLeft, transform); |
|
758 fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, 1, &transform, NULL); |
|
759 } |
|
760 |
|
761 SkScalar emPerFUnit = SkScalarInvert(SkIntToScalar(CGFontGetUnitsPerEm(fCGFont))); |
|
762 fFUnitMatrix.preScale(emPerFUnit, -emPerFUnit); |
|
763 } |
|
764 |
|
765 CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph, |
|
766 CGGlyph glyphID, size_t* rowBytesPtr, |
|
767 bool generateA8FromLCD) { |
|
768 if (!fRGBSpace) { |
|
769 //It doesn't appear to matter what color space is specified. |
|
770 //Regular blends and antialiased text are always (s*a + d*(1-a)) |
|
771 //and smoothed text is always g=2.0. |
|
772 fRGBSpace = CGColorSpaceCreateDeviceRGB(); |
|
773 } |
|
774 |
|
775 // default to kBW_Format |
|
776 bool doAA = false; |
|
777 bool doLCD = false; |
|
778 |
|
779 if (SkMask::kBW_Format != glyph.fMaskFormat) { |
|
780 doLCD = true; |
|
781 doAA = true; |
|
782 } |
|
783 |
|
784 // FIXME: lcd smoothed un-hinted rasterization unsupported. |
|
785 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) { |
|
786 doLCD = false; |
|
787 doAA = true; |
|
788 } |
|
789 |
|
790 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel); |
|
791 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) { |
|
792 if (fSize.fWidth < glyph.fWidth) { |
|
793 fSize.fWidth = RoundSize(glyph.fWidth); |
|
794 } |
|
795 if (fSize.fHeight < glyph.fHeight) { |
|
796 fSize.fHeight = RoundSize(glyph.fHeight); |
|
797 } |
|
798 |
|
799 rowBytes = fSize.fWidth * sizeof(CGRGBPixel); |
|
800 void* image = fImageStorage.reset(rowBytes * fSize.fHeight); |
|
801 fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8, |
|
802 rowBytes, fRGBSpace, BITMAP_INFO_RGB); |
|
803 |
|
804 // skia handles quantization itself, so we disable this for cg to get |
|
805 // full fractional data from them. |
|
806 CGContextSetAllowsFontSubpixelQuantization(fCG, false); |
|
807 CGContextSetShouldSubpixelQuantizeFonts(fCG, false); |
|
808 |
|
809 CGContextSetTextDrawingMode(fCG, kCGTextFill); |
|
810 CGContextSetFont(fCG, context.fCGFont); |
|
811 CGContextSetFontSize(fCG, 1 /*CTFontGetSize(context.fCTFont)*/); |
|
812 CGContextSetTextMatrix(fCG, CTFontGetMatrix(context.fCTFont)); |
|
813 |
|
814 // Because CG always draws from the horizontal baseline, |
|
815 // if there is a non-integral translation from the horizontal origin to the vertical origin, |
|
816 // then CG cannot draw the glyph in the correct location without subpixel positioning. |
|
817 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition || context.fVertical); |
|
818 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition || context.fVertical); |
|
819 |
|
820 // Draw white on black to create mask. |
|
821 // TODO: Draw black on white and invert, CG has a special case codepath. |
|
822 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f); |
|
823 |
|
824 // force our checks below to happen |
|
825 fDoAA = !doAA; |
|
826 fDoLCD = !doLCD; |
|
827 } |
|
828 |
|
829 if (fDoAA != doAA) { |
|
830 CGContextSetShouldAntialias(fCG, doAA); |
|
831 fDoAA = doAA; |
|
832 } |
|
833 if (fDoLCD != doLCD) { |
|
834 CGContextSetShouldSmoothFonts(fCG, doLCD); |
|
835 fDoLCD = doLCD; |
|
836 } |
|
837 |
|
838 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get(); |
|
839 // skip rows based on the glyph's height |
|
840 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth; |
|
841 |
|
842 // erase to black |
|
843 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes); |
|
844 |
|
845 float subX = 0; |
|
846 float subY = 0; |
|
847 if (context.fDoSubPosition) { |
|
848 subX = SkFixedToFloat(glyph.getSubXFixed()); |
|
849 subY = SkFixedToFloat(glyph.getSubYFixed()); |
|
850 } |
|
851 |
|
852 // CGContextShowGlyphsAtPoint always draws using the horizontal baseline origin. |
|
853 if (context.fVertical) { |
|
854 SkPoint offset; |
|
855 context.getVerticalOffset(glyphID, &offset); |
|
856 subX += offset.fX; |
|
857 subY += offset.fY; |
|
858 } |
|
859 |
|
860 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX, |
|
861 glyph.fTop + glyph.fHeight - subY, |
|
862 &glyphID, 1); |
|
863 |
|
864 SkASSERT(rowBytesPtr); |
|
865 *rowBytesPtr = rowBytes; |
|
866 return image; |
|
867 } |
|
868 |
|
869 void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkPoint* offset) const { |
|
870 // Snow Leopard returns cgVertOffset in completely un-transformed FUnits (em space, y up). |
|
871 // Lion and Leopard return cgVertOffset in CG units (pixels, y up). |
|
872 CGSize cgVertOffset; |
|
873 CTFontGetVerticalTranslationsForGlyphs(fCTFont, &glyphID, &cgVertOffset, 1); |
|
874 |
|
875 SkPoint skVertOffset = { CGToScalar(cgVertOffset.width), CGToScalar(cgVertOffset.height) }; |
|
876 if (isSnowLeopard()) { |
|
877 // From FUnits (em space, y up) to SkGlyph units (pixels, y down). |
|
878 fFUnitMatrix.mapPoints(&skVertOffset, 1); |
|
879 } else { |
|
880 // From CG units (pixels, y up) to SkGlyph units (pixels, y down). |
|
881 skVertOffset.fY = -skVertOffset.fY; |
|
882 } |
|
883 |
|
884 *offset = skVertOffset; |
|
885 } |
|
886 |
|
887 uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() { |
|
888 if (fFBoundingBoxesGlyphOffset) { |
|
889 return fFBoundingBoxesGlyphOffset; |
|
890 } |
|
891 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts |
|
892 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont); |
|
893 if (hheaTable.fData) { |
|
894 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics); |
|
895 } |
|
896 return fFBoundingBoxesGlyphOffset; |
|
897 } |
|
898 |
|
899 bool SkScalerContext_Mac::generateBBoxes() { |
|
900 if (fGeneratedFBoundingBoxes) { |
|
901 return NULL != fFBoundingBoxes.get(); |
|
902 } |
|
903 fGeneratedFBoundingBoxes = true; |
|
904 |
|
905 AutoCGTable<SkOTTableHead> headTable(fCGFont); |
|
906 if (!headTable.fData) { |
|
907 return false; |
|
908 } |
|
909 |
|
910 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont); |
|
911 if (!locaTable.fData) { |
|
912 return false; |
|
913 } |
|
914 |
|
915 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont); |
|
916 if (!glyfTable.fData) { |
|
917 return false; |
|
918 } |
|
919 |
|
920 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset; |
|
921 fFBoundingBoxes.reset(entries); |
|
922 |
|
923 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat; |
|
924 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat); |
|
925 glyphDataIter.advance(fFBoundingBoxesGlyphOffset); |
|
926 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) { |
|
927 const SkOTTableGlyphData* glyphData = glyphDataIter.next(); |
|
928 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex]; |
|
929 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin); |
|
930 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin); |
|
931 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax); |
|
932 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax); |
|
933 } |
|
934 |
|
935 return true; |
|
936 } |
|
937 |
|
938 unsigned SkScalerContext_Mac::generateGlyphCount(void) { |
|
939 return fGlyphCount; |
|
940 } |
|
941 |
|
942 uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) { |
|
943 CGGlyph cgGlyph[2]; |
|
944 UniChar theChar[2]; // UniChar is a UTF-16 16-bit code unit. |
|
945 |
|
946 // Get the glyph |
|
947 size_t numUniChar = SkUTF16_FromUnichar(uni, theChar); |
|
948 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t)); |
|
949 |
|
950 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points: |
|
951 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate. |
|
952 // It is documented that if a mapping is unavailable, the glyph will be set to 0. |
|
953 CTFontGetGlyphsForCharacters(fCTFont, theChar, cgGlyph, numUniChar); |
|
954 return cgGlyph[0]; |
|
955 } |
|
956 |
|
957 void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) { |
|
958 this->generateMetrics(glyph); |
|
959 } |
|
960 |
|
961 void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) { |
|
962 const CGGlyph cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount); |
|
963 glyph->zeroMetrics(); |
|
964 |
|
965 // The following block produces cgAdvance in CG units (pixels, y up). |
|
966 CGSize cgAdvance; |
|
967 if (fVertical) { |
|
968 CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation, |
|
969 &cgGlyph, &cgAdvance, 1); |
|
970 } else { |
|
971 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontHorizontalOrientation, |
|
972 &cgGlyph, &cgAdvance, 1); |
|
973 } |
|
974 glyph->fAdvanceX = SkFloatToFixed_Check(cgAdvance.width); |
|
975 glyph->fAdvanceY = -SkFloatToFixed_Check(cgAdvance.height); |
|
976 |
|
977 // The following produces skBounds in SkGlyph units (pixels, y down), |
|
978 // or returns early if skBounds would be empty. |
|
979 SkRect skBounds; |
|
980 |
|
981 // On Mountain Lion, CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation and |
|
982 // CTFontGetVerticalTranslationsForGlyphs do not agree when using OTF CFF fonts. |
|
983 // For TTF fonts these two do agree and we can use CTFontGetBoundingRectsForGlyphs to get |
|
984 // the bounding box and CTFontGetVerticalTranslationsForGlyphs to then draw the glyph |
|
985 // inside that bounding box. However, with OTF CFF fonts this does not work. It appears that |
|
986 // CTFontGetBoundingRectsForGlyphs with kCTFontVerticalOrientation on OTF CFF fonts tries |
|
987 // to center the glyph along the vertical baseline and also perform some mysterious shift |
|
988 // along the baseline. CTFontGetVerticalTranslationsForGlyphs does not appear to perform |
|
989 // these steps. |
|
990 // |
|
991 // It is not known which is correct (or if either is correct). However, we must always draw |
|
992 // from the horizontal origin and must use CTFontGetVerticalTranslationsForGlyphs to draw. |
|
993 // As a result, we do not call CTFontGetBoundingRectsForGlyphs for vertical glyphs. |
|
994 |
|
995 // On Snow Leopard, CTFontGetBoundingRectsForGlyphs ignores kCTFontVerticalOrientation and |
|
996 // returns horizontal bounds. |
|
997 |
|
998 // On Lion and Mountain Lion, CTFontGetBoundingRectsForGlyphs has a bug which causes it to |
|
999 // return a bad value in cgBounds.origin.x for SFNT fonts whose hhea::numberOfHMetrics is |
|
1000 // less than its maxp::numGlyphs. When this is the case we try to read the bounds from the |
|
1001 // font directly. |
|
1002 if ((isLion() || isMountainLion()) && |
|
1003 (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes())) |
|
1004 { |
|
1005 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset]; |
|
1006 if (gRect.fMinX >= gRect.fMaxX || gRect.fMinY >= gRect.fMaxY) { |
|
1007 return; |
|
1008 } |
|
1009 skBounds = SkRect::MakeLTRB(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY); |
|
1010 // From FUnits (em space, y up) to SkGlyph units (pixels, y down). |
|
1011 fFUnitMatrix.mapRect(&skBounds); |
|
1012 |
|
1013 } else { |
|
1014 // CTFontGetBoundingRectsForGlyphs produces cgBounds in CG units (pixels, y up). |
|
1015 CGRect cgBounds; |
|
1016 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontHorizontalOrientation, |
|
1017 &cgGlyph, &cgBounds, 1); |
|
1018 |
|
1019 // BUG? |
|
1020 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when |
|
1021 // it should be empty. So, if we see a zero-advance, we check if it has an |
|
1022 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance |
|
1023 // is rare, so we won't incur a big performance cost for this extra check. |
|
1024 if (0 == cgAdvance.width && 0 == cgAdvance.height) { |
|
1025 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL)); |
|
1026 if (NULL == path || CGPathIsEmpty(path)) { |
|
1027 return; |
|
1028 } |
|
1029 } |
|
1030 |
|
1031 if (CGRectIsEmpty_inline(cgBounds)) { |
|
1032 return; |
|
1033 } |
|
1034 |
|
1035 // Convert cgBounds to SkGlyph units (pixels, y down). |
|
1036 skBounds = SkRect::MakeXYWH(cgBounds.origin.x, -cgBounds.origin.y - cgBounds.size.height, |
|
1037 cgBounds.size.width, cgBounds.size.height); |
|
1038 } |
|
1039 |
|
1040 if (fVertical) { |
|
1041 // Due to all of the vertical bounds bugs, skBounds is always the horizontal bounds. |
|
1042 // Convert these horizontal bounds into vertical bounds. |
|
1043 SkPoint offset; |
|
1044 getVerticalOffset(cgGlyph, &offset); |
|
1045 skBounds.offset(offset); |
|
1046 } |
|
1047 |
|
1048 // Currently the bounds are based on being rendered at (0,0). |
|
1049 // The top left must not move, since that is the base from which subpixel positioning is offset. |
|
1050 if (fDoSubPosition) { |
|
1051 skBounds.fRight += SkFixedToFloat(glyph->getSubXFixed()); |
|
1052 skBounds.fBottom += SkFixedToFloat(glyph->getSubYFixed()); |
|
1053 } |
|
1054 |
|
1055 SkIRect skIBounds; |
|
1056 skBounds.roundOut(&skIBounds); |
|
1057 // Expand the bounds by 1 pixel, to give CG room for anti-aliasing. |
|
1058 // Note that this outset is to allow room for LCD smoothed glyphs. However, the correct outset |
|
1059 // is not currently known, as CG dilates the outlines by some percentage. |
|
1060 // Note that if this context is A8 and not back-forming from LCD, there is no need to outset. |
|
1061 skIBounds.outset(1, 1); |
|
1062 glyph->fLeft = SkToS16(skIBounds.fLeft); |
|
1063 glyph->fTop = SkToS16(skIBounds.fTop); |
|
1064 glyph->fWidth = SkToU16(skIBounds.width()); |
|
1065 glyph->fHeight = SkToU16(skIBounds.height()); |
|
1066 |
|
1067 #ifdef HACK_COLORGLYPHS |
|
1068 glyph->fMaskFormat = SkMask::kARGB32_Format; |
|
1069 #endif |
|
1070 } |
|
1071 |
|
1072 #include "SkColorPriv.h" |
|
1073 |
|
1074 static void build_power_table(uint8_t table[], float ee) { |
|
1075 for (int i = 0; i < 256; i++) { |
|
1076 float x = i / 255.f; |
|
1077 x = sk_float_pow(x, ee); |
|
1078 int xx = SkScalarRoundToInt(x * 255); |
|
1079 table[i] = SkToU8(xx); |
|
1080 } |
|
1081 } |
|
1082 |
|
1083 /** |
|
1084 * This will invert the gamma applied by CoreGraphics, so we can get linear |
|
1085 * values. |
|
1086 * |
|
1087 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value. |
|
1088 * The color space used does not appear to affect this choice. |
|
1089 */ |
|
1090 static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() { |
|
1091 static bool gInited; |
|
1092 static uint8_t gTableCoreGraphicsSmoothing[256]; |
|
1093 if (!gInited) { |
|
1094 build_power_table(gTableCoreGraphicsSmoothing, 2.0f); |
|
1095 gInited = true; |
|
1096 } |
|
1097 return gTableCoreGraphicsSmoothing; |
|
1098 } |
|
1099 |
|
1100 static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) { |
|
1101 while (count > 0) { |
|
1102 uint8_t mask = 0; |
|
1103 for (int i = 7; i >= 0; --i) { |
|
1104 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i; |
|
1105 if (0 == --count) { |
|
1106 break; |
|
1107 } |
|
1108 } |
|
1109 *dst++ = mask; |
|
1110 } |
|
1111 } |
|
1112 |
|
1113 template<bool APPLY_PREBLEND> |
|
1114 static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) { |
|
1115 U8CPU r = (rgb >> 16) & 0xFF; |
|
1116 U8CPU g = (rgb >> 8) & 0xFF; |
|
1117 U8CPU b = (rgb >> 0) & 0xFF; |
|
1118 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8); |
|
1119 } |
|
1120 template<bool APPLY_PREBLEND> |
|
1121 static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, |
|
1122 const SkGlyph& glyph, const uint8_t* table8) { |
|
1123 const int width = glyph.fWidth; |
|
1124 size_t dstRB = glyph.rowBytes(); |
|
1125 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage; |
|
1126 |
|
1127 for (int y = 0; y < glyph.fHeight; y++) { |
|
1128 for (int i = 0; i < width; ++i) { |
|
1129 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8); |
|
1130 } |
|
1131 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
|
1132 dst += dstRB; |
|
1133 } |
|
1134 } |
|
1135 |
|
1136 template<bool APPLY_PREBLEND> |
|
1137 static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR, |
|
1138 const uint8_t* tableG, |
|
1139 const uint8_t* tableB) { |
|
1140 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR); |
|
1141 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG); |
|
1142 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB); |
|
1143 return SkPack888ToRGB16(r, g, b); |
|
1144 } |
|
1145 template<bool APPLY_PREBLEND> |
|
1146 static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph, |
|
1147 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) { |
|
1148 const int width = glyph.fWidth; |
|
1149 size_t dstRB = glyph.rowBytes(); |
|
1150 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage; |
|
1151 |
|
1152 for (int y = 0; y < glyph.fHeight; y++) { |
|
1153 for (int i = 0; i < width; i++) { |
|
1154 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB); |
|
1155 } |
|
1156 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
|
1157 dst = (uint16_t*)((char*)dst + dstRB); |
|
1158 } |
|
1159 } |
|
1160 |
|
1161 template<bool APPLY_PREBLEND> |
|
1162 static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR, |
|
1163 const uint8_t* tableG, |
|
1164 const uint8_t* tableB) { |
|
1165 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR); |
|
1166 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG); |
|
1167 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB); |
|
1168 return SkPackARGB32(0xFF, r, g, b); |
|
1169 } |
|
1170 template<bool APPLY_PREBLEND> |
|
1171 static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph, |
|
1172 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) { |
|
1173 const int width = glyph.fWidth; |
|
1174 size_t dstRB = glyph.rowBytes(); |
|
1175 uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage; |
|
1176 for (int y = 0; y < glyph.fHeight; y++) { |
|
1177 for (int i = 0; i < width; i++) { |
|
1178 dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB); |
|
1179 } |
|
1180 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
|
1181 dst = (uint32_t*)((char*)dst + dstRB); |
|
1182 } |
|
1183 } |
|
1184 |
|
1185 #ifdef HACK_COLORGLYPHS |
|
1186 // hack to colorize the output for testing kARGB32_Format |
|
1187 static SkPMColor cgpixels_to_pmcolor(CGRGBPixel rgb, const SkGlyph& glyph, |
|
1188 int x, int y) { |
|
1189 U8CPU r = (rgb >> 16) & 0xFF; |
|
1190 U8CPU g = (rgb >> 8) & 0xFF; |
|
1191 U8CPU b = (rgb >> 0) & 0xFF; |
|
1192 unsigned a = SkComputeLuminance(r, g, b); |
|
1193 |
|
1194 // compute gradient from x,y |
|
1195 r = x * 255 / glyph.fWidth; |
|
1196 g = 0; |
|
1197 b = (glyph.fHeight - y) * 255 / glyph.fHeight; |
|
1198 return SkPreMultiplyARGB(a, r, g, b); // red |
|
1199 } |
|
1200 #endif |
|
1201 |
|
1202 template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) { |
|
1203 return (T*)((char*)ptr + byteOffset); |
|
1204 } |
|
1205 |
|
1206 void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) { |
|
1207 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount); |
|
1208 |
|
1209 // FIXME: lcd smoothed un-hinted rasterization unsupported. |
|
1210 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting; |
|
1211 |
|
1212 // Draw the glyph |
|
1213 size_t cgRowBytes; |
|
1214 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD); |
|
1215 if (cgPixels == NULL) { |
|
1216 return; |
|
1217 } |
|
1218 |
|
1219 //TODO: see if drawing black on white and inverting is faster (at least in |
|
1220 //lcd case) as core graphics appears to have special case code for drawing |
|
1221 //black text. |
|
1222 |
|
1223 // Fix the glyph |
|
1224 const bool isLCD = isLCDFormat(glyph.fMaskFormat); |
|
1225 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) { |
|
1226 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing(); |
|
1227 |
|
1228 //Note that the following cannot really be integrated into the |
|
1229 //pre-blend, since we may not be applying the pre-blend; when we aren't |
|
1230 //applying the pre-blend it means that a filter wants linear anyway. |
|
1231 //Other code may also be applying the pre-blend, so we'd need another |
|
1232 //one with this and one without. |
|
1233 CGRGBPixel* addr = cgPixels; |
|
1234 for (int y = 0; y < glyph.fHeight; ++y) { |
|
1235 for (int x = 0; x < glyph.fWidth; ++x) { |
|
1236 int r = (addr[x] >> 16) & 0xFF; |
|
1237 int g = (addr[x] >> 8) & 0xFF; |
|
1238 int b = (addr[x] >> 0) & 0xFF; |
|
1239 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b]; |
|
1240 } |
|
1241 addr = SkTAddByteOffset(addr, cgRowBytes); |
|
1242 } |
|
1243 } |
|
1244 |
|
1245 // Convert glyph to mask |
|
1246 switch (glyph.fMaskFormat) { |
|
1247 case SkMask::kLCD32_Format: { |
|
1248 if (fPreBlend.isApplicable()) { |
|
1249 rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph, |
|
1250 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
|
1251 } else { |
|
1252 rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph, |
|
1253 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
|
1254 } |
|
1255 } break; |
|
1256 case SkMask::kLCD16_Format: { |
|
1257 if (fPreBlend.isApplicable()) { |
|
1258 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph, |
|
1259 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
|
1260 } else { |
|
1261 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph, |
|
1262 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); |
|
1263 } |
|
1264 } break; |
|
1265 case SkMask::kA8_Format: { |
|
1266 if (fPreBlend.isApplicable()) { |
|
1267 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG); |
|
1268 } else { |
|
1269 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG); |
|
1270 } |
|
1271 } break; |
|
1272 case SkMask::kBW_Format: { |
|
1273 const int width = glyph.fWidth; |
|
1274 size_t dstRB = glyph.rowBytes(); |
|
1275 uint8_t* dst = (uint8_t*)glyph.fImage; |
|
1276 for (int y = 0; y < glyph.fHeight; y++) { |
|
1277 cgpixels_to_bits(dst, cgPixels, width); |
|
1278 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
|
1279 dst += dstRB; |
|
1280 } |
|
1281 } break; |
|
1282 #ifdef HACK_COLORGLYPHS |
|
1283 case SkMask::kARGB32_Format: { |
|
1284 const int width = glyph.fWidth; |
|
1285 size_t dstRB = glyph.rowBytes(); |
|
1286 SkPMColor* dst = (SkPMColor*)glyph.fImage; |
|
1287 for (int y = 0; y < glyph.fHeight; y++) { |
|
1288 for (int x = 0; x < width; ++x) { |
|
1289 dst[x] = cgpixels_to_pmcolor(cgPixels[x], glyph, x, y); |
|
1290 } |
|
1291 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes); |
|
1292 dst = (SkPMColor*)((char*)dst + dstRB); |
|
1293 } |
|
1294 } break; |
|
1295 #endif |
|
1296 default: |
|
1297 SkDEBUGFAIL("unexpected mask format"); |
|
1298 break; |
|
1299 } |
|
1300 } |
|
1301 |
|
1302 /* |
|
1303 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4 |
|
1304 * seems sufficient, and possibly even correct, to allow the hinted outline |
|
1305 * to be subpixel positioned. |
|
1306 */ |
|
1307 #define kScaleForSubPixelPositionHinting (4.0f) |
|
1308 |
|
1309 void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) { |
|
1310 CTFontRef font = fCTFont; |
|
1311 SkScalar scaleX = SK_Scalar1; |
|
1312 SkScalar scaleY = SK_Scalar1; |
|
1313 |
|
1314 /* |
|
1315 * For subpixel positioning, we want to return an unhinted outline, so it |
|
1316 * can be positioned nicely at fractional offsets. However, we special-case |
|
1317 * if the baseline of the (horizontal) text is axis-aligned. In those cases |
|
1318 * we want to retain hinting in the direction orthogonal to the baseline. |
|
1319 * e.g. for horizontal baseline, we want to retain hinting in Y. |
|
1320 * The way we remove hinting is to scale the font by some value (4) in that |
|
1321 * direction, ask for the path, and then scale the path back down. |
|
1322 */ |
|
1323 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { |
|
1324 SkMatrix m; |
|
1325 fRec.getSingleMatrix(&m); |
|
1326 |
|
1327 // start out by assuming that we want no hining in X and Y |
|
1328 scaleX = scaleY = kScaleForSubPixelPositionHinting; |
|
1329 // now see if we need to restore hinting for axis-aligned baselines |
|
1330 switch (SkComputeAxisAlignmentForHText(m)) { |
|
1331 case kX_SkAxisAlignment: |
|
1332 scaleY = SK_Scalar1; // want hinting in the Y direction |
|
1333 break; |
|
1334 case kY_SkAxisAlignment: |
|
1335 scaleX = SK_Scalar1; // want hinting in the X direction |
|
1336 break; |
|
1337 default: |
|
1338 break; |
|
1339 } |
|
1340 |
|
1341 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY); |
|
1342 // need to release font when we're done |
|
1343 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL); |
|
1344 } |
|
1345 |
|
1346 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount); |
|
1347 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL)); |
|
1348 |
|
1349 path->reset(); |
|
1350 if (cgPath != NULL) { |
|
1351 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement); |
|
1352 } |
|
1353 |
|
1354 if (fDoSubPosition) { |
|
1355 SkMatrix m; |
|
1356 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY)); |
|
1357 path->transform(m); |
|
1358 // balance the call to CTFontCreateCopyWithAttributes |
|
1359 CFSafeRelease(font); |
|
1360 } |
|
1361 if (fVertical) { |
|
1362 SkPoint offset; |
|
1363 getVerticalOffset(cgGlyph, &offset); |
|
1364 path->offset(offset.fX, offset.fY); |
|
1365 } |
|
1366 } |
|
1367 |
|
1368 void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, |
|
1369 SkPaint::FontMetrics* my) { |
|
1370 CGRect theBounds = CTFontGetBoundingBox(fCTFont); |
|
1371 |
|
1372 SkPaint::FontMetrics theMetrics; |
|
1373 theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds)); |
|
1374 theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont)); |
|
1375 theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont)); |
|
1376 theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds)); |
|
1377 theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont)); |
|
1378 theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds)); |
|
1379 theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds)); |
|
1380 theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds)); |
|
1381 theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont)); |
|
1382 theMetrics.fUnderlineThickness = CGToScalar( CTFontGetUnderlineThickness(fCTFont)); |
|
1383 theMetrics.fUnderlinePosition = -CGToScalar( CTFontGetUnderlinePosition(fCTFont)); |
|
1384 |
|
1385 theMetrics.fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; |
|
1386 theMetrics.fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; |
|
1387 |
|
1388 if (mx != NULL) { |
|
1389 *mx = theMetrics; |
|
1390 } |
|
1391 if (my != NULL) { |
|
1392 *my = theMetrics; |
|
1393 } |
|
1394 } |
|
1395 |
|
1396 void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) { |
|
1397 SkPath* skPath = (SkPath*)info; |
|
1398 |
|
1399 // Process the path element |
|
1400 switch (element->type) { |
|
1401 case kCGPathElementMoveToPoint: |
|
1402 skPath->moveTo(element->points[0].x, -element->points[0].y); |
|
1403 break; |
|
1404 |
|
1405 case kCGPathElementAddLineToPoint: |
|
1406 skPath->lineTo(element->points[0].x, -element->points[0].y); |
|
1407 break; |
|
1408 |
|
1409 case kCGPathElementAddQuadCurveToPoint: |
|
1410 skPath->quadTo(element->points[0].x, -element->points[0].y, |
|
1411 element->points[1].x, -element->points[1].y); |
|
1412 break; |
|
1413 |
|
1414 case kCGPathElementAddCurveToPoint: |
|
1415 skPath->cubicTo(element->points[0].x, -element->points[0].y, |
|
1416 element->points[1].x, -element->points[1].y, |
|
1417 element->points[2].x, -element->points[2].y); |
|
1418 break; |
|
1419 |
|
1420 case kCGPathElementCloseSubpath: |
|
1421 skPath->close(); |
|
1422 break; |
|
1423 |
|
1424 default: |
|
1425 SkDEBUGFAIL("Unknown path element!"); |
|
1426 break; |
|
1427 } |
|
1428 } |
|
1429 |
|
1430 |
|
1431 /////////////////////////////////////////////////////////////////////////////// |
|
1432 |
|
1433 // Returns NULL on failure |
|
1434 // Call must still manage its ownership of provider |
|
1435 static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) { |
|
1436 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider)); |
|
1437 if (NULL == cg) { |
|
1438 return NULL; |
|
1439 } |
|
1440 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL); |
|
1441 return cg ? SkCreateTypefaceFromCTFont(ct) : NULL; |
|
1442 } |
|
1443 |
|
1444 // Web fonts added to the the CTFont registry do not return their character set. |
|
1445 // Iterate through the font in this case. The existing caller caches the result, |
|
1446 // so the performance impact isn't too bad. |
|
1447 static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount, |
|
1448 SkTDArray<SkUnichar>* glyphToUnicode) { |
|
1449 glyphToUnicode->setCount(SkToInt(glyphCount)); |
|
1450 SkUnichar* out = glyphToUnicode->begin(); |
|
1451 sk_bzero(out, glyphCount * sizeof(SkUnichar)); |
|
1452 UniChar unichar = 0; |
|
1453 while (glyphCount > 0) { |
|
1454 CGGlyph glyph; |
|
1455 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) { |
|
1456 out[glyph] = unichar; |
|
1457 --glyphCount; |
|
1458 } |
|
1459 if (++unichar == 0) { |
|
1460 break; |
|
1461 } |
|
1462 } |
|
1463 } |
|
1464 |
|
1465 // Construct Glyph to Unicode table. |
|
1466 // Unicode code points that require conjugate pairs in utf16 are not |
|
1467 // supported. |
|
1468 static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount, |
|
1469 SkTDArray<SkUnichar>* glyphToUnicode) { |
|
1470 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont)); |
|
1471 if (!charSet) { |
|
1472 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode); |
|
1473 return; |
|
1474 } |
|
1475 |
|
1476 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault, |
|
1477 charSet)); |
|
1478 if (!bitmap) { |
|
1479 return; |
|
1480 } |
|
1481 CFIndex length = CFDataGetLength(bitmap); |
|
1482 if (!length) { |
|
1483 return; |
|
1484 } |
|
1485 if (length > 8192) { |
|
1486 // TODO: Add support for Unicode above 0xFFFF |
|
1487 // Consider only the BMP portion of the Unicode character points. |
|
1488 // The bitmap may contain other planes, up to plane 16. |
|
1489 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html |
|
1490 length = 8192; |
|
1491 } |
|
1492 const UInt8* bits = CFDataGetBytePtr(bitmap); |
|
1493 glyphToUnicode->setCount(SkToInt(glyphCount)); |
|
1494 SkUnichar* out = glyphToUnicode->begin(); |
|
1495 sk_bzero(out, glyphCount * sizeof(SkUnichar)); |
|
1496 for (int i = 0; i < length; i++) { |
|
1497 int mask = bits[i]; |
|
1498 if (!mask) { |
|
1499 continue; |
|
1500 } |
|
1501 for (int j = 0; j < 8; j++) { |
|
1502 CGGlyph glyph; |
|
1503 UniChar unichar = static_cast<UniChar>((i << 3) + j); |
|
1504 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) { |
|
1505 out[glyph] = unichar; |
|
1506 } |
|
1507 } |
|
1508 } |
|
1509 } |
|
1510 |
|
1511 static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) { |
|
1512 CGSize advance; |
|
1513 advance.width = 0; |
|
1514 CGGlyph glyph = gId; |
|
1515 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1); |
|
1516 *data = sk_float_round2int(advance.width); |
|
1517 return true; |
|
1518 } |
|
1519 |
|
1520 // we might move this into our CGUtils... |
|
1521 static void CFStringToSkString(CFStringRef src, SkString* dst) { |
|
1522 // Reserve enough room for the worst-case string, |
|
1523 // plus 1 byte for the trailing null. |
|
1524 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src), |
|
1525 kCFStringEncodingUTF8) + 1; |
|
1526 dst->resize(length); |
|
1527 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8); |
|
1528 // Resize to the actual UTF-8 length used, stripping the null character. |
|
1529 dst->resize(strlen(dst->c_str())); |
|
1530 } |
|
1531 |
|
1532 SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics( |
|
1533 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo, |
|
1534 const uint32_t* glyphIDs, |
|
1535 uint32_t glyphIDsCount) const { |
|
1536 |
|
1537 CTFontRef originalCTFont = fFontRef.get(); |
|
1538 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes( |
|
1539 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL)); |
|
1540 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics; |
|
1541 |
|
1542 { |
|
1543 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont)); |
|
1544 CFStringToSkString(fontName, &info->fFontName); |
|
1545 } |
|
1546 |
|
1547 info->fMultiMaster = false; |
|
1548 CFIndex glyphCount = CTFontGetGlyphCount(ctFont); |
|
1549 info->fLastGlyphID = SkToU16(glyphCount - 1); |
|
1550 info->fEmSize = CTFontGetUnitsPerEm(ctFont); |
|
1551 |
|
1552 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) { |
|
1553 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode); |
|
1554 } |
|
1555 |
|
1556 info->fStyle = 0; |
|
1557 |
|
1558 // If it's not a truetype font, mark it as 'other'. Assume that TrueType |
|
1559 // fonts always have both glyf and loca tables. At the least, this is what |
|
1560 // sfntly needs to subset the font. CTFontCopyAttribute() does not always |
|
1561 // succeed in determining this directly. |
|
1562 if (!this->getTableSize('glyf') || !this->getTableSize('loca')) { |
|
1563 info->fType = SkAdvancedTypefaceMetrics::kOther_Font; |
|
1564 info->fItalicAngle = 0; |
|
1565 info->fAscent = 0; |
|
1566 info->fDescent = 0; |
|
1567 info->fStemV = 0; |
|
1568 info->fCapHeight = 0; |
|
1569 info->fBBox = SkIRect::MakeEmpty(); |
|
1570 return info; |
|
1571 } |
|
1572 |
|
1573 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; |
|
1574 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont); |
|
1575 if (symbolicTraits & kCTFontMonoSpaceTrait) { |
|
1576 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; |
|
1577 } |
|
1578 if (symbolicTraits & kCTFontItalicTrait) { |
|
1579 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style; |
|
1580 } |
|
1581 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait; |
|
1582 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) { |
|
1583 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style; |
|
1584 } else if (stylisticClass & kCTFontScriptsClass) { |
|
1585 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style; |
|
1586 } |
|
1587 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont); |
|
1588 info->fAscent = (int16_t) CTFontGetAscent(ctFont); |
|
1589 info->fDescent = (int16_t) CTFontGetDescent(ctFont); |
|
1590 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont); |
|
1591 CGRect bbox = CTFontGetBoundingBox(ctFont); |
|
1592 |
|
1593 SkRect r; |
|
1594 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left |
|
1595 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top |
|
1596 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right |
|
1597 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom |
|
1598 |
|
1599 r.roundOut(&(info->fBBox)); |
|
1600 |
|
1601 // Figure out a good guess for StemV - Min width of i, I, !, 1. |
|
1602 // This probably isn't very good with an italic font. |
|
1603 int16_t min_width = SHRT_MAX; |
|
1604 info->fStemV = 0; |
|
1605 static const UniChar stem_chars[] = {'i', 'I', '!', '1'}; |
|
1606 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]); |
|
1607 CGGlyph glyphs[count]; |
|
1608 CGRect boundingRects[count]; |
|
1609 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) { |
|
1610 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation, |
|
1611 glyphs, boundingRects, count); |
|
1612 for (size_t i = 0; i < count; i++) { |
|
1613 int16_t width = (int16_t) boundingRects[i].size.width; |
|
1614 if (width > 0 && width < min_width) { |
|
1615 min_width = width; |
|
1616 info->fStemV = min_width; |
|
1617 } |
|
1618 } |
|
1619 } |
|
1620 |
|
1621 if (false) { // TODO: haven't figured out how to know if font is embeddable |
|
1622 // (information is in the OS/2 table) |
|
1623 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font; |
|
1624 } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) { |
|
1625 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) { |
|
1626 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0); |
|
1627 info->fGlyphWidths->fAdvance.append(1, &min_width); |
|
1628 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0, |
|
1629 SkAdvancedTypefaceMetrics::WidthRange::kDefault); |
|
1630 } else { |
|
1631 info->fGlyphWidths.reset( |
|
1632 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(), |
|
1633 SkToInt(glyphCount), |
|
1634 glyphIDs, |
|
1635 glyphIDsCount, |
|
1636 &getWidthAdvance)); |
|
1637 } |
|
1638 } |
|
1639 return info; |
|
1640 } |
|
1641 |
|
1642 /////////////////////////////////////////////////////////////////////////////// |
|
1643 |
|
1644 static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) { |
|
1645 CTFontRef ctFont = typeface->fFontRef.get(); |
|
1646 AutoCFRelease<CFNumberRef> fontFormatRef( |
|
1647 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute))); |
|
1648 if (!fontFormatRef) { |
|
1649 return 0; |
|
1650 } |
|
1651 |
|
1652 SInt32 fontFormatValue; |
|
1653 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) { |
|
1654 return 0; |
|
1655 } |
|
1656 |
|
1657 switch (fontFormatValue) { |
|
1658 case kCTFontFormatOpenTypePostScript: |
|
1659 return SkSFNTHeader::fontType_OpenTypeCFF::TAG; |
|
1660 case kCTFontFormatOpenTypeTrueType: |
|
1661 return SkSFNTHeader::fontType_WindowsTrueType::TAG; |
|
1662 case kCTFontFormatTrueType: |
|
1663 return SkSFNTHeader::fontType_MacTrueType::TAG; |
|
1664 case kCTFontFormatPostScript: |
|
1665 return SkSFNTHeader::fontType_PostScript::TAG; |
|
1666 case kCTFontFormatBitmap: |
|
1667 return SkSFNTHeader::fontType_MacTrueType::TAG; |
|
1668 case kCTFontFormatUnrecognized: |
|
1669 default: |
|
1670 //CT seems to be unreliable in being able to obtain the type, |
|
1671 //even if all we want is the first four bytes of the font resource. |
|
1672 //Just the presence of the FontForge 'FFTM' table seems to throw it off. |
|
1673 return SkSFNTHeader::fontType_WindowsTrueType::TAG; |
|
1674 } |
|
1675 } |
|
1676 |
|
1677 SkStream* SkTypeface_Mac::onOpenStream(int* ttcIndex) const { |
|
1678 SK_SFNT_ULONG fontType = get_font_type_tag(this); |
|
1679 if (0 == fontType) { |
|
1680 return NULL; |
|
1681 } |
|
1682 |
|
1683 // get table tags |
|
1684 int numTables = this->countTables(); |
|
1685 SkTDArray<SkFontTableTag> tableTags; |
|
1686 tableTags.setCount(numTables); |
|
1687 this->getTableTags(tableTags.begin()); |
|
1688 |
|
1689 // calc total size for font, save sizes |
|
1690 SkTDArray<size_t> tableSizes; |
|
1691 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables; |
|
1692 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) { |
|
1693 size_t tableSize = this->getTableSize(tableTags[tableIndex]); |
|
1694 totalSize += (tableSize + 3) & ~3; |
|
1695 *tableSizes.append() = tableSize; |
|
1696 } |
|
1697 |
|
1698 // reserve memory for stream, and zero it (tables must be zero padded) |
|
1699 SkMemoryStream* stream = new SkMemoryStream(totalSize); |
|
1700 char* dataStart = (char*)stream->getMemoryBase(); |
|
1701 sk_bzero(dataStart, totalSize); |
|
1702 char* dataPtr = dataStart; |
|
1703 |
|
1704 // compute font header entries |
|
1705 uint16_t entrySelector = 0; |
|
1706 uint16_t searchRange = 1; |
|
1707 while (searchRange < numTables >> 1) { |
|
1708 entrySelector++; |
|
1709 searchRange <<= 1; |
|
1710 } |
|
1711 searchRange <<= 4; |
|
1712 uint16_t rangeShift = (numTables << 4) - searchRange; |
|
1713 |
|
1714 // write font header |
|
1715 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr; |
|
1716 header->fontType = fontType; |
|
1717 header->numTables = SkEndian_SwapBE16(numTables); |
|
1718 header->searchRange = SkEndian_SwapBE16(searchRange); |
|
1719 header->entrySelector = SkEndian_SwapBE16(entrySelector); |
|
1720 header->rangeShift = SkEndian_SwapBE16(rangeShift); |
|
1721 dataPtr += sizeof(SkSFNTHeader); |
|
1722 |
|
1723 // write tables |
|
1724 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr; |
|
1725 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables; |
|
1726 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) { |
|
1727 size_t tableSize = tableSizes[tableIndex]; |
|
1728 this->getTableData(tableTags[tableIndex], 0, tableSize, dataPtr); |
|
1729 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]); |
|
1730 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr, |
|
1731 tableSize)); |
|
1732 entry->offset = SkEndian_SwapBE32(SkToU32(dataPtr - dataStart)); |
|
1733 entry->logicalLength = SkEndian_SwapBE32(SkToU32(tableSize)); |
|
1734 |
|
1735 dataPtr += (tableSize + 3) & ~3; |
|
1736 ++entry; |
|
1737 } |
|
1738 |
|
1739 return stream; |
|
1740 } |
|
1741 |
|
1742 /////////////////////////////////////////////////////////////////////////////// |
|
1743 /////////////////////////////////////////////////////////////////////////////// |
|
1744 |
|
1745 int SkTypeface_Mac::onGetUPEM() const { |
|
1746 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL)); |
|
1747 return CGFontGetUnitsPerEm(cgFont); |
|
1748 } |
|
1749 |
|
1750 SkTypeface::LocalizedStrings* SkTypeface_Mac::onCreateFamilyNameIterator() const { |
|
1751 SkTypeface::LocalizedStrings* nameIter = |
|
1752 SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(*this); |
|
1753 if (NULL == nameIter) { |
|
1754 AutoCFRelease<CFStringRef> cfLanguage; |
|
1755 AutoCFRelease<CFStringRef> cfFamilyName( |
|
1756 CTFontCopyLocalizedName(fFontRef, kCTFontFamilyNameKey, &cfLanguage)); |
|
1757 |
|
1758 SkString skLanguage; |
|
1759 SkString skFamilyName; |
|
1760 if (cfLanguage.get()) { |
|
1761 CFStringToSkString(cfLanguage.get(), &skLanguage); |
|
1762 } else { |
|
1763 skLanguage = "und"; //undetermined |
|
1764 } |
|
1765 if (cfFamilyName.get()) { |
|
1766 CFStringToSkString(cfFamilyName.get(), &skFamilyName); |
|
1767 } |
|
1768 |
|
1769 nameIter = new SkOTUtils::LocalizedStrings_SingleName(skFamilyName, skLanguage); |
|
1770 } |
|
1771 return nameIter; |
|
1772 } |
|
1773 |
|
1774 // If, as is the case with web fonts, the CTFont data isn't available, |
|
1775 // the CGFont data may work. While the CGFont may always provide the |
|
1776 // right result, leave the CTFont code path to minimize disruption. |
|
1777 static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) { |
|
1778 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag, |
|
1779 kCTFontTableOptionNoOptions); |
|
1780 if (NULL == data) { |
|
1781 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL)); |
|
1782 data = CGFontCopyTableForTag(cgFont, tag); |
|
1783 } |
|
1784 return data; |
|
1785 } |
|
1786 |
|
1787 int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const { |
|
1788 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef, |
|
1789 kCTFontTableOptionNoOptions)); |
|
1790 if (NULL == cfArray) { |
|
1791 return 0; |
|
1792 } |
|
1793 int count = SkToInt(CFArrayGetCount(cfArray)); |
|
1794 if (tags) { |
|
1795 for (int i = 0; i < count; ++i) { |
|
1796 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i)); |
|
1797 tags[i] = static_cast<SkFontTableTag>(fontTag); |
|
1798 } |
|
1799 } |
|
1800 return count; |
|
1801 } |
|
1802 |
|
1803 size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset, |
|
1804 size_t length, void* dstData) const { |
|
1805 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag)); |
|
1806 if (NULL == srcData) { |
|
1807 return 0; |
|
1808 } |
|
1809 |
|
1810 size_t srcSize = CFDataGetLength(srcData); |
|
1811 if (offset >= srcSize) { |
|
1812 return 0; |
|
1813 } |
|
1814 if (length > srcSize - offset) { |
|
1815 length = srcSize - offset; |
|
1816 } |
|
1817 if (dstData) { |
|
1818 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length); |
|
1819 } |
|
1820 return length; |
|
1821 } |
|
1822 |
|
1823 SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const { |
|
1824 return new SkScalerContext_Mac(const_cast<SkTypeface_Mac*>(this), desc); |
|
1825 } |
|
1826 |
|
1827 void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const { |
|
1828 if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag || |
|
1829 rec->fFlags & SkScalerContext::kLCD_Vertical_Flag) |
|
1830 { |
|
1831 rec->fMaskFormat = SkMask::kA8_Format; |
|
1832 // Render the glyphs as close as possible to what was requested. |
|
1833 // The above turns off subpixel rendering, but the user requested it. |
|
1834 // Normal hinting will cause the A8 masks to be generated from CoreGraphics subpixel masks. |
|
1835 // See comments below for more details. |
|
1836 rec->setHinting(SkPaint::kNormal_Hinting); |
|
1837 } |
|
1838 |
|
1839 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag | |
|
1840 SkScalerContext::kForceAutohinting_Flag | |
|
1841 SkScalerContext::kLCD_BGROrder_Flag | |
|
1842 SkScalerContext::kLCD_Vertical_Flag; |
|
1843 |
|
1844 rec->fFlags &= ~flagsWeDontSupport; |
|
1845 |
|
1846 bool lcdSupport = supports_LCD(); |
|
1847 |
|
1848 // Only two levels of hinting are supported. |
|
1849 // kNo_Hinting means avoid CoreGraphics outline dilation. |
|
1850 // kNormal_Hinting means CoreGraphics outline dilation is allowed. |
|
1851 // If there is no lcd support, hinting (dilation) cannot be supported. |
|
1852 SkPaint::Hinting hinting = rec->getHinting(); |
|
1853 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) { |
|
1854 hinting = SkPaint::kNo_Hinting; |
|
1855 } else if (SkPaint::kFull_Hinting == hinting) { |
|
1856 hinting = SkPaint::kNormal_Hinting; |
|
1857 } |
|
1858 rec->setHinting(hinting); |
|
1859 |
|
1860 // FIXME: lcd smoothed un-hinted rasterization unsupported. |
|
1861 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 . |
|
1862 // There is no current means to honor a request for unhinted lcd, |
|
1863 // so arbitrarilly ignore the hinting request and honor lcd. |
|
1864 |
|
1865 // Hinting and smoothing should be orthogonal, but currently they are not. |
|
1866 // CoreGraphics has no API to influence hinting. However, its lcd smoothed |
|
1867 // output is drawn from auto-dilated outlines (the amount of which is |
|
1868 // determined by AppleFontSmoothing). Its regular anti-aliased output is |
|
1869 // drawn from un-dilated outlines. |
|
1870 |
|
1871 // The behavior of Skia is as follows: |
|
1872 // [AA][no-hint]: generate AA using CoreGraphic's AA output. |
|
1873 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single |
|
1874 // channel. This matches [LCD][yes-hint] in weight. |
|
1875 // [LCD][no-hint]: curently unable to honor, and must pick which to respect. |
|
1876 // Currenly side with LCD, effectively ignoring the hinting setting. |
|
1877 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output. |
|
1878 |
|
1879 if (isLCDFormat(rec->fMaskFormat)) { |
|
1880 if (lcdSupport) { |
|
1881 //CoreGraphics creates 555 masks for smoothed text anyway. |
|
1882 rec->fMaskFormat = SkMask::kLCD16_Format; |
|
1883 rec->setHinting(SkPaint::kNormal_Hinting); |
|
1884 } else { |
|
1885 rec->fMaskFormat = SkMask::kA8_Format; |
|
1886 } |
|
1887 } |
|
1888 |
|
1889 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8. |
|
1890 // All other masks can use regular gamma. |
|
1891 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) { |
|
1892 #ifndef SK_GAMMA_APPLY_TO_A8 |
|
1893 rec->ignorePreBlend(); |
|
1894 #endif |
|
1895 } else { |
|
1896 //CoreGraphics dialates smoothed text as needed. |
|
1897 rec->setContrast(0); |
|
1898 } |
|
1899 } |
|
1900 |
|
1901 // we take ownership of the ref |
|
1902 static const char* get_str(CFStringRef ref, SkString* str) { |
|
1903 CFStringToSkString(ref, str); |
|
1904 CFSafeRelease(ref); |
|
1905 return str->c_str(); |
|
1906 } |
|
1907 |
|
1908 void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc, |
|
1909 bool* isLocalStream) const { |
|
1910 SkString tmpStr; |
|
1911 |
|
1912 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr)); |
|
1913 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr)); |
|
1914 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr)); |
|
1915 // TODO: need to add support for local-streams (here and openStream) |
|
1916 *isLocalStream = false; |
|
1917 } |
|
1918 |
|
1919 int SkTypeface_Mac::onCharsToGlyphs(const void* chars, Encoding encoding, |
|
1920 uint16_t glyphs[], int glyphCount) const |
|
1921 { |
|
1922 // Undocumented behavior of CTFontGetGlyphsForCharacters with non-bmp code points: |
|
1923 // When a surrogate pair is detected, the glyph index used is the index of the high surrogate. |
|
1924 // It is documented that if a mapping is unavailable, the glyph will be set to 0. |
|
1925 |
|
1926 SkAutoSTMalloc<1024, UniChar> charStorage; |
|
1927 const UniChar* src; // UniChar is a UTF-16 16-bit code unit. |
|
1928 int srcCount; |
|
1929 switch (encoding) { |
|
1930 case kUTF8_Encoding: { |
|
1931 const char* utf8 = reinterpret_cast<const char*>(chars); |
|
1932 UniChar* utf16 = charStorage.reset(2 * glyphCount); |
|
1933 src = utf16; |
|
1934 for (int i = 0; i < glyphCount; ++i) { |
|
1935 SkUnichar uni = SkUTF8_NextUnichar(&utf8); |
|
1936 utf16 += SkUTF16_FromUnichar(uni, utf16); |
|
1937 } |
|
1938 srcCount = SkToInt(utf16 - src); |
|
1939 break; |
|
1940 } |
|
1941 case kUTF16_Encoding: { |
|
1942 src = reinterpret_cast<const UniChar*>(chars); |
|
1943 int extra = 0; |
|
1944 for (int i = 0; i < glyphCount; ++i) { |
|
1945 if (SkUTF16_IsHighSurrogate(src[i + extra])) { |
|
1946 ++extra; |
|
1947 } |
|
1948 } |
|
1949 srcCount = glyphCount + extra; |
|
1950 break; |
|
1951 } |
|
1952 case kUTF32_Encoding: { |
|
1953 const SkUnichar* utf32 = reinterpret_cast<const SkUnichar*>(chars); |
|
1954 UniChar* utf16 = charStorage.reset(2 * glyphCount); |
|
1955 src = utf16; |
|
1956 for (int i = 0; i < glyphCount; ++i) { |
|
1957 utf16 += SkUTF16_FromUnichar(utf32[i], utf16); |
|
1958 } |
|
1959 srcCount = SkToInt(utf16 - src); |
|
1960 break; |
|
1961 } |
|
1962 } |
|
1963 |
|
1964 // If glyphs is NULL, CT still needs glyph storage for finding the first failure. |
|
1965 // Also, if there are any non-bmp code points, the provided 'glyphs' storage will be inadequate. |
|
1966 SkAutoSTMalloc<1024, uint16_t> glyphStorage; |
|
1967 uint16_t* macGlyphs = glyphs; |
|
1968 if (NULL == macGlyphs || srcCount > glyphCount) { |
|
1969 macGlyphs = glyphStorage.reset(srcCount); |
|
1970 } |
|
1971 |
|
1972 bool allEncoded = CTFontGetGlyphsForCharacters(fFontRef, src, macGlyphs, srcCount); |
|
1973 |
|
1974 // If there were any non-bmp, then copy and compact. |
|
1975 // If 'glyphs' is NULL, then compact glyphStorage in-place. |
|
1976 // If all are bmp and 'glyphs' is non-NULL, 'glyphs' already contains the compact glyphs. |
|
1977 // If some are non-bmp and 'glyphs' is non-NULL, copy and compact into 'glyphs'. |
|
1978 uint16_t* compactedGlyphs = glyphs; |
|
1979 if (NULL == compactedGlyphs) { |
|
1980 compactedGlyphs = macGlyphs; |
|
1981 } |
|
1982 if (srcCount > glyphCount) { |
|
1983 int extra = 0; |
|
1984 for (int i = 0; i < glyphCount; ++i) { |
|
1985 if (SkUTF16_IsHighSurrogate(src[i + extra])) { |
|
1986 ++extra; |
|
1987 } |
|
1988 compactedGlyphs[i] = macGlyphs[i + extra]; |
|
1989 } |
|
1990 } |
|
1991 |
|
1992 if (allEncoded) { |
|
1993 return glyphCount; |
|
1994 } |
|
1995 |
|
1996 // If we got false, then we need to manually look for first failure. |
|
1997 for (int i = 0; i < glyphCount; ++i) { |
|
1998 if (0 == compactedGlyphs[i]) { |
|
1999 return i; |
|
2000 } |
|
2001 } |
|
2002 // Odd to get here, as we expected CT to have returned true up front. |
|
2003 return glyphCount; |
|
2004 } |
|
2005 |
|
2006 int SkTypeface_Mac::onCountGlyphs() const { |
|
2007 return SkToInt(CTFontGetGlyphCount(fFontRef)); |
|
2008 } |
|
2009 |
|
2010 /////////////////////////////////////////////////////////////////////////////// |
|
2011 /////////////////////////////////////////////////////////////////////////////// |
|
2012 #if 1 |
|
2013 |
|
2014 static bool find_desc_str(CTFontDescriptorRef desc, CFStringRef name, SkString* value) { |
|
2015 AutoCFRelease<CFStringRef> ref((CFStringRef)CTFontDescriptorCopyAttribute(desc, name)); |
|
2016 if (NULL == ref.get()) { |
|
2017 return false; |
|
2018 } |
|
2019 CFStringToSkString(ref, value); |
|
2020 return true; |
|
2021 } |
|
2022 |
|
2023 static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) { |
|
2024 CFNumberRef num; |
|
2025 return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num) |
|
2026 && CFNumberIsFloatType(num) |
|
2027 && CFNumberGetValue(num, kCFNumberFloatType, value); |
|
2028 } |
|
2029 |
|
2030 #include "SkFontMgr.h" |
|
2031 |
|
2032 static int unit_weight_to_fontstyle(float unit) { |
|
2033 float value; |
|
2034 if (unit < 0) { |
|
2035 value = 100 + (1 + unit) * 300; |
|
2036 } else { |
|
2037 value = 400 + unit * 500; |
|
2038 } |
|
2039 return sk_float_round2int(value); |
|
2040 } |
|
2041 |
|
2042 static int unit_width_to_fontstyle(float unit) { |
|
2043 float value; |
|
2044 if (unit < 0) { |
|
2045 value = 1 + (1 + unit) * 4; |
|
2046 } else { |
|
2047 value = 5 + unit * 4; |
|
2048 } |
|
2049 return sk_float_round2int(value); |
|
2050 } |
|
2051 |
|
2052 static inline int sqr(int value) { |
|
2053 SkASSERT(SkAbs32(value) < 0x7FFF); // check for overflow |
|
2054 return value * value; |
|
2055 } |
|
2056 |
|
2057 // We normalize each axis (weight, width, italic) to be base-900 |
|
2058 static int compute_metric(const SkFontStyle& a, const SkFontStyle& b) { |
|
2059 return sqr(a.weight() - b.weight()) + |
|
2060 sqr((a.width() - b.width()) * 100) + |
|
2061 sqr((a.isItalic() != b.isItalic()) * 900); |
|
2062 } |
|
2063 |
|
2064 static SkFontStyle desc2fontstyle(CTFontDescriptorRef desc) { |
|
2065 AutoCFRelease<CFDictionaryRef> dict( |
|
2066 (CFDictionaryRef)CTFontDescriptorCopyAttribute(desc, |
|
2067 kCTFontTraitsAttribute)); |
|
2068 if (NULL == dict.get()) { |
|
2069 return SkFontStyle(); |
|
2070 } |
|
2071 |
|
2072 float weight, width, slant; |
|
2073 if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) { |
|
2074 weight = 0; |
|
2075 } |
|
2076 if (!find_dict_float(dict, kCTFontWidthTrait, &width)) { |
|
2077 width = 0; |
|
2078 } |
|
2079 if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) { |
|
2080 slant = 0; |
|
2081 } |
|
2082 |
|
2083 return SkFontStyle(unit_weight_to_fontstyle(weight), |
|
2084 unit_width_to_fontstyle(width), |
|
2085 slant ? SkFontStyle::kItalic_Slant |
|
2086 : SkFontStyle::kUpright_Slant); |
|
2087 } |
|
2088 |
|
2089 struct NameFontStyleRec { |
|
2090 SkString fFamilyName; |
|
2091 SkFontStyle fFontStyle; |
|
2092 }; |
|
2093 |
|
2094 static bool nameFontStyleProc(SkTypeface* face, SkTypeface::Style, |
|
2095 void* ctx) { |
|
2096 SkTypeface_Mac* macFace = (SkTypeface_Mac*)face; |
|
2097 const NameFontStyleRec* rec = (const NameFontStyleRec*)ctx; |
|
2098 |
|
2099 return macFace->fFontStyle == rec->fFontStyle && |
|
2100 macFace->fName == rec->fFamilyName; |
|
2101 } |
|
2102 |
|
2103 static SkTypeface* createFromDesc(CFStringRef cfFamilyName, |
|
2104 CTFontDescriptorRef desc) { |
|
2105 NameFontStyleRec rec; |
|
2106 CFStringToSkString(cfFamilyName, &rec.fFamilyName); |
|
2107 rec.fFontStyle = desc2fontstyle(desc); |
|
2108 |
|
2109 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(nameFontStyleProc, |
|
2110 &rec); |
|
2111 if (face) { |
|
2112 return face; |
|
2113 } |
|
2114 |
|
2115 AutoCFRelease<CFDictionaryRef> fontFamilyNameDictionary( |
|
2116 CFDictionaryCreate(kCFAllocatorDefault, |
|
2117 (const void**)&kCTFontFamilyNameAttribute, (const void**)&cfFamilyName, |
|
2118 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); |
|
2119 AutoCFRelease<CTFontDescriptorRef> fontDescriptor( |
|
2120 CTFontDescriptorCreateWithAttributes(fontFamilyNameDictionary)); |
|
2121 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithFontDescriptor(fontDescriptor, 0, NULL)); |
|
2122 CTFontRef ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, desc); |
|
2123 if (NULL == ctFont) { |
|
2124 return NULL; |
|
2125 } |
|
2126 |
|
2127 SkString str; |
|
2128 CFStringToSkString(cfFamilyName, &str); |
|
2129 |
|
2130 bool isFixedPitch; |
|
2131 (void)computeStyleBits(ctFont, &isFixedPitch); |
|
2132 SkFontID fontID = CTFontRef_to_SkFontID(ctFont); |
|
2133 |
|
2134 face = SkNEW_ARGS(SkTypeface_Mac, (rec.fFontStyle, fontID, isFixedPitch, |
|
2135 ctFont, str.c_str())); |
|
2136 SkTypefaceCache::Add(face, face->style()); |
|
2137 return face; |
|
2138 } |
|
2139 |
|
2140 class SkFontStyleSet_Mac : public SkFontStyleSet { |
|
2141 public: |
|
2142 SkFontStyleSet_Mac(CFStringRef familyName, CTFontDescriptorRef desc) |
|
2143 : fArray(CTFontDescriptorCreateMatchingFontDescriptors(desc, NULL)) |
|
2144 , fFamilyName(familyName) |
|
2145 , fCount(0) { |
|
2146 CFRetain(familyName); |
|
2147 if (NULL == fArray) { |
|
2148 fArray = CFArrayCreate(NULL, NULL, 0, NULL); |
|
2149 } |
|
2150 fCount = SkToInt(CFArrayGetCount(fArray)); |
|
2151 } |
|
2152 |
|
2153 virtual ~SkFontStyleSet_Mac() { |
|
2154 CFRelease(fArray); |
|
2155 CFRelease(fFamilyName); |
|
2156 } |
|
2157 |
|
2158 virtual int count() SK_OVERRIDE { |
|
2159 return fCount; |
|
2160 } |
|
2161 |
|
2162 virtual void getStyle(int index, SkFontStyle* style, |
|
2163 SkString* name) SK_OVERRIDE { |
|
2164 SkASSERT((unsigned)index < (unsigned)fCount); |
|
2165 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index); |
|
2166 if (style) { |
|
2167 *style = desc2fontstyle(desc); |
|
2168 } |
|
2169 if (name) { |
|
2170 if (!find_desc_str(desc, kCTFontStyleNameAttribute, name)) { |
|
2171 name->reset(); |
|
2172 } |
|
2173 } |
|
2174 } |
|
2175 |
|
2176 virtual SkTypeface* createTypeface(int index) SK_OVERRIDE { |
|
2177 SkASSERT((unsigned)index < (unsigned)CFArrayGetCount(fArray)); |
|
2178 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, index); |
|
2179 |
|
2180 return createFromDesc(fFamilyName, desc); |
|
2181 } |
|
2182 |
|
2183 virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE { |
|
2184 if (0 == fCount) { |
|
2185 return NULL; |
|
2186 } |
|
2187 return createFromDesc(fFamilyName, findMatchingDesc(pattern)); |
|
2188 } |
|
2189 |
|
2190 private: |
|
2191 CFArrayRef fArray; |
|
2192 CFStringRef fFamilyName; |
|
2193 int fCount; |
|
2194 |
|
2195 CTFontDescriptorRef findMatchingDesc(const SkFontStyle& pattern) const { |
|
2196 int bestMetric = SK_MaxS32; |
|
2197 CTFontDescriptorRef bestDesc = NULL; |
|
2198 |
|
2199 for (int i = 0; i < fCount; ++i) { |
|
2200 CTFontDescriptorRef desc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fArray, i); |
|
2201 int metric = compute_metric(pattern, desc2fontstyle(desc)); |
|
2202 if (0 == metric) { |
|
2203 return desc; |
|
2204 } |
|
2205 if (metric < bestMetric) { |
|
2206 bestMetric = metric; |
|
2207 bestDesc = desc; |
|
2208 } |
|
2209 } |
|
2210 SkASSERT(bestDesc); |
|
2211 return bestDesc; |
|
2212 } |
|
2213 }; |
|
2214 |
|
2215 class SkFontMgr_Mac : public SkFontMgr { |
|
2216 CFArrayRef fNames; |
|
2217 int fCount; |
|
2218 |
|
2219 CFStringRef stringAt(int index) const { |
|
2220 SkASSERT((unsigned)index < (unsigned)fCount); |
|
2221 return (CFStringRef)CFArrayGetValueAtIndex(fNames, index); |
|
2222 } |
|
2223 |
|
2224 static SkFontStyleSet* CreateSet(CFStringRef cfFamilyName) { |
|
2225 AutoCFRelease<CFMutableDictionaryRef> cfAttr( |
|
2226 CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
|
2227 &kCFTypeDictionaryKeyCallBacks, |
|
2228 &kCFTypeDictionaryValueCallBacks)); |
|
2229 |
|
2230 CFDictionaryAddValue(cfAttr, kCTFontFamilyNameAttribute, cfFamilyName); |
|
2231 |
|
2232 AutoCFRelease<CTFontDescriptorRef> desc( |
|
2233 CTFontDescriptorCreateWithAttributes(cfAttr)); |
|
2234 return SkNEW_ARGS(SkFontStyleSet_Mac, (cfFamilyName, desc)); |
|
2235 } |
|
2236 |
|
2237 public: |
|
2238 SkFontMgr_Mac() |
|
2239 : fNames(SkCTFontManagerCopyAvailableFontFamilyNames()) |
|
2240 , fCount(fNames ? SkToInt(CFArrayGetCount(fNames)) : 0) {} |
|
2241 |
|
2242 virtual ~SkFontMgr_Mac() { |
|
2243 CFSafeRelease(fNames); |
|
2244 } |
|
2245 |
|
2246 protected: |
|
2247 virtual int onCountFamilies() const SK_OVERRIDE { |
|
2248 return fCount; |
|
2249 } |
|
2250 |
|
2251 virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE { |
|
2252 if ((unsigned)index < (unsigned)fCount) { |
|
2253 CFStringToSkString(this->stringAt(index), familyName); |
|
2254 } else { |
|
2255 familyName->reset(); |
|
2256 } |
|
2257 } |
|
2258 |
|
2259 virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE { |
|
2260 if ((unsigned)index >= (unsigned)fCount) { |
|
2261 return NULL; |
|
2262 } |
|
2263 return CreateSet(this->stringAt(index)); |
|
2264 } |
|
2265 |
|
2266 virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE { |
|
2267 AutoCFRelease<CFStringRef> cfName(make_CFString(familyName)); |
|
2268 return CreateSet(cfName); |
|
2269 } |
|
2270 |
|
2271 virtual SkTypeface* onMatchFamilyStyle(const char familyName[], |
|
2272 const SkFontStyle&) const SK_OVERRIDE { |
|
2273 return NULL; |
|
2274 } |
|
2275 |
|
2276 virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember, |
|
2277 const SkFontStyle&) const SK_OVERRIDE { |
|
2278 return NULL; |
|
2279 } |
|
2280 |
|
2281 virtual SkTypeface* onCreateFromData(SkData* data, |
|
2282 int ttcIndex) const SK_OVERRIDE { |
|
2283 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromData(data)); |
|
2284 if (NULL == pr) { |
|
2285 return NULL; |
|
2286 } |
|
2287 return create_from_dataProvider(pr); |
|
2288 } |
|
2289 |
|
2290 virtual SkTypeface* onCreateFromStream(SkStream* stream, |
|
2291 int ttcIndex) const SK_OVERRIDE { |
|
2292 AutoCFRelease<CGDataProviderRef> pr(SkCreateDataProviderFromStream(stream)); |
|
2293 if (NULL == pr) { |
|
2294 return NULL; |
|
2295 } |
|
2296 return create_from_dataProvider(pr); |
|
2297 } |
|
2298 |
|
2299 virtual SkTypeface* onCreateFromFile(const char path[], |
|
2300 int ttcIndex) const SK_OVERRIDE { |
|
2301 AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path)); |
|
2302 if (NULL == pr) { |
|
2303 return NULL; |
|
2304 } |
|
2305 return create_from_dataProvider(pr); |
|
2306 } |
|
2307 |
|
2308 virtual SkTypeface* onLegacyCreateTypeface(const char familyName[], |
|
2309 unsigned styleBits) const SK_OVERRIDE { |
|
2310 return create_typeface(NULL, familyName, (SkTypeface::Style)styleBits); |
|
2311 } |
|
2312 }; |
|
2313 |
|
2314 /////////////////////////////////////////////////////////////////////////////// |
|
2315 |
|
2316 SkFontMgr* SkFontMgr::Factory() { |
|
2317 return SkNEW(SkFontMgr_Mac); |
|
2318 } |
|
2319 #endif |
|
2320 |
|
2321 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, |
|
2322 const char famillyName[], |
|
2323 SkTypeface::Style style) |
|
2324 { |
|
2325 SkDEBUGFAIL("SkFontHost::FindTypeface unimplemented"); |
|
2326 return NULL; |
|
2327 } |
|
2328 |
|
2329 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream*) |
|
2330 { |
|
2331 SkDEBUGFAIL("SkFontHost::CreateTypeface unimplemented"); |
|
2332 return NULL; |
|
2333 } |
|
2334 |
|
2335 SkTypeface* SkFontHost::CreateTypefaceFromFile(char const*) |
|
2336 { |
|
2337 SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented"); |
|
2338 return NULL; |
|
2339 } |
|
2340 |