SkScalerContext.cpp revision 05b6b4d746867a9fb02e14edfe1bf3685abeb813
1/* libs/graphics/sgl/SkScalerContext.cpp 2** 3** Copyright 2006, The Android Open Source Project 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18#include "SkScalerContext.h" 19#include "SkColorPriv.h" 20#include "SkDescriptor.h" 21#include "SkDraw.h" 22#include "SkFontHost.h" 23#include "SkMaskFilter.h" 24#include "SkPathEffect.h" 25#include "SkRasterizer.h" 26#include "SkRegion.h" 27#include "SkStroke.h" 28#include "SkThread.h" 29 30#ifdef SK_DEBUG 31// #define TRACK_MISSING_CHARS 32#endif 33 34#define ComputeBWRowBytes(width) (((unsigned)(width) + 7) >> 3) 35 36static const uint8_t* gBlackGammaTable; 37static const uint8_t* gWhiteGammaTable; 38 39void SkGlyph::toMask(SkMask* mask) const { 40 SkASSERT(mask); 41 42 mask->fImage = (uint8_t*)fImage; 43 mask->fBounds.set(fLeft, fTop, fLeft + fWidth, fTop + fHeight); 44 mask->fRowBytes = this->rowBytes(); 45 mask->fFormat = static_cast<SkMask::Format>(fMaskFormat); 46} 47 48size_t SkGlyph::computeImageSize() const { 49 const size_t size = this->rowBytes() * fHeight; 50 51 switch (fMaskFormat) { 52 case SkMask::kHorizontalLCD_Format: 53 return SkAlign4(size) + sizeof(uint32_t) * ((fWidth + 2) * fHeight); 54 case SkMask::kVerticalLCD_Format: 55 return SkAlign4(size) + sizeof(uint32_t) * (fWidth * (fHeight + 2)); 56 case SkMask::k3D_Format: 57 return 3 * size; 58 default: 59 return size; 60 } 61} 62 63void SkGlyph::zeroMetrics() { 64 fAdvanceX = 0; 65 fAdvanceY = 0; 66 fWidth = 0; 67 fHeight = 0; 68 fTop = 0; 69 fLeft = 0; 70 fRsbDelta = 0; 71 fLsbDelta = 0; 72} 73 74void SkGlyph::expandA8ToLCD() const { 75 SkASSERT(fMaskFormat == SkMask::kHorizontalLCD_Format || 76 fMaskFormat == SkMask::kVerticalLCD_Format); 77 78#if defined(SK_SUPPORT_LCDTEXT) 79 uint8_t* input = reinterpret_cast<uint8_t*>(fImage); 80 uint32_t* output = reinterpret_cast<uint32_t*>(input + SkAlign4(rowBytes() * fHeight)); 81 82 if (fMaskFormat == SkMask::kHorizontalLCD_Format) { 83 for (unsigned y = 0; y < fHeight; ++y) { 84 const uint8_t* inputRow = input; 85 *output++ = 0; // make the extra column on the left clear 86 for (unsigned x = 0; x < fWidth; ++x) { 87 const uint8_t alpha = *inputRow++; 88 *output++ = SkPackARGB32(alpha, alpha, alpha, alpha); 89 } 90 *output++ = 0; 91 92 input += rowBytes(); 93 } 94 } else { 95 const unsigned outputRowBytes = sizeof(uint32_t) * fWidth; 96 memset(output, 0, outputRowBytes); 97 output += fWidth; 98 99 for (unsigned y = 0; y < fHeight; ++y) { 100 const uint8_t* inputRow = input; 101 for (unsigned x = 0; x < fWidth; ++x) { 102 const uint8_t alpha = *inputRow++; 103 *output++ = SkPackARGB32(alpha, alpha, alpha, alpha); 104 } 105 106 input += rowBytes(); 107 } 108 109 memset(output, 0, outputRowBytes); 110 output += fWidth; 111 } 112#else 113#endif 114} 115 116/////////////////////////////////////////////////////////////////////////////// 117 118#ifdef SK_DEBUG 119 #define DUMP_RECx 120#endif 121 122static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) { 123 SkFlattenable* obj = NULL; 124 uint32_t len; 125 const void* data = desc->findEntry(tag, &len); 126 127 if (data) { 128 SkFlattenableReadBuffer buffer(data, len); 129 obj = buffer.readFlattenable(); 130 SkASSERT(buffer.offset() == buffer.size()); 131 } 132 return obj; 133} 134 135SkScalerContext::SkScalerContext(const SkDescriptor* desc) 136 : fPathEffect(NULL), fMaskFilter(NULL) 137{ 138 static bool gHaveGammaTables; 139 if (!gHaveGammaTables) { 140 const uint8_t* tables[2]; 141 SkFontHost::GetGammaTables(tables); 142 gBlackGammaTable = tables[0]; 143 gWhiteGammaTable = tables[1]; 144 gHaveGammaTables = true; 145 } 146 147 fBaseGlyphCount = 0; 148 fNextContext = NULL; 149 150 const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL); 151 SkASSERT(rec); 152 153 fRec = *rec; 154 155#ifdef DUMP_REC 156 desc->assertChecksum(); 157 SkDebugf("SkScalarContext checksum %x count %d length %d\n", desc->getChecksum(), desc->getCount(), desc->getLength()); 158 SkDebugf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\n", 159 rec->fTextSize, rec->fPreScaleX, rec->fPreSkewX, rec->fPost2x2[0][0], 160 rec->fPost2x2[0][1], rec->fPost2x2[1][0], rec->fPost2x2[1][1]); 161 SkDebugf(" frame %g miter %g hints %d framefill %d format %d join %d\n", 162 rec->fFrameWidth, rec->fMiterLimit, rec->fHints, rec->fFrameAndFill, 163 rec->fMaskFormat, rec->fStrokeJoin); 164 SkDebugf(" pathEffect %x maskFilter %x\n", desc->findEntry(kPathEffect_SkDescriptorTag, NULL), 165 desc->findEntry(kMaskFilter_SkDescriptorTag, NULL)); 166#endif 167 168 fPathEffect = (SkPathEffect*)load_flattenable(desc, kPathEffect_SkDescriptorTag); 169 fMaskFilter = (SkMaskFilter*)load_flattenable(desc, kMaskFilter_SkDescriptorTag); 170 fRasterizer = (SkRasterizer*)load_flattenable(desc, kRasterizer_SkDescriptorTag); 171} 172 173SkScalerContext::~SkScalerContext() { 174 SkDELETE(fNextContext); 175 176 SkSafeUnref(fPathEffect); 177 SkSafeUnref(fMaskFilter); 178 SkSafeUnref(fRasterizer); 179} 180 181static SkScalerContext* allocNextContext(const SkScalerContext::Rec& rec) { 182 // fonthost will determine the next possible font to search, based 183 // on the current font in fRec. It will return NULL if ctx is our 184 // last font that can be searched (i.e. ultimate fallback font) 185 uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID); 186 if (0 == newFontID) { 187 return NULL; 188 } 189 190 SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1)); 191 SkDescriptor* desc = ad.getDesc(); 192 193 desc->init(); 194 SkScalerContext::Rec* newRec = 195 (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag, 196 sizeof(rec), &rec); 197 newRec->fFontID = newFontID; 198 desc->computeChecksum(); 199 200 return SkFontHost::CreateScalerContext(desc); 201} 202 203/* Return the next context, creating it if its not already created, but return 204 NULL if the fonthost says there are no more fonts to fallback to. 205 */ 206SkScalerContext* SkScalerContext::getNextContext() { 207 SkScalerContext* next = fNextContext; 208 // if next is null, then either it isn't cached yet, or we're at the 209 // end of our possible chain 210 if (NULL == next) { 211 next = allocNextContext(fRec); 212 if (NULL == next) { 213 return NULL; 214 } 215 // next's base is our base + our local count 216 next->setBaseGlyphCount(fBaseGlyphCount + this->getGlyphCount()); 217 // cache the answer 218 fNextContext = next; 219 } 220 return next; 221} 222 223SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) { 224 unsigned glyphID = glyph.getGlyphID(); 225 SkScalerContext* ctx = this; 226 for (;;) { 227 unsigned count = ctx->getGlyphCount(); 228 if (glyphID < count) { 229 break; 230 } 231 glyphID -= count; 232 ctx = ctx->getNextContext(); 233 if (NULL == ctx) { 234 SkDebugf("--- no context for glyph %x\n", glyph.getGlyphID()); 235 // just return the original context (this) 236 return this; 237 } 238 } 239 return ctx; 240} 241 242/* This loops through all available fallback contexts (if needed) until it 243 finds some context that can handle the unichar. If all fail, returns 0 244 */ 245uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) { 246 SkScalerContext* ctx = this; 247 unsigned glyphID; 248 for (;;) { 249 glyphID = ctx->generateCharToGlyph(uni); 250 if (glyphID) { 251 break; // found it 252 } 253 ctx = ctx->getNextContext(); 254 if (NULL == ctx) { 255 return 0; // no more contexts, return missing glyph 256 } 257 } 258 // add the ctx's base, making glyphID unique for chain of contexts 259 glyphID += ctx->fBaseGlyphCount; 260 // check for overflow of 16bits, since our glyphID cannot exceed that 261 if (glyphID > 0xFFFF) { 262 glyphID = 0; 263 } 264 return SkToU16(glyphID); 265} 266 267SkUnichar SkScalerContext::glyphIDToChar(uint16_t glyphID) { 268 SkScalerContext* ctx = this; 269 unsigned rangeEnd = 0; 270 do { 271 unsigned rangeStart = rangeEnd; 272 273 rangeEnd += ctx->getGlyphCount(); 274 if (rangeStart <= glyphID && glyphID < rangeEnd) { 275 return ctx->generateGlyphToChar(glyphID - rangeStart); 276 } 277 ctx = ctx->getNextContext(); 278 } while (NULL != ctx); 279 return 0; 280} 281 282void SkScalerContext::getAdvance(SkGlyph* glyph) { 283 // mark us as just having a valid advance 284 glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE; 285 // we mark the format before making the call, in case the impl 286 // internally ends up calling its generateMetrics, which is OK 287 // albeit slower than strictly necessary 288 this->getGlyphContext(*glyph)->generateAdvance(glyph); 289} 290 291void SkScalerContext::getMetrics(SkGlyph* glyph) { 292 this->getGlyphContext(*glyph)->generateMetrics(glyph); 293 294 // for now we have separate cache entries for devkerning on and off 295 // in the future we might share caches, but make our measure/draw 296 // code make the distinction. Thus we zap the values if the caller 297 // has not asked for them. 298 if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) { 299 // no devkern, so zap the fields 300 glyph->fLsbDelta = glyph->fRsbDelta = 0; 301 } 302 303 // if either dimension is empty, zap the image bounds of the glyph 304 if (0 == glyph->fWidth || 0 == glyph->fHeight) { 305 glyph->fWidth = 0; 306 glyph->fHeight = 0; 307 glyph->fTop = 0; 308 glyph->fLeft = 0; 309 glyph->fMaskFormat = 0; 310 return; 311 } 312 313 if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { 314 SkPath devPath, fillPath; 315 SkMatrix fillToDevMatrix; 316 317 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); 318 319 if (fRasterizer) { 320 SkMask mask; 321 322 if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, 323 fMaskFilter, &mask, 324 SkMask::kJustComputeBounds_CreateMode)) { 325 glyph->fLeft = mask.fBounds.fLeft; 326 glyph->fTop = mask.fBounds.fTop; 327 glyph->fWidth = SkToU16(mask.fBounds.width()); 328 glyph->fHeight = SkToU16(mask.fBounds.height()); 329 } else { 330 goto SK_ERROR; 331 } 332 } else { 333 // just use devPath 334 SkIRect ir; 335 devPath.getBounds().roundOut(&ir); 336 337 if (ir.isEmpty() || !ir.is16Bit()) { 338 goto SK_ERROR; 339 } 340 glyph->fLeft = ir.fLeft; 341 glyph->fTop = ir.fTop; 342 glyph->fWidth = SkToU16(ir.width()); 343 glyph->fHeight = SkToU16(ir.height()); 344 } 345 } 346 347 if (SkMask::kARGB32_Format != glyph->fMaskFormat) { 348 glyph->fMaskFormat = fRec.fMaskFormat; 349 } 350 351 if (fMaskFilter) { 352 SkMask src, dst; 353 SkMatrix matrix; 354 355 glyph->toMask(&src); 356 fRec.getMatrixFrom2x2(&matrix); 357 358 src.fImage = NULL; // only want the bounds from the filter 359 if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) { 360 SkASSERT(dst.fImage == NULL); 361 glyph->fLeft = dst.fBounds.fLeft; 362 glyph->fTop = dst.fBounds.fTop; 363 glyph->fWidth = SkToU16(dst.fBounds.width()); 364 glyph->fHeight = SkToU16(dst.fBounds.height()); 365 glyph->fMaskFormat = dst.fFormat; 366 } 367 } 368 return; 369 370SK_ERROR: 371 // draw nothing 'cause we failed 372 glyph->fLeft = 0; 373 glyph->fTop = 0; 374 glyph->fWidth = 0; 375 glyph->fHeight = 0; 376 // put a valid value here, in case it was earlier set to 377 // MASK_FORMAT_JUST_ADVANCE 378 glyph->fMaskFormat = fRec.fMaskFormat; 379} 380 381void SkScalerContext::getImage(const SkGlyph& origGlyph) { 382 const SkGlyph* glyph = &origGlyph; 383 SkGlyph tmpGlyph; 384 385 if (fMaskFilter) { // restore the prefilter bounds 386 tmpGlyph.init(origGlyph.fID); 387 388 // need the original bounds, sans our maskfilter 389 SkMaskFilter* mf = fMaskFilter; 390 fMaskFilter = NULL; // temp disable 391 this->getMetrics(&tmpGlyph); 392 fMaskFilter = mf; // restore 393 394 tmpGlyph.fImage = origGlyph.fImage; 395 396 // we need the prefilter bounds to be <= filter bounds 397 SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth); 398 SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight); 399 glyph = &tmpGlyph; 400 } 401 402 if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { 403 SkPath devPath, fillPath; 404 SkMatrix fillToDevMatrix; 405 406 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); 407 408 const bool lcdMode = fRec.fMaskFormat == SkMask::kHorizontalLCD_Format || 409 fRec.fMaskFormat == SkMask::kVerticalLCD_Format; 410 411 if (fRasterizer) { 412 SkMask mask; 413 414 glyph->toMask(&mask); 415 mask.fFormat = SkMask::kA8_Format; 416 sk_bzero(glyph->fImage, mask.computeImageSize()); 417 418 if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, 419 fMaskFilter, &mask, 420 SkMask::kJustRenderImage_CreateMode)) { 421 return; 422 } 423 } else { 424 SkBitmap bm; 425 SkBitmap::Config config; 426 SkMatrix matrix; 427 SkRegion clip; 428 SkPaint paint; 429 SkDraw draw; 430 431 if (SkMask::kA8_Format == fRec.fMaskFormat || lcdMode) { 432 config = SkBitmap::kA8_Config; 433 paint.setAntiAlias(true); 434 } else { 435 SkASSERT(SkMask::kBW_Format == fRec.fMaskFormat); 436 config = SkBitmap::kA1_Config; 437 paint.setAntiAlias(false); 438 } 439 440 clip.setRect(0, 0, glyph->fWidth, glyph->fHeight); 441 matrix.setTranslate(-SkIntToScalar(glyph->fLeft), 442 -SkIntToScalar(glyph->fTop)); 443 bm.setConfig(config, glyph->fWidth, glyph->fHeight, 444 glyph->rowBytes()); 445 bm.setPixels(glyph->fImage); 446 sk_bzero(glyph->fImage, bm.height() * bm.rowBytes()); 447 448 draw.fClip = &clip; 449 draw.fMatrix = &matrix; 450 draw.fBitmap = &bm; 451 draw.fBounder = NULL; 452 draw.drawPath(devPath, paint); 453 } 454 455 if (lcdMode) 456 glyph->expandA8ToLCD(); 457 } else { 458 this->getGlyphContext(*glyph)->generateImage(*glyph); 459 } 460 461 if (fMaskFilter) { 462 SkMask srcM, dstM; 463 SkMatrix matrix; 464 465 // the src glyph image shouldn't be 3D 466 SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat); 467 glyph->toMask(&srcM); 468 fRec.getMatrixFrom2x2(&matrix); 469 470 if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) { 471 int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width()); 472 int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height()); 473 int dstRB = origGlyph.rowBytes(); 474 int srcRB = dstM.fRowBytes; 475 476 const uint8_t* src = (const uint8_t*)dstM.fImage; 477 uint8_t* dst = (uint8_t*)origGlyph.fImage; 478 479 if (SkMask::k3D_Format == dstM.fFormat) { 480 // we have to copy 3 times as much 481 height *= 3; 482 } 483 484 // clean out our glyph, since it may be larger than dstM 485 //sk_bzero(dst, height * dstRB); 486 487 while (--height >= 0) { 488 memcpy(dst, src, width); 489 src += srcRB; 490 dst += dstRB; 491 } 492 SkMask::FreeImage(dstM.fImage); 493 } 494 } 495 496 // check to see if we should filter the alpha channel 497 498 if (NULL == fMaskFilter && 499 fRec.fMaskFormat != SkMask::kBW_Format && 500 fRec.fMaskFormat != SkMask::kLCD16_Format && 501 (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0) 502 { 503 const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable; 504 if (NULL != table) 505 { 506 uint8_t* dst = (uint8_t*)origGlyph.fImage; 507 unsigned rowBytes = origGlyph.rowBytes(); 508 509 for (int y = origGlyph.fHeight - 1; y >= 0; --y) 510 { 511 for (int x = origGlyph.fWidth - 1; x >= 0; --x) 512 dst[x] = table[dst[x]]; 513 dst += rowBytes; 514 } 515 } 516 } 517} 518 519void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) 520{ 521 this->internalGetPath(glyph, NULL, path, NULL); 522} 523 524void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my) 525{ 526 this->generateFontMetrics(mx, my); 527} 528 529SkUnichar SkScalerContext::generateGlyphToChar(uint16_t glyph) { 530 return 0; 531} 532 533/////////////////////////////////////////////////////////////////////// 534 535void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix) 536{ 537 SkPath path; 538 539 this->getGlyphContext(glyph)->generatePath(glyph, &path); 540 541 if (fRec.fFrameWidth > 0 || fPathEffect != NULL) 542 { 543 // need the path in user-space, with only the point-size applied 544 // so that our stroking and effects will operate the same way they 545 // would if the user had extracted the path themself, and then 546 // called drawPath 547 SkPath localPath; 548 SkMatrix matrix, inverse; 549 550 fRec.getMatrixFrom2x2(&matrix); 551 matrix.invert(&inverse); 552 path.transform(inverse, &localPath); 553 // now localPath is only affected by the paint settings, and not the canvas matrix 554 555 SkScalar width = fRec.fFrameWidth; 556 557 if (fPathEffect) 558 { 559 SkPath effectPath; 560 561 if (fPathEffect->filterPath(&effectPath, localPath, &width)) 562 localPath.swap(effectPath); 563 } 564 565 if (width > 0) 566 { 567 SkStroke stroker; 568 SkPath outline; 569 570 stroker.setWidth(width); 571 stroker.setMiterLimit(fRec.fMiterLimit); 572 stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin); 573 stroker.setDoFill(SkToBool(fRec.fFlags & kFrameAndFill_Flag)); 574 stroker.strokePath(localPath, &outline); 575 localPath.swap(outline); 576 } 577 578 // now return stuff to the caller 579 if (fillToDevMatrix) 580 *fillToDevMatrix = matrix; 581 582 if (devPath) 583 localPath.transform(matrix, devPath); 584 585 if (fillPath) 586 fillPath->swap(localPath); 587 } 588 else // nothing tricky to do 589 { 590 if (fillToDevMatrix) 591 fillToDevMatrix->reset(); 592 593 if (devPath) 594 { 595 if (fillPath == NULL) 596 devPath->swap(path); 597 else 598 *devPath = path; 599 } 600 601 if (fillPath) 602 fillPath->swap(path); 603 } 604 605 if (devPath) 606 devPath->updateBoundsCache(); 607 if (fillPath) 608 fillPath->updateBoundsCache(); 609} 610 611 612void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const 613{ 614 dst->reset(); 615 dst->setScaleX(fPost2x2[0][0]); 616 dst->setSkewX( fPost2x2[0][1]); 617 dst->setSkewY( fPost2x2[1][0]); 618 dst->setScaleY(fPost2x2[1][1]); 619} 620 621void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const 622{ 623 m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize); 624 if (fPreSkewX) 625 m->postSkew(fPreSkewX, 0); 626} 627 628void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const 629{ 630 this->getLocalMatrix(m); 631 632 // now concat the device matrix 633 { 634 SkMatrix deviceMatrix; 635 this->getMatrixFrom2x2(&deviceMatrix); 636 m->postConcat(deviceMatrix); 637 } 638} 639 640/////////////////////////////////////////////////////////////////////////////// 641 642#include "SkFontHost.h" 643 644class SkScalerContext_Empty : public SkScalerContext { 645public: 646 SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {} 647 648protected: 649 virtual unsigned generateGlyphCount() { 650 return 0; 651 } 652 virtual uint16_t generateCharToGlyph(SkUnichar uni) { 653 return 0; 654 } 655 virtual void generateAdvance(SkGlyph* glyph) { 656 glyph->zeroMetrics(); 657 } 658 virtual void generateMetrics(SkGlyph* glyph) { 659 glyph->zeroMetrics(); 660 } 661 virtual void generateImage(const SkGlyph& glyph) {} 662 virtual void generatePath(const SkGlyph& glyph, SkPath* path) {} 663 virtual void generateFontMetrics(SkPaint::FontMetrics* mx, 664 SkPaint::FontMetrics* my) { 665 if (mx) { 666 sk_bzero(mx, sizeof(*mx)); 667 } 668 if (my) { 669 sk_bzero(my, sizeof(*my)); 670 } 671 } 672}; 673 674extern SkScalerContext* SkCreateColorScalerContext(const SkDescriptor* desc); 675 676SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc) 677{ 678 SkScalerContext* c = NULL; //SkCreateColorScalerContext(desc); 679 if (NULL == c) { 680 c = SkFontHost::CreateScalerContext(desc); 681 } 682 if (NULL == c) { 683 c = SkNEW_ARGS(SkScalerContext_Empty, (desc)); 684 } 685 return c; 686} 687 688