gfx/skia/trunk/src/ports/SkFontHost_mac.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rwxr-xr-x

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial