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