blob: 83c811a684e0aafb6f8d1566969434e02b260079 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com0680d6c2008-12-19 19:46:15 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00009#include <vector>
10#ifdef SK_BUILD_FOR_MAC
11#import <ApplicationServices/ApplicationServices.h>
12#endif
reed@android.com0680d6c2008-12-19 19:46:15 +000013
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +000014#ifdef SK_BUILD_FOR_IOS
15#include <CoreText/CoreText.h>
16#include <CoreGraphics/CoreGraphics.h>
17#include <CoreFoundation/CoreFoundation.h>
18#endif
19
20#include "SkFontHost.h"
21#include "SkCGUtils.h"
22#include "SkColorPriv.h"
23#include "SkDescriptor.h"
24#include "SkEndian.h"
25#include "SkFontDescriptor.h"
26#include "SkFloatingPoint.h"
27#include "SkGlyph.h"
28#include "SkMaskGamma.h"
29#include "SkSFNTHeader.h"
30#include "SkOTTable_glyf.h"
31#include "SkOTTable_head.h"
32#include "SkOTTable_hhea.h"
33#include "SkOTTable_loca.h"
34#include "SkOTUtils.h"
35#include "SkPaint.h"
36#include "SkPath.h"
37#include "SkString.h"
38#include "SkStream.h"
39#include "SkThread.h"
40#include "SkTypeface_mac.h"
41#include "SkUtils.h"
42#include "SkTypefaceCache.h"
43
44class SkScalerContext_Mac;
45
46// Being templated and taking const T* prevents calling
47// CFSafeRelease(autoCFRelease) through implicit conversion.
48template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
49 if (cfTypeRef) {
50 CFRelease(cfTypeRef);
51 }
52}
53
54// Being templated and taking const T* prevents calling
55// CFSafeRetain(autoCFRelease) through implicit conversion.
56template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
57 if (cfTypeRef) {
58 CFRetain(cfTypeRef);
59 }
60}
61
62/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
63template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
64public:
65 explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
66 ~AutoCFRelease() { CFSafeRelease(fCFRef); }
67
68 void reset(CFRef that = NULL) {
69 CFSafeRetain(that);
70 CFSafeRelease(fCFRef);
71 fCFRef = that;
72 }
73
74 AutoCFRelease& operator =(CFRef that) {
75 reset(that);
76 return *this;
77 }
78
79 operator CFRef() const { return fCFRef; }
80 CFRef get() const { return fCFRef; }
81
82private:
83 CFRef fCFRef;
84};
85
86template<typename T> class AutoCGTable : SkNoncopyable {
87public:
88 AutoCGTable(CGFontRef font)
89 //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
90 : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
91 , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
92 { }
93
94 const T* operator->() const { return fData; }
95
96private:
97 AutoCFRelease<CFDataRef> fCFData;
98public:
99 const T* fData;
100};
101
102// inline versions of these rect helpers
103
104static bool CGRectIsEmpty_inline(const CGRect& rect) {
105 return rect.size.width <= 0 || rect.size.height <= 0;
106}
107
108static void CGRectInset_inline(CGRect* rect, CGFloat dx, CGFloat dy) {
109 rect->origin.x += dx;
110 rect->origin.y += dy;
111 rect->size.width -= dx * 2;
112 rect->size.height -= dy * 2;
113}
114
115static CGFloat CGRectGetMinX_inline(const CGRect& rect) {
116 return rect.origin.x;
117}
118
119static CGFloat CGRectGetMaxX_inline(const CGRect& rect) {
120 return rect.origin.x + rect.size.width;
121}
122
123static CGFloat CGRectGetMinY_inline(const CGRect& rect) {
124 return rect.origin.y;
125}
126
127static CGFloat CGRectGetMaxY_inline(const CGRect& rect) {
128 return rect.origin.y + rect.size.height;
129}
130
131static CGFloat CGRectGetWidth_inline(const CGRect& rect) {
132 return rect.size.width;
133}
134
135///////////////////////////////////////////////////////////////////////////////
136
137static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
138 size_t width, size_t height, size_t rowBytes) {
139 SkASSERT(width);
140 SkASSERT(width * sizeof(uint32_t) <= rowBytes);
141
142 if (width >= 32) {
143 while (height) {
144 sk_memset32(ptr, value, width);
145 ptr = (uint32_t*)((char*)ptr + rowBytes);
146 height -= 1;
147 }
148 return;
149 }
150
151 rowBytes -= width * sizeof(uint32_t);
152
153 if (width >= 8) {
154 while (height) {
155 int w = width;
156 do {
157 *ptr++ = value; *ptr++ = value;
158 *ptr++ = value; *ptr++ = value;
159 *ptr++ = value; *ptr++ = value;
160 *ptr++ = value; *ptr++ = value;
161 w -= 8;
162 } while (w >= 8);
163 while (--w >= 0) {
164 *ptr++ = value;
165 }
166 ptr = (uint32_t*)((char*)ptr + rowBytes);
167 height -= 1;
168 }
169 } else {
170 while (height) {
171 int w = width;
172 do {
173 *ptr++ = value;
174 } while (--w > 0);
175 ptr = (uint32_t*)((char*)ptr + rowBytes);
176 height -= 1;
177 }
178 }
179}
180
181#include <sys/utsname.h>
182
183typedef uint32_t CGRGBPixel;
184
185static unsigned CGRGBPixel_getAlpha(CGRGBPixel pixel) {
186 return pixel & 0xFF;
187}
188
189// The calls to support subpixel are present in 10.5, but are not included in
190// the 10.5 SDK. The needed calls have been extracted from the 10.6 SDK and are
191// included below. To verify that CGContextSetShouldSubpixelQuantizeFonts, for
192// instance, is present in the 10.5 CoreGraphics libary, use:
193// cd /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/
194// cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/
195// nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts
196
197#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
198CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value);
199CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value);
200CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value);
201CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value);
202CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value);
203#endif
204
205static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
206
207// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
208static int readVersion() {
209 struct utsname info;
210 if (uname(&info) != 0) {
211 SkDebugf("uname failed\n");
212 return 0;
213 }
214 if (strcmp(info.sysname, "Darwin") != 0) {
215 SkDebugf("unexpected uname sysname %s\n", info.sysname);
216 return 0;
217 }
218 char* dot = strchr(info.release, '.');
219 if (!dot) {
220 SkDebugf("expected dot in uname release %s\n", info.release);
221 return 0;
222 }
223 int version = atoi(info.release);
224 if (version == 0) {
225 SkDebugf("could not parse uname release %s\n", info.release);
226 }
227 return version;
228}
229
230static int darwinVersion() {
231 static int darwin_version = readVersion();
232 return darwin_version;
233}
234
235static bool isLeopard() {
236 return darwinVersion() == 9;
237}
238
239static bool isSnowLeopard() {
240 return darwinVersion() == 10;
241}
242
243static bool isLion() {
244 return darwinVersion() == 11;
245}
246
247static bool isMountainLion() {
248 return darwinVersion() == 12;
249}
250
251static bool isLCDFormat(unsigned format) {
252 return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
253}
254
255static CGFloat ScalarToCG(SkScalar scalar) {
256 if (sizeof(CGFloat) == sizeof(float)) {
257 return SkScalarToFloat(scalar);
258 } else {
259 SkASSERT(sizeof(CGFloat) == sizeof(double));
260 return (CGFloat) SkScalarToDouble(scalar);
261 }
262}
263
264static SkScalar CGToScalar(CGFloat cgFloat) {
265 if (sizeof(CGFloat) == sizeof(float)) {
266 return SkFloatToScalar(cgFloat);
267 } else {
268 SkASSERT(sizeof(CGFloat) == sizeof(double));
269 return SkDoubleToScalar(cgFloat);
270 }
271}
272
273static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
274 SkScalar sx = SK_Scalar1,
275 SkScalar sy = SK_Scalar1) {
276 return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
277 -ScalarToCG(matrix[SkMatrix::kMSkewY] * sy),
278 -ScalarToCG(matrix[SkMatrix::kMSkewX] * sx),
279 ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
280 ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
281 ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
282}
283
284static SkScalar getFontScale(CGFontRef cgFont) {
285 int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
286 return SkScalarInvert(SkIntToScalar(unitsPerEm));
287}
288
289///////////////////////////////////////////////////////////////////////////////
290
291#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
292#define BITMAP_INFO_GRAY (kCGImageAlphaNone)
293
294/**
295 * There does not appear to be a publicly accessable API for determining if lcd
296 * font smoothing will be applied if we request it. The main issue is that if
297 * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
298 */
299static bool supports_LCD() {
300 static int gSupportsLCD = -1;
301 if (gSupportsLCD >= 0) {
302 return (bool) gSupportsLCD;
303 }
304 uint32_t rgb = 0;
305 AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
306 AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
307 colorspace, BITMAP_INFO_RGB));
308 CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
309 CGContextSetShouldSmoothFonts(cgContext, true);
310 CGContextSetShouldAntialias(cgContext, true);
311 CGContextSetTextDrawingMode(cgContext, kCGTextFill);
312 CGContextSetGrayFillColor(cgContext, 1, 1);
313 CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
314 uint32_t r = (rgb >> 16) & 0xFF;
315 uint32_t g = (rgb >> 8) & 0xFF;
316 uint32_t b = (rgb >> 0) & 0xFF;
317 gSupportsLCD = (r != g || r != b);
318 return (bool) gSupportsLCD;
319}
320
321class Offscreen {
322public:
323 Offscreen();
324
325 CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
326 CGGlyph glyphID, size_t* rowBytesPtr,
327 bool generateA8FromLCD);
328
329private:
330 enum {
331 kSize = 32 * 32 * sizeof(CGRGBPixel)
332 };
333 SkAutoSMalloc<kSize> fImageStorage;
334 AutoCFRelease<CGColorSpaceRef> fRGBSpace;
335
336 // cached state
337 AutoCFRelease<CGContextRef> fCG;
338 SkISize fSize;
339 bool fDoAA;
340 bool fDoLCD;
341
342 static int RoundSize(int dimension) {
343 return SkNextPow2(dimension);
344 }
345};
346
347Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL) {
348 fSize.set(0, 0);
349}
350
351///////////////////////////////////////////////////////////////////////////////
352
353static SkTypeface::Style computeStyleBits(CTFontRef font, bool* isMonospace) {
354 unsigned style = SkTypeface::kNormal;
355 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(font);
356
357 if (traits & kCTFontBoldTrait) {
358 style |= SkTypeface::kBold;
359 }
360 if (traits & kCTFontItalicTrait) {
361 style |= SkTypeface::kItalic;
362 }
363 if (isMonospace) {
364 *isMonospace = (traits & kCTFontMonoSpaceTrait) != 0;
365 }
366 return (SkTypeface::Style)style;
367}
368
369static SkFontID CTFontRef_to_SkFontID(CTFontRef fontRef) {
370 SkFontID id = 0;
371// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
372// bracket this to be Mac only.
373#ifdef SK_BUILD_FOR_MAC
374 ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
375 id = (SkFontID)ats;
376 if (id != 0) {
377 id &= 0x3FFFFFFF; // make top two bits 00
378 return id;
379 }
380#endif
381 // CTFontGetPlatformFont returns NULL if the font is local
382 // (e.g., was created by a CSS3 @font-face rule).
383 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fontRef, NULL));
384 AutoCGTable<SkOTTableHead> headTable(cgFont);
385 if (headTable.fData) {
386 id = (SkFontID) headTable->checksumAdjustment;
387 id = (id & 0x3FFFFFFF) | 0x40000000; // make top two bits 01
388 }
389 // well-formed fonts have checksums, but as a last resort, use the pointer.
390 if (id == 0) {
391 id = (SkFontID) (uintptr_t) fontRef;
392 id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
393 }
394 return id;
395}
396
397class SkTypeface_Mac : public SkTypeface {
398public:
399 SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isMonospace,
400 CTFontRef fontRef, const char name[])
401 : SkTypeface(style, fontID, isMonospace)
402 , fName(name)
403 , fFontRef(fontRef) // caller has already called CFRetain for us
404 {
405 SkASSERT(fontRef);
406 }
407
408 SkString fName;
409 AutoCFRelease<CTFontRef> fFontRef;
410
411protected:
412 friend class SkFontHost; // to access our protected members for deprecated methods
413
414 virtual int onGetUPEM() const SK_OVERRIDE;
415 virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
416 virtual size_t onGetTableData(SkFontTableTag, size_t offset,
417 size_t length, void* data) const SK_OVERRIDE;
418 virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE;
419 virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE;
420 virtual void onGetFontDescriptor(SkFontDescriptor*) const SK_OVERRIDE;
421
422private:
423 typedef SkTypeface INHERITED;
424};
425
426static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
427 SkASSERT(fontRef);
428 bool isMonospace;
429 SkTypeface::Style style = computeStyleBits(fontRef, &isMonospace);
430 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
431
432 return new SkTypeface_Mac(style, fontID, isMonospace, fontRef, name);
433}
434
435static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
436 CTFontRef ctFont = NULL;
437
438 CTFontSymbolicTraits ctFontTraits = 0;
439 if (theStyle & SkTypeface::kBold) {
440 ctFontTraits |= kCTFontBoldTrait;
441 }
442 if (theStyle & SkTypeface::kItalic) {
443 ctFontTraits |= kCTFontItalicTrait;
444 }
445
446 // Create the font info
447 AutoCFRelease<CFStringRef> cfFontName(
448 CFStringCreateWithCString(NULL, familyName, kCFStringEncodingUTF8));
449
450 AutoCFRelease<CFNumberRef> cfFontTraits(
451 CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
452
453 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
454 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
455 &kCFTypeDictionaryKeyCallBacks,
456 &kCFTypeDictionaryValueCallBacks));
457
458 AutoCFRelease<CFMutableDictionaryRef> cfTraits(
459 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
460 &kCFTypeDictionaryKeyCallBacks,
461 &kCFTypeDictionaryValueCallBacks));
462
463 // Create the font
464 if (cfFontName != NULL && cfFontTraits != NULL && cfAttributes != NULL && cfTraits != NULL) {
465 CFDictionaryAddValue(cfTraits, kCTFontSymbolicTrait, cfFontTraits);
466
467 CFDictionaryAddValue(cfAttributes, kCTFontFamilyNameAttribute, cfFontName);
468 CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
469
470 AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
471 CTFontDescriptorCreateWithAttributes(cfAttributes));
472
473 if (ctFontDesc != NULL) {
474 if (isLeopard()) {
475 // CTFontCreateWithFontDescriptor on Leopard ignores the name
476 AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFontName, 1, NULL));
477 ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, ctFontDesc);
478 } else {
479 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
480 }
481 }
482 }
483
484 return ctFont ? NewFromFontRef(ctFont, familyName) : NULL;
485}
486
487static CTFontRef GetFontRefFromFontID(SkFontID fontID) {
488 SkTypeface_Mac* face = reinterpret_cast<SkTypeface_Mac*>(SkTypefaceCache::FindByID(fontID));
489 return face ? face->fFontRef.get() : NULL;
490}
491
492static SkTypeface* GetDefaultFace() {
493 SK_DECLARE_STATIC_MUTEX(gMutex);
494 SkAutoMutexAcquire ma(gMutex);
495
496 static SkTypeface* gDefaultFace;
497
498 if (NULL == gDefaultFace) {
499 gDefaultFace = NewFromName(FONT_DEFAULT_NAME, SkTypeface::kNormal);
500 SkTypefaceCache::Add(gDefaultFace, SkTypeface::kNormal);
501 }
502 return gDefaultFace;
503}
504
505///////////////////////////////////////////////////////////////////////////////
506
507extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
508CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
509 const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
510 return macface ? macface->fFontRef.get() : NULL;
511}
512
513/* This function is visible on the outside. It first searches the cache, and if
514 * not found, returns a new entry (after adding it to the cache).
515 */
516SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef) {
517 SkFontID fontID = CTFontRef_to_SkFontID(fontRef);
518 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
519 if (face) {
520 face->ref();
521 } else {
522 face = NewFromFontRef(fontRef, NULL);
523 SkTypefaceCache::Add(face, face->style());
524 // NewFromFontRef doesn't retain the parameter, but the typeface it
525 // creates does release it in its destructor, so we balance that with
526 // a retain call here.
527 CFRetain(fontRef);
528 }
529 SkASSERT(face->getRefCnt() > 1);
530 return face;
531}
532
533struct NameStyleRec {
534 const char* fName;
535 SkTypeface::Style fStyle;
536};
537
538static bool FindByNameStyle(SkTypeface* face, SkTypeface::Style style,
539 void* ctx) {
540 const SkTypeface_Mac* mface = reinterpret_cast<SkTypeface_Mac*>(face);
541 const NameStyleRec* rec = reinterpret_cast<const NameStyleRec*>(ctx);
542
543 return rec->fStyle == style && mface->fName.equals(rec->fName);
544}
545
546static const char* map_css_names(const char* name) {
547 static const struct {
548 const char* fFrom; // name the caller specified
549 const char* fTo; // "canonical" name we map to
550 } gPairs[] = {
551 { "sans-serif", "Helvetica" },
552 { "serif", "Times" },
553 { "monospace", "Courier" }
554 };
555
556 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
557 if (strcmp(name, gPairs[i].fFrom) == 0) {
558 return gPairs[i].fTo;
559 }
560 }
561 return name; // no change
562}
563
564SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
565 const char familyName[],
566 SkTypeface::Style style) {
567 if (familyName) {
568 familyName = map_css_names(familyName);
569 }
570
571 // Clone an existing typeface
572 // TODO: only clone if style matches the familyFace's style...
573 if (familyName == NULL && familyFace != NULL) {
574 familyFace->ref();
575 return const_cast<SkTypeface*>(familyFace);
576 }
577
578 if (!familyName || !*familyName) {
579 familyName = FONT_DEFAULT_NAME;
580 }
581
582 NameStyleRec rec = { familyName, style };
583 SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByNameStyle, &rec);
584
585 if (NULL == face) {
586 face = NewFromName(familyName, style);
587 if (face) {
588 SkTypefaceCache::Add(face, style);
589 } else {
590 face = GetDefaultFace();
591 face->ref();
592 }
593 }
594 return face;
595}
596
597static void flip(SkMatrix* matrix) {
598 matrix->setSkewX(-matrix->getSkewX());
599 matrix->setSkewY(-matrix->getSkewY());
600}
601
602///////////////////////////////////////////////////////////////////////////////
603
604struct GlyphRect {
605 int16_t fMinX;
606 int16_t fMinY;
607 int16_t fMaxX;
608 int16_t fMaxY;
609};
610
611class SkScalerContext_Mac : public SkScalerContext {
612public:
613 SkScalerContext_Mac(const SkDescriptor* desc);
614 virtual ~SkScalerContext_Mac(void);
615
616
617protected:
618 unsigned generateGlyphCount(void) SK_OVERRIDE;
619 uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
620 void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
621 void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
622 void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
623 void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
624 void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
625
626private:
627 static void CTPathElement(void *info, const CGPathElement *element);
628 uint16_t getFBoundingBoxesGlyphOffset();
629 void getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const;
630 bool generateBBoxes();
631
632 CGAffineTransform fTransform;
633 SkMatrix fUnitMatrix; // without font size
634 SkMatrix fVerticalMatrix; // unit rotated
635 SkMatrix fMatrix; // with font size
636 SkMatrix fFBoundingBoxesMatrix; // lion-specific fix
637 Offscreen fOffscreen;
638 AutoCFRelease<CTFontRef> fCTFont;
639 AutoCFRelease<CTFontRef> fCTVerticalFont; // for vertical advance
640 AutoCFRelease<CGFontRef> fCGFont;
641 GlyphRect* fFBoundingBoxes;
642 uint16_t fFBoundingBoxesGlyphOffset;
643 uint16_t fGlyphCount;
644 bool fGeneratedFBoundingBoxes;
645 bool fDoSubPosition;
646 bool fVertical;
647
648 friend class Offscreen;
649};
650
651SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
652 : SkScalerContext(desc)
653 , fFBoundingBoxes(NULL)
654 , fFBoundingBoxesGlyphOffset(0)
655 , fGeneratedFBoundingBoxes(false)
656{
657 CTFontRef ctFont = GetFontRefFromFontID(fRec.fFontID);
658 CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
659
660 // Get the state we need
661 fRec.getSingleMatrix(&fMatrix);
662 fUnitMatrix = fMatrix;
663
664 // extract the font size out of the matrix, but leave the skewing for italic
665 SkScalar reciprocal = SkScalarInvert(fRec.fTextSize);
666 fUnitMatrix.preScale(reciprocal, reciprocal);
667
668 SkASSERT(numGlyphs >= 1 && numGlyphs <= 0xFFFF);
669
670 fTransform = MatrixToCGAffineTransform(fMatrix);
671
672 CGAffineTransform transform;
673 CGFloat unitFontSize;
674 if (isLeopard()) {
675 // passing 1 for pointSize to Leopard sets the font size to 1 pt.
676 // pass the CoreText size explicitly
677 transform = MatrixToCGAffineTransform(fUnitMatrix);
678 unitFontSize = SkScalarToFloat(fRec.fTextSize);
679 } else {
680 // since our matrix includes everything, we pass 1 for pointSize
681 transform = fTransform;
682 unitFontSize = 1;
683 }
684 flip(&fUnitMatrix); // flip to fix up bounds later
685 fVertical = SkToBool(fRec.fFlags & kVertical_Flag);
686 AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
687 if (fVertical) {
688 AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
689 kCFAllocatorDefault, 0,
690 &kCFTypeDictionaryKeyCallBacks,
691 &kCFTypeDictionaryValueCallBacks));
692 if (cfAttributes) {
693 CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
694 AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
695 kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
696 CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
697 ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
698 }
699 }
700 fCTFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform, ctFontDesc);
701 fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL);
702 if (fVertical) {
703 CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
704 transform = CGAffineTransformConcat(rotateLeft, transform);
705 fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform, NULL);
706 fVerticalMatrix = fUnitMatrix;
707 if (isSnowLeopard()) {
708 SkScalar scale = SkScalarMul(fRec.fTextSize, getFontScale(fCGFont));
709 fVerticalMatrix.preScale(scale, scale);
710 } else {
711 fVerticalMatrix.preRotate(SkIntToScalar(90));
712 }
713 fVerticalMatrix.postScale(SK_Scalar1, -SK_Scalar1);
714 }
715 fGlyphCount = SkToU16(numGlyphs);
716 fDoSubPosition = SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
717}
718
719SkScalerContext_Mac::~SkScalerContext_Mac() {
720 delete[] fFBoundingBoxes;
721}
722
723CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
724 CGGlyph glyphID, size_t* rowBytesPtr,
725 bool generateA8FromLCD) {
726 if (!fRGBSpace) {
727 //It doesn't appear to matter what color space is specified.
728 //Regular blends and antialiased text are always (s*a + d*(1-a))
729 //and smoothed text is always g=2.0.
730 fRGBSpace = CGColorSpaceCreateDeviceRGB();
731 }
732
733 // default to kBW_Format
734 bool doAA = false;
735 bool doLCD = false;
736
737 if (SkMask::kBW_Format != glyph.fMaskFormat) {
738 doLCD = true;
739 doAA = true;
740 }
741
742 // FIXME: lcd smoothed un-hinted rasterization unsupported.
743 if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
744 doLCD = false;
745 doAA = true;
746 }
747
748 size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
749 if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
750 if (fSize.fWidth < glyph.fWidth) {
751 fSize.fWidth = RoundSize(glyph.fWidth);
752 }
753 if (fSize.fHeight < glyph.fHeight) {
754 fSize.fHeight = RoundSize(glyph.fHeight);
755 }
756
757 rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
758 void* image = fImageStorage.reset(rowBytes * fSize.fHeight);
759 fCG = CGBitmapContextCreate(image, fSize.fWidth, fSize.fHeight, 8,
760 rowBytes, fRGBSpace, BITMAP_INFO_RGB);
761
762 // skia handles quantization itself, so we disable this for cg to get
763 // full fractional data from them.
764 CGContextSetAllowsFontSubpixelQuantization(fCG, false);
765 CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
766
767 CGContextSetTextDrawingMode(fCG, kCGTextFill);
768 CGContextSetFont(fCG, context.fCGFont);
769 CGContextSetFontSize(fCG, 1);
770 CGContextSetTextMatrix(fCG, context.fTransform);
771
772 CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition);
773 CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition);
774
775 // Draw white on black to create mask.
776 // TODO: Draw black on white and invert, CG has a special case codepath.
777 CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
778
779 // force our checks below to happen
780 fDoAA = !doAA;
781 fDoLCD = !doLCD;
782 }
783
784 if (fDoAA != doAA) {
785 CGContextSetShouldAntialias(fCG, doAA);
786 fDoAA = doAA;
787 }
788 if (fDoLCD != doLCD) {
789 CGContextSetShouldSmoothFonts(fCG, doLCD);
790 fDoLCD = doLCD;
791 }
792
793 CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
794 // skip rows based on the glyph's height
795 image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
796
797 // erase to black
798 sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
799
800 float subX = 0;
801 float subY = 0;
802 if (context.fDoSubPosition) {
803 subX = SkFixedToFloat(glyph.getSubXFixed());
804 subY = SkFixedToFloat(glyph.getSubYFixed());
805 }
806 if (context.fVertical) {
807 SkIPoint offset;
808 context.getVerticalOffset(glyphID, &offset);
809 subX += offset.fX;
810 subY += offset.fY;
811 }
812 CGContextShowGlyphsAtPoint(fCG, -glyph.fLeft + subX,
813 glyph.fTop + glyph.fHeight - subY,
814 &glyphID, 1);
815
816 SkASSERT(rowBytesPtr);
817 *rowBytesPtr = rowBytes;
818 return image;
819}
820
821void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const {
822 CGSize vertOffset;
823 CTFontGetVerticalTranslationsForGlyphs(fCTVerticalFont, &glyphID, &vertOffset, 1);
824 const SkPoint trans = {CGToScalar(vertOffset.width),
825 CGToScalar(vertOffset.height)};
826 SkPoint floatOffset;
827 fVerticalMatrix.mapPoints(&floatOffset, &trans, 1);
828 if (!isSnowLeopard()) {
829 // SnowLeopard fails to apply the font's matrix to the vertical metrics,
830 // but Lion and Leopard do. The unit matrix describes the font's matrix at
831 // point size 1. There may be some way to avoid mapping here by setting up
832 // fVerticalMatrix differently, but this works for now.
833 fUnitMatrix.mapPoints(&floatOffset, 1);
834 }
835 offset->fX = SkScalarRound(floatOffset.fX);
836 offset->fY = SkScalarRound(floatOffset.fY);
837}
838
839uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
840 if (fFBoundingBoxesGlyphOffset) {
841 return fFBoundingBoxesGlyphOffset;
842 }
843 fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
844 AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
845 if (hheaTable.fData) {
846 fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
847 }
848 return fFBoundingBoxesGlyphOffset;
849}
reed@android.com0680d6c2008-12-19 19:46:15 +0000850
reed@android.comfeda2f92010-05-19 13:47:05 +0000851/*
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000852 * Lion has a bug in CTFontGetBoundingRectsForGlyphs which returns a bad value
853 * in theBounds.origin.x for fonts whose numOfLogHorMetrics is less than its
854 * glyph count. This workaround reads the glyph bounds from the font directly.
855 *
856 * The table is computed only if the font is a TrueType font, if the glyph
857 * value is >= fFBoundingBoxesGlyphOffset. (called only if fFBoundingBoxesGlyphOffset < fGlyphCount).
858 *
859 * TODO: A future optimization will compute fFBoundingBoxes once per CGFont, and
860 * compute fFBoundingBoxesMatrix once per font context.
861 */
862bool SkScalerContext_Mac::generateBBoxes() {
863 if (fGeneratedFBoundingBoxes) {
864 return NULL != fFBoundingBoxes;
865 }
866 fGeneratedFBoundingBoxes = true;
reed@android.com0bf64d42009-03-09 17:22:22 +0000867
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +0000868 AutoCGTable<SkOTTableHead> headTable(fCGFont);
869 if (!headTable.fData) {
870 return false;
871 }
872
873 AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
874 if (!locaTable.fData) {
875 return false;
876 }
877
878 AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
879 if (!glyfTable.fData) {
880 return false;
881 }
882
883 uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
884 fFBoundingBoxes = new GlyphRect[entries];
885
886 SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
887 SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
888 glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
889 for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
890 const SkOTTableGlyphData* glyphData = glyphDataIter.next();
891 GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
892 rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
893 rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
894 rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
895 rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
896 }
897 fFBoundingBoxesMatrix = fMatrix;
898 flip(&fFBoundingBoxesMatrix);
899 SkScalar fontScale = getFontScale(fCGFont);
900 fFBoundingBoxesMatrix.preScale(fontScale, fontScale);
901 return true;
902}
903
904unsigned SkScalerContext_Mac::generateGlyphCount(void) {
905 return fGlyphCount;
906}
907
908uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
909 CGGlyph cgGlyph;
910 UniChar theChar;
911
912 // Validate our parameters and state
913 SkASSERT(uni <= 0x0000FFFF);
914 SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
915
916 // Get the glyph
917 theChar = (UniChar) uni;
918
919 if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1)) {
920 cgGlyph = 0;
921 }
922
923 return cgGlyph;
924}
925
926void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
927 this->generateMetrics(glyph);
928}
929
930void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
931 CGSize advance;
932 CGRect bounds;
933 CGGlyph cgGlyph;
934
935 // Get the state we need
936 cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
937
938 if (fVertical) {
939 if (!isSnowLeopard()) {
940 // Lion and Leopard respect the vertical font metrics.
941 CTFontGetBoundingRectsForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
942 &cgGlyph, &bounds, 1);
943 } else {
944 // Snow Leopard and earlier respect the vertical font metrics for
945 // advances, but not bounds, so use the default box and adjust it below.
946 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
947 &cgGlyph, &bounds, 1);
948 }
949 CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
950 &cgGlyph, &advance, 1);
951 } else {
952 CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
953 &cgGlyph, &bounds, 1);
954 CTFontGetAdvancesForGlyphs(fCTFont, kCTFontDefaultOrientation,
955 &cgGlyph, &advance, 1);
956 }
957
958 // BUG?
959 // 0x200B (zero-advance space) seems to return a huge (garbage) bounds, when
960 // it should be empty. So, if we see a zero-advance, we check if it has an
961 // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
962 // is rare, so we won't incur a big performance cost for this extra check.
963 if (0 == advance.width && 0 == advance.height) {
964 AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
965 if (NULL == path || CGPathIsEmpty(path)) {
966 bounds = CGRectMake(0, 0, 0, 0);
967 }
968 }
969
970 glyph->zeroMetrics();
971 glyph->fAdvanceX = SkFloatToFixed_Check(advance.width);
972 glyph->fAdvanceY = -SkFloatToFixed_Check(advance.height);
973
974 if (CGRectIsEmpty_inline(bounds)) {
975 return;
976 }
977
978 if (isLeopard() && !fVertical) {
979 // Leopard does not consider the matrix skew in its bounds.
980 // Run the bounding rectangle through the skew matrix to determine
981 // the true bounds. However, this doesn't work if the font is vertical.
982 // FIXME (Leopard): If the font has synthetic italic (e.g., matrix skew)
983 // and the font is vertical, the bounds need to be recomputed.
984 SkRect glyphBounds = SkRect::MakeXYWH(
985 bounds.origin.x, bounds.origin.y,
986 bounds.size.width, bounds.size.height);
987 fUnitMatrix.mapRect(&glyphBounds);
988 bounds.origin.x = glyphBounds.fLeft;
989 bounds.origin.y = glyphBounds.fTop;
990 bounds.size.width = glyphBounds.width();
991 bounds.size.height = glyphBounds.height();
992 }
993 // Adjust the bounds
994 //
995 // CTFontGetBoundingRectsForGlyphs ignores the font transform, so we need
996 // to transform the bounding box ourselves.
997 //
998 // The bounds are also expanded by 1 pixel, to give CG room for anti-aliasing.
999 CGRectInset_inline(&bounds, -1, -1);
1000
1001 // Get the metrics
1002 bool lionAdjustedMetrics = false;
1003 if (isLion() || isMountainLion()) {
1004 if (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()){
1005 lionAdjustedMetrics = true;
1006 SkRect adjust;
1007 const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
1008 adjust.set(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
1009 fFBoundingBoxesMatrix.mapRect(&adjust);
1010 bounds.origin.x = SkScalarToFloat(adjust.fLeft) - 1;
1011 bounds.origin.y = SkScalarToFloat(adjust.fTop) - 1;
1012 }
1013 // Lion returns fractions in the bounds
1014 glyph->fWidth = SkToU16(sk_float_ceil2int(bounds.size.width));
1015 glyph->fHeight = SkToU16(sk_float_ceil2int(bounds.size.height));
1016 } else {
1017 glyph->fWidth = SkToU16(sk_float_round2int(bounds.size.width));
1018 glyph->fHeight = SkToU16(sk_float_round2int(bounds.size.height));
1019 }
1020 glyph->fTop = SkToS16(-sk_float_round2int(CGRectGetMaxY_inline(bounds)));
1021 glyph->fLeft = SkToS16(sk_float_round2int(CGRectGetMinX_inline(bounds)));
1022 SkIPoint offset;
1023 if (fVertical && (isSnowLeopard() || lionAdjustedMetrics)) {
1024 // SnowLeopard doesn't respect vertical metrics, so compute them manually.
1025 // Also compute them for Lion when the metrics were computed by hand.
1026 getVerticalOffset(cgGlyph, &offset);
1027 glyph->fLeft += offset.fX;
1028 glyph->fTop += offset.fY;
1029 }
1030}
1031
1032#include "SkColorPriv.h"
1033
1034static void build_power_table(uint8_t table[], float ee) {
1035 for (int i = 0; i < 256; i++) {
1036 float x = i / 255.f;
1037 x = sk_float_pow(x, ee);
1038 int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
1039 table[i] = SkToU8(xx);
1040 }
1041}
1042
1043/**
1044 * This will invert the gamma applied by CoreGraphics, so we can get linear
1045 * values.
1046 *
1047 * CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
1048 * The color space used does not appear to affect this choice.
1049 */
1050static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
1051 static bool gInited;
1052 static uint8_t gTableCoreGraphicsSmoothing[256];
1053 if (!gInited) {
1054 build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
1055 gInited = true;
1056 }
1057 return gTableCoreGraphicsSmoothing;
1058}
1059
1060static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
1061 while (count > 0) {
1062 uint8_t mask = 0;
1063 for (int i = 7; i >= 0; --i) {
1064 mask |= (CGRGBPixel_getAlpha(*src++) >> 7) << i;
1065 if (0 == --count) {
1066 break;
1067 }
1068 }
1069 *dst++ = mask;
1070 }
1071}
1072
1073template<bool APPLY_PREBLEND>
1074static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
1075 U8CPU r = (rgb >> 16) & 0xFF;
1076 U8CPU g = (rgb >> 8) & 0xFF;
1077 U8CPU b = (rgb >> 0) & 0xFF;
1078 return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
1079}
1080template<bool APPLY_PREBLEND>
1081static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
1082 const SkGlyph& glyph, const uint8_t* table8) {
1083 const int width = glyph.fWidth;
1084 size_t dstRB = glyph.rowBytes();
1085 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
1086
1087 for (int y = 0; y < glyph.fHeight; y++) {
1088 for (int i = 0; i < width; ++i) {
1089 dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
1090 }
1091 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1092 dst += dstRB;
1093 }
1094}
1095
1096template<bool APPLY_PREBLEND>
1097static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
1098 const uint8_t* tableG,
1099 const uint8_t* tableB) {
1100 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1101 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1102 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1103 return SkPack888ToRGB16(r, g, b);
1104}
1105template<bool APPLY_PREBLEND>
1106static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1107 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1108 const int width = glyph.fWidth;
1109 size_t dstRB = glyph.rowBytes();
1110 uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
1111
1112 for (int y = 0; y < glyph.fHeight; y++) {
1113 for (int i = 0; i < width; i++) {
1114 dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1115 }
1116 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1117 dst = (uint16_t*)((char*)dst + dstRB);
1118 }
1119}
1120
1121template<bool APPLY_PREBLEND>
1122static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
1123 const uint8_t* tableG,
1124 const uint8_t* tableB) {
1125 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
1126 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 8) & 0xFF, tableG);
1127 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 0) & 0xFF, tableB);
1128 return SkPackARGB32(0xFF, r, g, b);
1129}
1130template<bool APPLY_PREBLEND>
1131static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
1132 const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
1133 const int width = glyph.fWidth;
1134 size_t dstRB = glyph.rowBytes();
1135 uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
1136 for (int y = 0; y < glyph.fHeight; y++) {
1137 for (int i = 0; i < width; i++) {
1138 dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
1139 }
1140 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1141 dst = (uint32_t*)((char*)dst + dstRB);
1142 }
1143}
1144
1145template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
1146 return (T*)((char*)ptr + byteOffset);
1147}
1148
1149void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
1150 CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
1151
1152 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1153 bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
1154
1155 // Draw the glyph
1156 size_t cgRowBytes;
1157 CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
1158 if (cgPixels == NULL) {
1159 return;
1160 }
1161
1162 //TODO: see if drawing black on white and inverting is faster (at least in
1163 //lcd case) as core graphics appears to have special case code for drawing
1164 //black text.
1165
1166 // Fix the glyph
1167 const bool isLCD = isLCDFormat(glyph.fMaskFormat);
1168 if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
1169 const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
1170
1171 //Note that the following cannot really be integrated into the
1172 //pre-blend, since we may not be applying the pre-blend; when we aren't
1173 //applying the pre-blend it means that a filter wants linear anyway.
1174 //Other code may also be applying the pre-blend, so we'd need another
1175 //one with this and one without.
1176 CGRGBPixel* addr = cgPixels;
1177 for (int y = 0; y < glyph.fHeight; ++y) {
1178 for (int x = 0; x < glyph.fWidth; ++x) {
1179 int r = (addr[x] >> 16) & 0xFF;
1180 int g = (addr[x] >> 8) & 0xFF;
1181 int b = (addr[x] >> 0) & 0xFF;
1182 addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
1183 }
1184 addr = SkTAddByteOffset(addr, cgRowBytes);
1185 }
1186 }
1187
1188 // Convert glyph to mask
1189 switch (glyph.fMaskFormat) {
1190 case SkMask::kLCD32_Format: {
1191 if (fPreBlend.isApplicable()) {
1192 rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
1193 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1194 } else {
1195 rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
1196 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1197 }
1198 } break;
1199 case SkMask::kLCD16_Format: {
1200 if (fPreBlend.isApplicable()) {
1201 rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
1202 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1203 } else {
1204 rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
1205 fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
1206 }
1207 } break;
1208 case SkMask::kA8_Format: {
1209 if (fPreBlend.isApplicable()) {
1210 rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1211 } else {
1212 rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
1213 }
1214 } break;
1215 case SkMask::kBW_Format: {
1216 const int width = glyph.fWidth;
1217 size_t dstRB = glyph.rowBytes();
1218 uint8_t* dst = (uint8_t*)glyph.fImage;
1219 for (int y = 0; y < glyph.fHeight; y++) {
1220 cgpixels_to_bits(dst, cgPixels, width);
1221 cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
1222 dst += dstRB;
1223 }
1224 } break;
1225 default:
1226 SkDEBUGFAIL("unexpected mask format");
1227 break;
1228 }
1229}
1230
1231/*
1232 * Our subpixel resolution is only 2 bits in each direction, so a scale of 4
1233 * seems sufficient, and possibly even correct, to allow the hinted outline
1234 * to be subpixel positioned.
1235 */
1236#define kScaleForSubPixelPositionHinting (4.0f)
1237
1238void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
1239 CTFontRef font = fCTFont;
1240 SkScalar scaleX = SK_Scalar1;
1241 SkScalar scaleY = SK_Scalar1;
1242
1243 /*
1244 * For subpixel positioning, we want to return an unhinted outline, so it
1245 * can be positioned nicely at fractional offsets. However, we special-case
1246 * if the baseline of the (horizontal) text is axis-aligned. In those cases
1247 * we want to retain hinting in the direction orthogonal to the baseline.
1248 * e.g. for horizontal baseline, we want to retain hinting in Y.
1249 * The way we remove hinting is to scale the font by some value (4) in that
1250 * direction, ask for the path, and then scale the path back down.
1251 */
1252 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1253 SkMatrix m;
1254 fRec.getSingleMatrix(&m);
1255
1256 // start out by assuming that we want no hining in X and Y
1257 scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
1258 // now see if we need to restore hinting for axis-aligned baselines
1259 switch (SkComputeAxisAlignmentForHText(m)) {
1260 case kX_SkAxisAlignment:
1261 scaleY = SK_Scalar1; // want hinting in the Y direction
1262 break;
1263 case kY_SkAxisAlignment:
1264 scaleX = SK_Scalar1; // want hinting in the X direction
1265 break;
1266 default:
1267 break;
1268 }
1269
1270 CGAffineTransform xform = MatrixToCGAffineTransform(m, scaleX, scaleY);
1271 // need to release font when we're done
1272 font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
1273 }
1274
1275 CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
1276 AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
1277
1278 path->reset();
1279 if (cgPath != NULL) {
1280 CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
1281 }
1282
1283 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
1284 SkMatrix m;
1285 m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
1286 path->transform(m);
1287 // balance the call to CTFontCreateCopyWithAttributes
1288 CFSafeRelease(font);
1289 }
1290 if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
1291 SkIPoint offset;
1292 getVerticalOffset(cgGlyph, &offset);
1293 path->offset(SkIntToScalar(offset.fX), SkIntToScalar(offset.fY));
1294 }
1295}
1296
1297void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx,
1298 SkPaint::FontMetrics* my) {
1299 CGRect theBounds = CTFontGetBoundingBox(fCTFont);
1300
1301 SkPaint::FontMetrics theMetrics;
1302 theMetrics.fTop = CGToScalar(-CGRectGetMaxY_inline(theBounds));
1303 theMetrics.fAscent = CGToScalar(-CTFontGetAscent(fCTFont));
1304 theMetrics.fDescent = CGToScalar( CTFontGetDescent(fCTFont));
1305 theMetrics.fBottom = CGToScalar(-CGRectGetMinY_inline(theBounds));
1306 theMetrics.fLeading = CGToScalar( CTFontGetLeading(fCTFont));
1307 theMetrics.fAvgCharWidth = CGToScalar( CGRectGetWidth_inline(theBounds));
1308 theMetrics.fXMin = CGToScalar( CGRectGetMinX_inline(theBounds));
1309 theMetrics.fXMax = CGToScalar( CGRectGetMaxX_inline(theBounds));
1310 theMetrics.fXHeight = CGToScalar( CTFontGetXHeight(fCTFont));
1311
1312 if (mx != NULL) {
1313 *mx = theMetrics;
1314 }
1315 if (my != NULL) {
1316 *my = theMetrics;
1317 }
1318}
1319
1320void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
1321 SkPath* skPath = (SkPath*)info;
1322
1323 // Process the path element
1324 switch (element->type) {
1325 case kCGPathElementMoveToPoint:
1326 skPath->moveTo(element->points[0].x, -element->points[0].y);
1327 break;
1328
1329 case kCGPathElementAddLineToPoint:
1330 skPath->lineTo(element->points[0].x, -element->points[0].y);
1331 break;
1332
1333 case kCGPathElementAddQuadCurveToPoint:
1334 skPath->quadTo(element->points[0].x, -element->points[0].y,
1335 element->points[1].x, -element->points[1].y);
1336 break;
1337
1338 case kCGPathElementAddCurveToPoint:
1339 skPath->cubicTo(element->points[0].x, -element->points[0].y,
1340 element->points[1].x, -element->points[1].y,
1341 element->points[2].x, -element->points[2].y);
1342 break;
1343
1344 case kCGPathElementCloseSubpath:
1345 skPath->close();
1346 break;
1347
1348 default:
1349 SkDEBUGFAIL("Unknown path element!");
1350 break;
1351 }
1352}
1353
1354
1355///////////////////////////////////////////////////////////////////////////////
1356
1357// Returns NULL on failure
1358// Call must still manage its ownership of provider
1359static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
1360 AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
1361 if (NULL == cg) {
1362 return NULL;
1363 }
1364 CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
1365 return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
1366}
1367
1368SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
1369 AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
1370 if (NULL == provider) {
1371 return NULL;
1372 }
1373 return create_from_dataProvider(provider);
1374}
1375
1376SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
1377 AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
1378 if (NULL == provider) {
1379 return NULL;
1380 }
1381 return create_from_dataProvider(provider);
1382}
1383
1384// Web fonts added to the the CTFont registry do not return their character set.
1385// Iterate through the font in this case. The existing caller caches the result,
1386// so the performance impact isn't too bad.
1387static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
1388 SkTDArray<SkUnichar>* glyphToUnicode) {
1389 glyphToUnicode->setCount(glyphCount);
1390 SkUnichar* out = glyphToUnicode->begin();
1391 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1392 UniChar unichar = 0;
1393 while (glyphCount > 0) {
1394 CGGlyph glyph;
1395 if (CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1396 out[glyph] = unichar;
1397 --glyphCount;
1398 }
1399 if (++unichar == 0) {
1400 break;
1401 }
1402 }
1403}
1404
1405// Construct Glyph to Unicode table.
1406// Unicode code points that require conjugate pairs in utf16 are not
1407// supported.
1408static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
1409 SkTDArray<SkUnichar>* glyphToUnicode) {
1410 AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
1411 if (!charSet) {
1412 populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
1413 return;
1414 }
1415
1416 AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
1417 charSet));
1418 if (!bitmap) {
1419 return;
1420 }
1421 CFIndex length = CFDataGetLength(bitmap);
1422 if (!length) {
1423 return;
1424 }
1425 if (length > 8192) {
1426 // TODO: Add support for Unicode above 0xFFFF
1427 // Consider only the BMP portion of the Unicode character points.
1428 // The bitmap may contain other planes, up to plane 16.
1429 // See http://developer.apple.com/library/ios/#documentation/CoreFoundation/Reference/CFCharacterSetRef/Reference/reference.html
1430 length = 8192;
1431 }
1432 const UInt8* bits = CFDataGetBytePtr(bitmap);
1433 glyphToUnicode->setCount(glyphCount);
1434 SkUnichar* out = glyphToUnicode->begin();
1435 sk_bzero(out, glyphCount * sizeof(SkUnichar));
1436 for (int i = 0; i < length; i++) {
1437 int mask = bits[i];
1438 if (!mask) {
1439 continue;
1440 }
1441 for (int j = 0; j < 8; j++) {
1442 CGGlyph glyph;
1443 UniChar unichar = static_cast<UniChar>((i << 3) + j);
1444 if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
1445 out[glyph] = unichar;
1446 }
1447 }
1448 }
1449}
1450
1451static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
1452 CGSize advance;
1453 advance.width = 0;
1454 CGGlyph glyph = gId;
1455 CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
1456 *data = sk_float_round2int(advance.width);
1457 return true;
1458}
1459
1460// we might move this into our CGUtils...
1461static void CFStringToSkString(CFStringRef src, SkString* dst) {
1462 // Reserve enough room for the worst-case string,
1463 // plus 1 byte for the trailing null.
1464 CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
1465 kCFStringEncodingUTF8) + 1;
1466 dst->resize(length);
1467 CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
1468 // Resize to the actual UTF-8 length used, stripping the null character.
1469 dst->resize(strlen(dst->c_str()));
1470}
1471
1472// static
1473SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
1474 uint32_t fontID,
1475 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
1476 const uint32_t* glyphIDs,
1477 uint32_t glyphIDsCount) {
1478 CTFontRef originalCTFont = GetFontRefFromFontID(fontID);
1479 AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
1480 originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
1481 SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
1482
1483 {
1484 AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
1485 CFStringToSkString(fontName, &info->fFontName);
1486 }
1487
1488 info->fMultiMaster = false;
1489 CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
1490 info->fLastGlyphID = SkToU16(glyphCount - 1);
1491 info->fEmSize = CTFontGetUnitsPerEm(ctFont);
1492
1493 if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
1494 populate_glyph_to_unicode(ctFont, glyphCount, &info->fGlyphToUnicode);
1495 }
1496
1497 info->fStyle = 0;
1498
1499 // If it's not a truetype font, mark it as 'other'. Assume that TrueType
1500 // fonts always have both glyf and loca tables. At the least, this is what
1501 // sfntly needs to subset the font. CTFontCopyAttribute() does not always
1502 // succeed in determining this directly.
1503 if (!GetTableSize(fontID, 'glyf') || !GetTableSize(fontID, 'loca')) {
1504 info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
1505 info->fItalicAngle = 0;
1506 info->fAscent = 0;
1507 info->fDescent = 0;
1508 info->fStemV = 0;
1509 info->fCapHeight = 0;
1510 info->fBBox = SkIRect::MakeEmpty();
1511 return info;
1512 }
1513
1514 info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
1515 CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(ctFont);
1516 if (symbolicTraits & kCTFontMonoSpaceTrait) {
1517 info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
1518 }
1519 if (symbolicTraits & kCTFontItalicTrait) {
1520 info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
1521 }
1522 CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
1523 if (stylisticClass & kCTFontSymbolicClass) {
1524 info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
1525 }
1526 if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
1527 info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
1528 } else if (stylisticClass & kCTFontScriptsClass) {
1529 info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
1530 }
1531 info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
1532 info->fAscent = (int16_t) CTFontGetAscent(ctFont);
1533 info->fDescent = (int16_t) CTFontGetDescent(ctFont);
1534 info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
1535 CGRect bbox = CTFontGetBoundingBox(ctFont);
1536
1537 SkRect r;
1538 r.set( CGToScalar(CGRectGetMinX_inline(bbox)), // Left
1539 CGToScalar(CGRectGetMaxY_inline(bbox)), // Top
1540 CGToScalar(CGRectGetMaxX_inline(bbox)), // Right
1541 CGToScalar(CGRectGetMinY_inline(bbox))); // Bottom
1542
1543 r.roundOut(&(info->fBBox));
1544
1545 // Figure out a good guess for StemV - Min width of i, I, !, 1.
1546 // This probably isn't very good with an italic font.
1547 int16_t min_width = SHRT_MAX;
1548 info->fStemV = 0;
1549 static const UniChar stem_chars[] = {'i', 'I', '!', '1'};
1550 const size_t count = sizeof(stem_chars) / sizeof(stem_chars[0]);
1551 CGGlyph glyphs[count];
1552 CGRect boundingRects[count];
1553 if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
1554 CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
1555 glyphs, boundingRects, count);
1556 for (size_t i = 0; i < count; i++) {
1557 int16_t width = (int16_t) boundingRects[i].size.width;
1558 if (width > 0 && width < min_width) {
1559 min_width = width;
1560 info->fStemV = min_width;
1561 }
1562 }
1563 }
1564
1565 if (false) { // TODO: haven't figured out how to know if font is embeddable
1566 // (information is in the OS/2 table)
1567 info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
1568 } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
1569 if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
1570 skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
1571 info->fGlyphWidths->fAdvance.append(1, &min_width);
1572 skia_advanced_typeface_metrics_utils::finishRange(info->fGlyphWidths.get(), 0,
1573 SkAdvancedTypefaceMetrics::WidthRange::kDefault);
1574 } else {
1575 info->fGlyphWidths.reset(
1576 skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
1577 glyphCount,
1578 glyphIDs,
1579 glyphIDsCount,
1580 &getWidthAdvance));
1581 }
1582 }
1583 return info;
1584}
1585
1586///////////////////////////////////////////////////////////////////////////////
1587
1588static SK_SFNT_ULONG get_font_type_tag(SkFontID uniqueID) {
1589 CTFontRef ctFont = GetFontRefFromFontID(uniqueID);
1590 AutoCFRelease<CFNumberRef> fontFormatRef(
1591 static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
1592 if (!fontFormatRef) {
1593 return 0;
1594 }
1595
1596 SInt32 fontFormatValue;
1597 if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
1598 return 0;
1599 }
1600
1601 switch (fontFormatValue) {
1602 case kCTFontFormatOpenTypePostScript:
1603 return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
1604 case kCTFontFormatOpenTypeTrueType:
1605 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1606 case kCTFontFormatTrueType:
1607 return SkSFNTHeader::fontType_MacTrueType::TAG;
1608 case kCTFontFormatPostScript:
1609 return SkSFNTHeader::fontType_PostScript::TAG;
1610 case kCTFontFormatBitmap:
1611 return SkSFNTHeader::fontType_MacTrueType::TAG;
1612 case kCTFontFormatUnrecognized:
1613 default:
1614 //CT seems to be unreliable in being able to obtain the type,
1615 //even if all we want is the first four bytes of the font resource.
1616 //Just the presence of the FontForge 'FFTM' table seems to throw it off.
1617 return SkSFNTHeader::fontType_WindowsTrueType::TAG;
1618 }
1619}
1620
1621SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
1622 SK_SFNT_ULONG fontType = get_font_type_tag(uniqueID);
1623 if (0 == fontType) {
1624 return NULL;
1625 }
1626
1627 // get table tags
1628 int numTables = CountTables(uniqueID);
1629 SkTDArray<SkFontTableTag> tableTags;
1630 tableTags.setCount(numTables);
1631 GetTableTags(uniqueID, tableTags.begin());
1632
1633 // calc total size for font, save sizes
1634 SkTDArray<size_t> tableSizes;
1635 size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1636 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1637 size_t tableSize = GetTableSize(uniqueID, tableTags[tableIndex]);
1638 totalSize += (tableSize + 3) & ~3;
1639 *tableSizes.append() = tableSize;
1640 }
1641
1642 // reserve memory for stream, and zero it (tables must be zero padded)
1643 SkMemoryStream* stream = new SkMemoryStream(totalSize);
1644 char* dataStart = (char*)stream->getMemoryBase();
1645 sk_bzero(dataStart, totalSize);
1646 char* dataPtr = dataStart;
1647
1648 // compute font header entries
1649 uint16_t entrySelector = 0;
1650 uint16_t searchRange = 1;
1651 while (searchRange < numTables >> 1) {
1652 entrySelector++;
1653 searchRange <<= 1;
1654 }
1655 searchRange <<= 4;
1656 uint16_t rangeShift = (numTables << 4) - searchRange;
1657
1658 // write font header
1659 SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
1660 header->fontType = fontType;
1661 header->numTables = SkEndian_SwapBE16(numTables);
1662 header->searchRange = SkEndian_SwapBE16(searchRange);
1663 header->entrySelector = SkEndian_SwapBE16(entrySelector);
1664 header->rangeShift = SkEndian_SwapBE16(rangeShift);
1665 dataPtr += sizeof(SkSFNTHeader);
1666
1667 // write tables
1668 SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
1669 dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
1670 for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
1671 size_t tableSize = tableSizes[tableIndex];
1672 GetTableData(uniqueID, tableTags[tableIndex], 0, tableSize, dataPtr);
1673 entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
1674 entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
1675 tableSize));
1676 entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
1677 entry->logicalLength = SkEndian_SwapBE32(tableSize);
1678
1679 dataPtr += (tableSize + 3) & ~3;
1680 ++entry;
1681 }
1682
1683 return stream;
1684}
1685
1686size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) {
1687 SkDEBUGFAIL("SkFontHost::GetFileName unimplemented");
1688 return 0;
1689}
1690
1691///////////////////////////////////////////////////////////////////////////////
1692
1693#include "SkStream.h"
1694
1695void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
1696 SkFontDescriptor desc;
1697 face->onGetFontDescriptor(&desc);
1698
1699 desc.serialize(stream);
1700
1701 // by convention, we also write out the actual sfnt data, preceeded by
1702 // a packed-length. For now we skip that, so we just write the zero.
1703 stream->writePackedUInt(0);
1704}
1705
1706SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
1707 SkFontDescriptor desc(stream);
1708
1709 // by convention, Serialize will have also written the actual sfnt data.
1710 // for now, we just want to skip it.
1711 size_t size = stream->readPackedUInt();
1712 stream->skip(size);
1713
1714 return SkFontHost::CreateTypeface(NULL, desc.getFamilyName(), desc.getStyle());
1715}
1716
1717///////////////////////////////////////////////////////////////////////////////
1718
1719// DEPRECATED
1720SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
1721 return new SkScalerContext_Mac(desc);
1722}
1723
1724// DEPRECATED
1725SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
1726 SkFontID nextFontID = 0;
1727 SkTypeface* face = GetDefaultFace();
1728 if (face->uniqueID() != currFontID) {
1729 nextFontID = face->uniqueID();
1730 }
1731 return nextFontID;
1732}
1733
1734// DEPRECATED
1735void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface* face) {
1736 face->onFilterRec(rec);
1737}
1738
1739// DEPRECATED
1740int SkFontHost::CountTables(SkFontID fontID) {
1741 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
1742 return face ? face->onGetTableTags(NULL) : 0;
1743}
1744
1745// DEPRECATED
1746int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
1747 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
1748 return face ? face->onGetTableTags(tags) : 0;
1749}
1750
1751// DEPRECATED
1752size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
1753 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
1754 return face ? face->onGetTableData(tag, 0, ~0U, NULL) : 0;
1755}
1756
1757// DEPRECATED
1758size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
1759 size_t offset, size_t length, void* dst) {
1760 SkTypeface* face = SkTypefaceCache::FindByID(fontID);
1761 return face ? face->onGetTableData(tag, offset, length, dst) : 0;
1762}
1763
1764///////////////////////////////////////////////////////////////////////////////
1765///////////////////////////////////////////////////////////////////////////////
1766
1767int SkTypeface_Mac::onGetUPEM() const {
1768 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
1769 return CGFontGetUnitsPerEm(cgFont);
1770}
1771
1772// If, as is the case with web fonts, the CTFont data isn't available,
1773// the CGFont data may work. While the CGFont may always provide the
1774// right result, leave the CTFont code path to minimize disruption.
1775static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
1776 CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
1777 kCTFontTableOptionNoOptions);
1778 if (NULL == data) {
1779 AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
1780 data = CGFontCopyTableForTag(cgFont, tag);
1781 }
1782 return data;
1783}
1784
1785int SkTypeface_Mac::onGetTableTags(SkFontTableTag tags[]) const {
1786 AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(fFontRef,
1787 kCTFontTableOptionNoOptions));
1788 if (NULL == cfArray) {
1789 return 0;
1790 }
1791 int count = CFArrayGetCount(cfArray);
1792 if (tags) {
1793 for (int i = 0; i < count; ++i) {
1794 uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
1795 tags[i] = static_cast<SkFontTableTag>(fontTag);
1796 }
1797 }
1798 return count;
1799}
1800
1801size_t SkTypeface_Mac::onGetTableData(SkFontTableTag tag, size_t offset,
1802 size_t length, void* dstData) const {
1803 AutoCFRelease<CFDataRef> srcData(copyTableFromFont(fFontRef, tag));
1804 if (NULL == srcData) {
1805 return 0;
1806 }
1807
1808 size_t srcSize = CFDataGetLength(srcData);
1809 if (offset >= srcSize) {
1810 return 0;
1811 }
1812 if (length > srcSize - offset) {
1813 length = srcSize - offset;
1814 }
1815 if (dstData) {
1816 memcpy(dstData, CFDataGetBytePtr(srcData) + offset, length);
1817 }
1818 return length;
1819}
1820
1821SkScalerContext* SkTypeface_Mac::onCreateScalerContext(const SkDescriptor* desc) const {
1822 return new SkScalerContext_Mac(desc);
1823}
1824
1825void SkTypeface_Mac::onFilterRec(SkScalerContextRec* rec) const {
1826 unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
1827 SkScalerContext::kAutohinting_Flag;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001828
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001829 rec->fFlags &= ~flagsWeDontSupport;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001830
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001831 bool lcdSupport = supports_LCD();
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001832
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001833 // Only two levels of hinting are supported.
1834 // kNo_Hinting means avoid CoreGraphics outline dilation.
1835 // kNormal_Hinting means CoreGraphics outline dilation is allowed.
1836 // If there is no lcd support, hinting (dilation) cannot be supported.
1837 SkPaint::Hinting hinting = rec->getHinting();
1838 if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
1839 hinting = SkPaint::kNo_Hinting;
1840 } else if (SkPaint::kFull_Hinting == hinting) {
1841 hinting = SkPaint::kNormal_Hinting;
1842 }
1843 rec->setHinting(hinting);
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001844
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001845 // FIXME: lcd smoothed un-hinted rasterization unsupported.
1846 // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
1847 // There is no current means to honor a request for unhinted lcd,
1848 // so arbitrarilly ignore the hinting request and honor lcd.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001849
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001850 // Hinting and smoothing should be orthogonal, but currently they are not.
1851 // CoreGraphics has no API to influence hinting. However, its lcd smoothed
1852 // output is drawn from auto-dilated outlines (the amount of which is
1853 // determined by AppleFontSmoothing). Its regular anti-aliased output is
1854 // drawn from un-dilated outlines.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001855
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001856 // The behavior of Skia is as follows:
1857 // [AA][no-hint]: generate AA using CoreGraphic's AA output.
1858 // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
1859 // channel. This matches [LCD][yes-hint] in weight.
1860 // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
1861 // Currenly side with LCD, effectively ignoring the hinting setting.
1862 // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001863
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001864 if (isLCDFormat(rec->fMaskFormat)) {
1865 if (lcdSupport) {
1866 //CoreGraphics creates 555 masks for smoothed text anyway.
1867 rec->fMaskFormat = SkMask::kLCD16_Format;
1868 rec->setHinting(SkPaint::kNormal_Hinting);
1869 } else {
1870 rec->fMaskFormat = SkMask::kA8_Format;
1871 }
1872 }
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001873
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001874 // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
1875 // All other masks can use regular gamma.
1876 if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
1877#ifndef SK_GAMMA_APPLY_TO_A8
1878 rec->ignorePreBlend();
reed@android.comfeda2f92010-05-19 13:47:05 +00001879#endif
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001880 } else {
1881 //CoreGraphics dialates smoothed text as needed.
1882 rec->setContrast(0);
1883 }
1884}
1885
1886// we take ownership of the ref
1887static const char* get_str(CFStringRef ref, SkString* str) {
1888 CFStringToSkString(ref, str);
1889 CFSafeRelease(ref);
1890 return str->c_str();
1891}
1892
1893void SkTypeface_Mac::onGetFontDescriptor(SkFontDescriptor* desc) const {
1894 this->INHERITED::onGetFontDescriptor(desc);
1895 SkString tmpStr;
skia.committer@gmail.com0c23faf2013-03-03 07:16:29 +00001896
mike@reedtribe.orgb103ed42013-03-03 03:50:09 +00001897 desc->setFamilyName(get_str(CTFontCopyFamilyName(fFontRef), &tmpStr));
1898 desc->setFullName(get_str(CTFontCopyFullName(fFontRef), &tmpStr));
1899 desc->setPostscriptName(get_str(CTFontCopyPostScriptName(fFontRef), &tmpStr));
1900}