1/* 2 ** Copyright 2006, The Android Open Source Project 3 ** 4 ** Licensed under the Apache License, Version 2.0 (the "License"); 5 ** you may not use this file except in compliance with the License. 6 ** You may obtain a copy of the License at 7 ** 8 ** http://www.apache.org/licenses/LICENSE-2.0 9 ** 10 ** Unless required by applicable law or agreed to in writing, software 11 ** distributed under the License is distributed on an "AS IS" BASIS, 12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 ** See the License for the specific language governing permissions and 14 ** limitations under the License. 15*/ 16 17#include <Carbon/Carbon.h> 18#include "SkFontHost.h" 19#include "SkDescriptor.h" 20#include "SkEndian.h" 21#include "SkFloatingPoint.h" 22#include "SkPaint.h" 23#include "SkPoint.h" 24 25// Give 1MB font cache budget 26#define FONT_CACHE_MEMORY_BUDGET (1024 * 1024) 27 28const char* gDefaultfont = "Arial"; // hard code for now 29static SkMutex gFTMutex; 30 31static inline SkPoint F32PtToSkPoint(const Float32Point p) { 32 SkPoint sp = { SkFloatToScalar(p.x), SkFloatToScalar(p.y) }; 33 return sp; 34} 35 36static inline uint32_t _rotl(uint32_t v, uint32_t r) { 37 return (v << r | v >> (32 - r)); 38} 39 40class SkTypeface_Mac : public SkTypeface { 41public: 42 SkTypeface_Mac(SkTypeface::Style style, uint32_t id) 43 : SkTypeface(style, id) {} 44}; 45 46#pragma mark - 47 48static uint32_t find_from_name(const char name[]) { 49 CFStringRef str = CFStringCreateWithCString(NULL, name, 50 kCFStringEncodingUTF8); 51 uint32_t fontID = ::ATSFontFindFromName(str, kATSOptionFlagsDefault); 52 CFRelease(str); 53 return fontID; 54} 55 56static uint32_t find_default_fontID() { 57 static const char* gDefaultNames[] = { "Arial", "Tahoma", "Helvetica" }; 58 59 uint32_t fontID; 60 for (size_t i = 0; i < SK_ARRAY_COUNT(gDefaultNames); i++) { 61 fontID = find_from_name(gDefaultNames[i]); 62 if (fontID) { 63 return fontID; 64 } 65 } 66 sk_throw(); 67 return 0; 68} 69 70static SkTypeface* CreateTypeface_(const char name[], SkTypeface::Style style) { 71 uint32_t fontID = 0; 72 if (NULL != name) { 73 fontID = find_from_name(name); 74 } 75 if (0 == fontID) { 76 fontID = find_default_fontID(); 77 } 78 // we lie (for now) and report that we found the exact style bits 79 return new SkTypeface_Mac(style, fontID); 80} 81 82#pragma mark - 83 84class SkScalerContext_Mac : public SkScalerContext { 85public: 86 SkScalerContext_Mac(const SkDescriptor* desc); 87 virtual ~SkScalerContext_Mac(); 88 89protected: 90 virtual unsigned generateGlyphCount(); 91 virtual uint16_t generateCharToGlyph(SkUnichar uni); 92 virtual void generateAdvance(SkGlyph* glyph); 93 virtual void generateMetrics(SkGlyph* glyph); 94 virtual void generateImage(const SkGlyph& glyph); 95 virtual void generatePath(const SkGlyph& glyph, SkPath* path); 96 virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY); 97 98private: 99 ATSUTextLayout fLayout; 100 ATSUStyle fStyle; 101 CGColorSpaceRef fGrayColorSpace; 102 CGAffineTransform fTransform; 103 104 static OSStatus MoveTo(const Float32Point *pt, void *cb); 105 static OSStatus Line(const Float32Point *pt, void *cb); 106 static OSStatus Curve(const Float32Point *pt1, const Float32Point *pt2, const Float32Point *pt3, void *cb); 107 static OSStatus Close(void *cb); 108}; 109 110void SkFontHost::FilterRec(SkScalerContext::Rec* rec) { 111 // we only support 2 levels of hinting 112 SkPaint::Hinting h = rec->getHinting(); 113 if (SkPaint::kSlight_Hinting == h) { 114 h = SkPaint::kNo_Hinting; 115 } else if (SkPaint::kFull_Hinting == h) { 116 h = SkPaint::kNormal_Hinting; 117 } 118 rec->setHinting(h); 119 120 // we don't support LCD text 121 if (SkMask::FormatIsLCD((SkMask::Format)rec->fMaskFormat)) { 122 rec->fMaskFormat = SkMask::kA8_Format; 123 } 124} 125 126SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc) 127 : SkScalerContext(desc), fLayout(0), fStyle(0) 128{ 129 SkAutoMutexAcquire ac(gFTMutex); 130 OSStatus err; 131 132 err = ::ATSUCreateStyle(&fStyle); 133 SkASSERT(0 == err); 134 135 SkMatrix m; 136 fRec.getSingleMatrix(&m); 137 138 fTransform = CGAffineTransformMake(SkScalarToFloat(m[SkMatrix::kMScaleX]), 139 SkScalarToFloat(m[SkMatrix::kMSkewX]), 140 SkScalarToFloat(m[SkMatrix::kMSkewY]), 141 SkScalarToFloat(m[SkMatrix::kMScaleY]), 142 SkScalarToFloat(m[SkMatrix::kMTransX]), 143 SkScalarToFloat(m[SkMatrix::kMTransY])); 144 145 ATSStyleRenderingOptions renderOpts = kATSStyleApplyAntiAliasing; 146 switch (fRec.getHinting()) { 147 case SkPaint::kNo_Hinting: 148 case SkPaint::kSlight_Hinting: 149 renderOpts |= kATSStyleNoHinting; 150 break; 151 case SkPaint::kNormal_Hinting: 152 case SkPaint::kFull_Hinting: 153 renderOpts |= kATSStyleApplyHints; 154 break; 155 } 156 157 ATSUFontID fontID = FMGetFontFromATSFontRef(fRec.fFontID); 158 // we put everything in the matrix, so our pt size is just 1.0 159 Fixed fixedSize = SK_Fixed1; 160 static const ATSUAttributeTag tags[] = { 161 kATSUFontTag, kATSUSizeTag, kATSUFontMatrixTag, kATSUStyleRenderingOptionsTag 162 }; 163 static const ByteCount sizes[] = { 164 sizeof(fontID), sizeof(fixedSize), sizeof(fTransform), sizeof(renderOpts) 165 }; 166 const ATSUAttributeValuePtr values[] = { 167 &fontID, &fixedSize, &fTransform, &renderOpts 168 }; 169 err = ::ATSUSetAttributes(fStyle, SK_ARRAY_COUNT(tags), 170 tags, sizes, values); 171 SkASSERT(0 == err); 172 173 err = ::ATSUCreateTextLayout(&fLayout); 174 SkASSERT(0 == err); 175 176 fGrayColorSpace = ::CGColorSpaceCreateDeviceGray(); 177} 178 179SkScalerContext_Mac::~SkScalerContext_Mac() { 180 ::CGColorSpaceRelease(fGrayColorSpace); 181 ::ATSUDisposeTextLayout(fLayout); 182 ::ATSUDisposeStyle(fStyle); 183} 184 185// man, we need to consider caching this, since it is just dependent on 186// fFontID, and not on any of the other settings like matrix or flags 187unsigned SkScalerContext_Mac::generateGlyphCount() { 188 // The 'maxp' table stores the number of glyphs a offset 4, in 2 bytes 189 uint16_t numGlyphs; 190 if (SkFontHost::GetTableData(fRec.fFontID, 191 SkSetFourByteTag('m', 'a', 'x', 'p'), 192 4, 2, &numGlyphs) != 2) { 193 return 0xFFFF; 194 } 195 return SkEndian_SwapBE16(numGlyphs); 196} 197 198uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) 199{ 200 SkAutoMutexAcquire ac(gFTMutex); 201 202 OSStatus err; 203 UniChar achar = uni; 204 err = ::ATSUSetTextPointerLocation(fLayout,&achar,0,1,1); 205 err = ::ATSUSetRunStyle(fLayout,fStyle,kATSUFromTextBeginning,kATSUToTextEnd); 206 207 ATSLayoutRecord *layoutPtr; 208 ItemCount count; 209 ATSGlyphRef glyph; 210 211 err = ::ATSUDirectGetLayoutDataArrayPtrFromTextLayout(fLayout,0,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr,&count); 212 glyph = layoutPtr->glyphID; 213 ::ATSUDirectReleaseLayoutDataArrayPtr(NULL,kATSUDirectDataLayoutRecordATSLayoutRecordCurrent,(void**)&layoutPtr); 214 return glyph; 215} 216 217static void set_glyph_metrics_on_error(SkGlyph* glyph) { 218 glyph->fRsbDelta = 0; 219 glyph->fLsbDelta = 0; 220 glyph->fWidth = 0; 221 glyph->fHeight = 0; 222 glyph->fTop = 0; 223 glyph->fLeft = 0; 224 glyph->fAdvanceX = 0; 225 glyph->fAdvanceY = 0; 226} 227 228void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) { 229 this->generateMetrics(glyph); 230} 231 232void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) { 233 GlyphID glyphID = glyph->getGlyphID(fBaseGlyphCount); 234 ATSGlyphScreenMetrics screenMetrics; 235 ATSGlyphIdealMetrics idealMetrics; 236 237 OSStatus err = ATSUGlyphGetScreenMetrics(fStyle, 1, &glyphID, 0, true, true, 238 &screenMetrics); 239 if (noErr != err) { 240 set_glyph_metrics_on_error(glyph); 241 return; 242 } 243 err = ATSUGlyphGetIdealMetrics(fStyle, 1, &glyphID, 0, &idealMetrics); 244 if (noErr != err) { 245 set_glyph_metrics_on_error(glyph); 246 return; 247 } 248 249 if ((fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) == 0) { 250 glyph->fAdvanceX = SkFloatToFixed(screenMetrics.deviceAdvance.x); 251 glyph->fAdvanceY = -SkFloatToFixed(screenMetrics.deviceAdvance.y); 252 } else { 253 glyph->fAdvanceX = SkFloatToFixed(idealMetrics.advance.x); 254 glyph->fAdvanceY = -SkFloatToFixed(idealMetrics.advance.y); 255 } 256 257 // specify an extra 1-pixel border, go tive CG room for its antialiasing 258 // i.e. without this, I was seeing some edges chopped off! 259 glyph->fWidth = screenMetrics.width + 2; 260 glyph->fHeight = screenMetrics.height + 2; 261 glyph->fLeft = sk_float_round2int(screenMetrics.topLeft.x) - 1; 262 glyph->fTop = -sk_float_round2int(screenMetrics.topLeft.y) - 1; 263} 264 265void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) 266{ 267 SkAutoMutexAcquire ac(gFTMutex); 268 SkASSERT(fLayout); 269 270 sk_bzero(glyph.fImage, glyph.fHeight * glyph.rowBytes()); 271 CGContextRef contextRef = ::CGBitmapContextCreate(glyph.fImage, 272 glyph.fWidth, glyph.fHeight, 8, 273 glyph.rowBytes(), fGrayColorSpace, 274 kCGImageAlphaNone); 275 if (!contextRef) { 276 SkASSERT(false); 277 return; 278 } 279 280 ::CGContextSetGrayFillColor(contextRef, 1.0, 1.0); 281 ::CGContextSetTextDrawingMode(contextRef, kCGTextFill); 282 283 CGGlyph glyphID = glyph.getGlyphID(fBaseGlyphCount); 284 CGFontRef fontRef = CGFontCreateWithPlatformFont(&fRec.fFontID); 285 CGContextSetFont(contextRef, fontRef); 286 CGContextSetFontSize(contextRef, 1); 287 CGContextSetTextMatrix(contextRef, fTransform); 288 CGContextShowGlyphsAtPoint(contextRef, -glyph.fLeft, 289 glyph.fTop + glyph.fHeight, &glyphID, 1); 290 291 ::CGContextRelease(contextRef); 292} 293 294#if 0 295static void convert_metrics(SkPaint::FontMetrics* dst, 296 const ATSFontMetrics& src) { 297 dst->fTop = -SkFloatToScalar(src.ascent); 298 dst->fAscent = -SkFloatToScalar(src.ascent); 299 dst->fDescent = SkFloatToScalar(src.descent); 300 dst->fBottom = SkFloatToScalar(src.descent); 301 dst->fLeading = SkFloatToScalar(src.leading); 302} 303#endif 304 305static void* get_font_table(ATSFontRef fontID, uint32_t tag) { 306 ByteCount size; 307 OSStatus err = ATSFontGetTable(fontID, tag, 0, 0, NULL, &size); 308 if (err) { 309 return NULL; 310 } 311 void* data = sk_malloc_throw(size); 312 err = ATSFontGetTable(fontID, tag, 0, size, data, &size); 313 if (err) { 314 sk_free(data); 315 data = NULL; 316 } 317 return data; 318} 319 320static int get_be16(const void* data, size_t offset) { 321 const char* ptr = reinterpret_cast<const char*>(data); 322 uint16_t value = *reinterpret_cast<const uint16_t*>(ptr + offset); 323 int n = SkEndian_SwapBE16(value); 324 // now force it to be signed 325 return n << 16 >> 16; 326} 327 328#define SFNT_HEAD_UPEM_OFFSET 18 329#define SFNT_HEAD_YMIN_OFFSET 38 330#define SFNT_HEAD_YMAX_OFFSET 42 331#define SFNT_HEAD_STYLE_OFFSET 44 332 333#define SFNT_HHEA_ASCENT_OFFSET 4 334#define SFNT_HHEA_DESCENT_OFFSET 6 335#define SFNT_HHEA_LEADING_OFFSET 8 336 337static bool init_vertical_metrics(ATSFontRef font, SkPoint pts[5]) { 338 void* head = get_font_table(font, 'head'); 339 if (NULL == head) { 340 return false; 341 } 342 void* hhea = get_font_table(font, 'hhea'); 343 if (NULL == hhea) { 344 sk_free(head); 345 return false; 346 } 347 348 int upem = get_be16(head, SFNT_HEAD_UPEM_OFFSET); 349 int ys[5]; 350 351 ys[0] = -get_be16(head, SFNT_HEAD_YMAX_OFFSET); 352 ys[1] = -get_be16(hhea, SFNT_HHEA_ASCENT_OFFSET); 353 ys[2] = -get_be16(hhea, SFNT_HHEA_DESCENT_OFFSET); 354 ys[3] = -get_be16(head, SFNT_HEAD_YMIN_OFFSET); 355 ys[4] = get_be16(hhea, SFNT_HHEA_LEADING_OFFSET); 356 357 // now do some cleanup, to ensure y[max,min] are really that 358 if (ys[0] > ys[1]) { 359 ys[0] = ys[1]; 360 } 361 if (ys[3] < ys[2]) { 362 ys[3] = ys[2]; 363 } 364 365 for (int i = 0; i < 5; i++) { 366 pts[i].set(0, SkIntToScalar(ys[i]) / upem); 367 } 368 369 sk_free(hhea); 370 sk_free(head); 371 return true; 372} 373 374void SkScalerContext_Mac::generateFontMetrics(SkPaint::FontMetrics* mx, 375 SkPaint::FontMetrics* my) { 376 SkPoint pts[5]; 377 378 if (!init_vertical_metrics(fRec.fFontID, pts)) { 379 // these are not as accurate as init_vertical_metrics :( 380 ATSFontMetrics metrics; 381 ATSFontGetVerticalMetrics(fRec.fFontID, kATSOptionFlagsDefault, 382 &metrics); 383 pts[0].set(0, -SkFloatToScalar(metrics.ascent)); 384 pts[1].set(0, -SkFloatToScalar(metrics.ascent)); 385 pts[2].set(0, -SkFloatToScalar(metrics.descent)); 386 pts[3].set(0, -SkFloatToScalar(metrics.descent)); 387 pts[4].set(0, SkFloatToScalar(metrics.leading)); //+ or -? 388 } 389 390 SkMatrix m; 391 fRec.getSingleMatrix(&m); 392 m.mapPoints(pts, 5); 393 394 if (mx) { 395 mx->fTop = pts[0].fX; 396 mx->fAscent = pts[1].fX; 397 mx->fDescent = pts[2].fX; 398 mx->fBottom = pts[3].fX; 399 mx->fLeading = pts[4].fX; 400 // FIXME: 401 mx->fAvgCharWidth = 0; 402 mx->fXMin = 0; 403 mx->fXMax = 0; 404 mx->fXHeight = 0; 405 } 406 if (my) { 407 my->fTop = pts[0].fY; 408 my->fAscent = pts[1].fY; 409 my->fDescent = pts[2].fY; 410 my->fBottom = pts[3].fY; 411 my->fLeading = pts[4].fY; 412 // FIXME: 413 my->fAvgCharWidth = 0; 414 my->fXMin = 0; 415 my->fXMax = 0; 416 my->fXHeight = 0; 417 } 418} 419 420void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) 421{ 422 SkAutoMutexAcquire ac(gFTMutex); 423 OSStatus err,result; 424 425 err = ::ATSUGlyphGetCubicPaths( 426 fStyle,glyph.fID, 427 &SkScalerContext_Mac::MoveTo, 428 &SkScalerContext_Mac::Line, 429 &SkScalerContext_Mac::Curve, 430 &SkScalerContext_Mac::Close, 431 path,&result); 432 SkASSERT(err == noErr); 433} 434 435OSStatus SkScalerContext_Mac::MoveTo(const Float32Point *pt, void *cb) 436{ 437 reinterpret_cast<SkPath*>(cb)->moveTo(F32PtToSkPoint(*pt)); 438 return noErr; 439} 440 441OSStatus SkScalerContext_Mac::Line(const Float32Point *pt, void *cb) 442{ 443 reinterpret_cast<SkPath*>(cb)->lineTo(F32PtToSkPoint(*pt)); 444 return noErr; 445} 446 447OSStatus SkScalerContext_Mac::Curve(const Float32Point *pt1, 448 const Float32Point *pt2, 449 const Float32Point *pt3, void *cb) 450{ 451 reinterpret_cast<SkPath*>(cb)->cubicTo(F32PtToSkPoint(*pt1), 452 F32PtToSkPoint(*pt2), 453 F32PtToSkPoint(*pt3)); 454 return noErr; 455} 456 457OSStatus SkScalerContext_Mac::Close(void *cb) 458{ 459 reinterpret_cast<SkPath*>(cb)->close(); 460 return noErr; 461} 462 463#pragma mark - 464 465void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) { 466 SkASSERT(!"SkFontHost::Serialize unimplemented"); 467} 468 469SkTypeface* SkFontHost::Deserialize(SkStream* stream) { 470 SkASSERT(!"SkFontHost::Deserialize unimplemented"); 471 return NULL; 472} 473 474SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) { 475 return NULL; 476} 477 478SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) { 479 return NULL; 480} 481 482// static 483SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics( 484 uint32_t fontID, 485 SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) { 486 SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented"); 487 return NULL; 488} 489 490SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) { 491 return new SkScalerContext_Mac(desc); 492} 493 494SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) { 495 uint32_t newFontID = find_default_fontID(); 496 if (newFontID == currFontID) { 497 newFontID = 0; 498 } 499 return newFontID; 500} 501 502SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace, 503 const char familyName[], 504 const void* data, size_t bytelength, 505 SkTypeface::Style style) { 506 // todo: we don't know how to respect style bits 507 if (NULL == familyName && NULL != familyFace) { 508 familyFace->ref(); 509 return const_cast<SkTypeface*>(familyFace); 510 } else { 511 return CreateTypeface_(familyName, style); 512 } 513} 514 515size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) { 516 if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET) 517 return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET; 518 else 519 return 0; // nothing to do 520} 521 522int SkFontHost::ComputeGammaFlag(const SkPaint& paint) { 523 return 0; 524} 525 526void SkFontHost::GetGammaTables(const uint8_t* tables[2]) { 527 tables[0] = NULL; // black gamma (e.g. exp=1.4) 528 tables[1] = NULL; // white gamma (e.g. exp= 1/1.4) 529} 530 531/////////////////////////////////////////////////////////////////////////////// 532 533struct SkSFNTHeader { 534 uint32_t fVersion; 535 uint16_t fNumTables; 536 uint16_t fSearchRange; 537 uint16_t fEntrySelector; 538 uint16_t fRangeShift; 539}; 540 541struct SkSFNTDirEntry { 542 uint32_t fTag; 543 uint32_t fChecksum; 544 uint32_t fOffset; 545 uint32_t fLength; 546}; 547 548struct SfntHeader { 549 SfntHeader(SkFontID fontID, bool needDir) : fCount(0), fData(NULL) { 550 ByteCount size; 551 if (ATSFontGetTableDirectory(fontID, 0, NULL, &size)) { 552 return; 553 } 554 555 SkAutoMalloc storage(size); 556 SkSFNTHeader* header = reinterpret_cast<SkSFNTHeader*>(storage.get()); 557 if (ATSFontGetTableDirectory(fontID, size, header, &size)) { 558 return; 559 } 560 561 fCount = SkEndian_SwapBE16(header->fNumTables); 562 fData = header; 563 storage.detach(); 564 } 565 566 ~SfntHeader() { 567 sk_free(fData); 568 } 569 570 int count() const { return fCount; } 571 const SkSFNTDirEntry* entries() const { 572 return reinterpret_cast<const SkSFNTDirEntry*> 573 (reinterpret_cast<char*>(fData) + sizeof(SkSFNTHeader)); 574 } 575 576private: 577 int fCount; 578 void* fData; 579}; 580 581int SkFontHost::CountTables(SkFontID fontID) { 582 SfntHeader header(fontID, false); 583 return header.count(); 584} 585 586int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) { 587 SfntHeader header(fontID, true); 588 int count = header.count(); 589 const SkSFNTDirEntry* entry = header.entries(); 590 for (int i = 0; i < count; i++) { 591 tags[i] = SkEndian_SwapBE32(entry[i].fTag); 592 } 593 return count; 594} 595 596size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) { 597 ByteCount size; 598 if (ATSFontGetTable(fontID, tag, 0, 0, NULL, &size)) { 599 return 0; 600 } 601 return size; 602} 603 604size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag, 605 size_t offset, size_t length, void* data) { 606 ByteCount size; 607 if (ATSFontGetTable(fontID, tag, offset, length, data, &size)) { 608 return 0; 609 } 610 if (offset >= size) { 611 return 0; 612 } 613 if (offset + length > size) { 614 length = size - offset; 615 } 616 return length; 617} 618 619