• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
36 static const uint8_t* gBlackGammaTable;
37 static const uint8_t* gWhiteGammaTable;
38 
toMask(SkMask * mask) const39 void 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 
computeImageSize() const48 size_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 
zeroMetrics()63 void 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 
expandA8ToLCD() const74 void 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 
load_flattenable(const SkDescriptor * desc,uint32_t tag)122 static 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 
SkScalerContext(const SkDescriptor * desc)135 SkScalerContext::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 
~SkScalerContext()173 SkScalerContext::~SkScalerContext() {
174     SkDELETE(fNextContext);
175 
176     fPathEffect->safeUnref();
177     fMaskFilter->safeUnref();
178     fRasterizer->safeUnref();
179 }
180 
allocNextContext(const SkScalerContext::Rec & rec)181 static 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  */
getNextContext()206 SkScalerContext* 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 
getGlyphContext(const SkGlyph & glyph)223 SkScalerContext* 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  */
charToGlyphID(SkUnichar uni)245 uint16_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 
getAdvance(SkGlyph * glyph)267 void SkScalerContext::getAdvance(SkGlyph* glyph) {
268     // mark us as just having a valid advance
269     glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE;
270     // we mark the format before making the call, in case the impl
271     // internally ends up calling its generateMetrics, which is OK
272     // albeit slower than strictly necessary
273     this->getGlyphContext(*glyph)->generateAdvance(glyph);
274 }
275 
getMetrics(SkGlyph * glyph)276 void SkScalerContext::getMetrics(SkGlyph* glyph) {
277     this->getGlyphContext(*glyph)->generateMetrics(glyph);
278 
279     // for now we have separate cache entries for devkerning on and off
280     // in the future we might share caches, but make our measure/draw
281     // code make the distinction. Thus we zap the values if the caller
282     // has not asked for them.
283     if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) {
284         // no devkern, so zap the fields
285         glyph->fLsbDelta = glyph->fRsbDelta = 0;
286     }
287 
288     // if either dimension is empty, zap the image bounds of the glyph
289     if (0 == glyph->fWidth || 0 == glyph->fHeight) {
290         glyph->fWidth   = 0;
291         glyph->fHeight  = 0;
292         glyph->fTop     = 0;
293         glyph->fLeft    = 0;
294         glyph->fMaskFormat = 0;
295         return;
296     }
297 
298     if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) {
299         SkPath      devPath, fillPath;
300         SkMatrix    fillToDevMatrix;
301 
302         this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
303 
304         if (fRasterizer) {
305             SkMask  mask;
306 
307             if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
308                                        fMaskFilter, &mask,
309                                        SkMask::kJustComputeBounds_CreateMode)) {
310                 glyph->fLeft    = mask.fBounds.fLeft;
311                 glyph->fTop     = mask.fBounds.fTop;
312                 glyph->fWidth   = SkToU16(mask.fBounds.width());
313                 glyph->fHeight  = SkToU16(mask.fBounds.height());
314             } else {
315                 // draw nothing 'cause we failed
316                 glyph->fLeft    = 0;
317                 glyph->fTop     = 0;
318                 glyph->fWidth   = 0;
319                 glyph->fHeight  = 0;
320                 return;
321             }
322         } else {
323             // just use devPath
324             SkIRect ir;
325             devPath.getBounds().roundOut(&ir);
326 
327             glyph->fLeft    = ir.fLeft;
328             glyph->fTop     = ir.fTop;
329             glyph->fWidth   = SkToU16(ir.width());
330             glyph->fHeight  = SkToU16(ir.height());
331         }
332     }
333 
334     glyph->fMaskFormat = fRec.fMaskFormat;
335 
336     if (fMaskFilter) {
337         SkMask      src, dst;
338         SkMatrix    matrix;
339 
340         glyph->toMask(&src);
341         fRec.getMatrixFrom2x2(&matrix);
342 
343         src.fImage = NULL;  // only want the bounds from the filter
344         if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) {
345             SkASSERT(dst.fImage == NULL);
346             glyph->fLeft    = dst.fBounds.fLeft;
347             glyph->fTop     = dst.fBounds.fTop;
348             glyph->fWidth   = SkToU16(dst.fBounds.width());
349             glyph->fHeight  = SkToU16(dst.fBounds.height());
350             glyph->fMaskFormat = dst.fFormat;
351         }
352     }
353 }
354 
getImage(const SkGlyph & origGlyph)355 void SkScalerContext::getImage(const SkGlyph& origGlyph) {
356     const SkGlyph*  glyph = &origGlyph;
357     SkGlyph         tmpGlyph;
358 
359     if (fMaskFilter) {   // restore the prefilter bounds
360         tmpGlyph.fID = origGlyph.fID;
361 
362         // need the original bounds, sans our maskfilter
363         SkMaskFilter* mf = fMaskFilter;
364         fMaskFilter = NULL;             // temp disable
365         this->getMetrics(&tmpGlyph);
366         fMaskFilter = mf;               // restore
367 
368         tmpGlyph.fImage = origGlyph.fImage;
369 
370         // we need the prefilter bounds to be <= filter bounds
371         SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
372         SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
373         glyph = &tmpGlyph;
374     }
375 
376     if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) {
377         SkPath      devPath, fillPath;
378         SkMatrix    fillToDevMatrix;
379 
380         this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
381 
382         const bool lcdMode = fRec.fMaskFormat == SkMask::kHorizontalLCD_Format ||
383                              fRec.fMaskFormat == SkMask::kVerticalLCD_Format;
384 
385         if (fRasterizer) {
386             SkMask  mask;
387 
388             glyph->toMask(&mask);
389             mask.fFormat = SkMask::kA8_Format;
390             sk_bzero(glyph->fImage, mask.computeImageSize());
391 
392             if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
393                                         fMaskFilter, &mask,
394                                         SkMask::kJustRenderImage_CreateMode)) {
395                 return;
396             }
397         } else {
398             SkBitmap    bm;
399             SkBitmap::Config config;
400             SkMatrix    matrix;
401             SkRegion    clip;
402             SkPaint     paint;
403             SkDraw      draw;
404 
405             if (SkMask::kA8_Format == fRec.fMaskFormat || lcdMode) {
406                 config = SkBitmap::kA8_Config;
407                 paint.setAntiAlias(true);
408             } else {
409                 SkASSERT(SkMask::kBW_Format == fRec.fMaskFormat);
410                 config = SkBitmap::kA1_Config;
411                 paint.setAntiAlias(false);
412             }
413 
414             clip.setRect(0, 0, glyph->fWidth, glyph->fHeight);
415             matrix.setTranslate(-SkIntToScalar(glyph->fLeft),
416                                 -SkIntToScalar(glyph->fTop));
417             bm.setConfig(config, glyph->fWidth, glyph->fHeight,
418                          glyph->rowBytes());
419             bm.setPixels(glyph->fImage);
420             sk_bzero(glyph->fImage, bm.height() * bm.rowBytes());
421 
422             draw.fClip  = &clip;
423             draw.fMatrix = &matrix;
424             draw.fBitmap = &bm;
425             draw.fBounder = NULL;
426             draw.drawPath(devPath, paint);
427         }
428 
429         if (lcdMode)
430             glyph->expandA8ToLCD();
431     } else {
432         this->getGlyphContext(*glyph)->generateImage(*glyph);
433     }
434 
435     if (fMaskFilter) {
436         SkMask      srcM, dstM;
437         SkMatrix    matrix;
438 
439         // the src glyph image shouldn't be 3D
440         SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
441         glyph->toMask(&srcM);
442         fRec.getMatrixFrom2x2(&matrix);
443 
444         if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {
445             int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width());
446             int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height());
447             int dstRB = origGlyph.rowBytes();
448             int srcRB = dstM.fRowBytes;
449 
450             const uint8_t* src = (const uint8_t*)dstM.fImage;
451             uint8_t* dst = (uint8_t*)origGlyph.fImage;
452 
453             if (SkMask::k3D_Format == dstM.fFormat) {
454                 // we have to copy 3 times as much
455                 height *= 3;
456             }
457 
458             // clean out our glyph, since it may be larger than dstM
459             //sk_bzero(dst, height * dstRB);
460 
461             while (--height >= 0) {
462                 memcpy(dst, src, width);
463                 src += srcRB;
464                 dst += dstRB;
465             }
466             SkMask::FreeImage(dstM.fImage);
467         }
468     }
469 
470     // check to see if we should filter the alpha channel
471 
472     if (NULL == fMaskFilter &&
473         fRec.fMaskFormat != SkMask::kBW_Format &&
474         (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0)
475     {
476         const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable;
477         if (NULL != table)
478         {
479             uint8_t* dst = (uint8_t*)origGlyph.fImage;
480             unsigned rowBytes = origGlyph.rowBytes();
481 
482             for (int y = origGlyph.fHeight - 1; y >= 0; --y)
483             {
484                 for (int x = origGlyph.fWidth - 1; x >= 0; --x)
485                     dst[x] = table[dst[x]];
486                 dst += rowBytes;
487             }
488         }
489     }
490 }
491 
getPath(const SkGlyph & glyph,SkPath * path)492 void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path)
493 {
494     this->internalGetPath(glyph, NULL, path, NULL);
495 }
496 
getFontMetrics(SkPaint::FontMetrics * mx,SkPaint::FontMetrics * my)497 void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my)
498 {
499     this->generateFontMetrics(mx, my);
500 }
501 
502 ///////////////////////////////////////////////////////////////////////
503 
internalGetPath(const SkGlyph & glyph,SkPath * fillPath,SkPath * devPath,SkMatrix * fillToDevMatrix)504 void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix)
505 {
506     SkPath  path;
507 
508     this->getGlyphContext(glyph)->generatePath(glyph, &path);
509 
510     if (fRec.fFrameWidth > 0 || fPathEffect != NULL)
511     {
512         // need the path in user-space, with only the point-size applied
513         // so that our stroking and effects will operate the same way they
514         // would if the user had extracted the path themself, and then
515         // called drawPath
516         SkPath      localPath;
517         SkMatrix    matrix, inverse;
518 
519         fRec.getMatrixFrom2x2(&matrix);
520         matrix.invert(&inverse);
521         path.transform(inverse, &localPath);
522         // now localPath is only affected by the paint settings, and not the canvas matrix
523 
524         SkScalar width = fRec.fFrameWidth;
525 
526         if (fPathEffect)
527         {
528             SkPath effectPath;
529 
530             if (fPathEffect->filterPath(&effectPath, localPath, &width))
531                 localPath.swap(effectPath);
532         }
533 
534         if (width > 0)
535         {
536             SkStroke    stroker;
537             SkPath      outline;
538 
539             stroker.setWidth(width);
540             stroker.setMiterLimit(fRec.fMiterLimit);
541             stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin);
542             stroker.setDoFill(SkToBool(fRec.fFlags & kFrameAndFill_Flag));
543             stroker.strokePath(localPath, &outline);
544             localPath.swap(outline);
545         }
546 
547         // now return stuff to the caller
548         if (fillToDevMatrix)
549             *fillToDevMatrix = matrix;
550 
551         if (devPath)
552             localPath.transform(matrix, devPath);
553 
554         if (fillPath)
555             fillPath->swap(localPath);
556     }
557     else    // nothing tricky to do
558     {
559         if (fillToDevMatrix)
560             fillToDevMatrix->reset();
561 
562         if (devPath)
563         {
564             if (fillPath == NULL)
565                 devPath->swap(path);
566             else
567                 *devPath = path;
568         }
569 
570         if (fillPath)
571             fillPath->swap(path);
572     }
573 
574     if (devPath)
575         devPath->updateBoundsCache();
576     if (fillPath)
577         fillPath->updateBoundsCache();
578 }
579 
580 
getMatrixFrom2x2(SkMatrix * dst) const581 void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const
582 {
583     dst->reset();
584     dst->setScaleX(fPost2x2[0][0]);
585     dst->setSkewX( fPost2x2[0][1]);
586     dst->setSkewY( fPost2x2[1][0]);
587     dst->setScaleY(fPost2x2[1][1]);
588 }
589 
getLocalMatrix(SkMatrix * m) const590 void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const
591 {
592     m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
593     if (fPreSkewX)
594         m->postSkew(fPreSkewX, 0);
595 }
596 
getSingleMatrix(SkMatrix * m) const597 void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const
598 {
599     this->getLocalMatrix(m);
600 
601     //  now concat the device matrix
602     {
603         SkMatrix    deviceMatrix;
604         this->getMatrixFrom2x2(&deviceMatrix);
605         m->postConcat(deviceMatrix);
606     }
607 }
608 
609 ///////////////////////////////////////////////////////////////////////////////
610 
611 #include "SkFontHost.h"
612 
613 class SkScalerContext_Empty : public SkScalerContext {
614 public:
SkScalerContext_Empty(const SkDescriptor * desc)615     SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {}
616 
617 protected:
generateGlyphCount() const618     virtual unsigned generateGlyphCount() const {
619         return 0;
620     }
generateCharToGlyph(SkUnichar uni)621     virtual uint16_t generateCharToGlyph(SkUnichar uni) {
622         return 0;
623     }
generateAdvance(SkGlyph * glyph)624     virtual void generateAdvance(SkGlyph* glyph) {
625         glyph->zeroMetrics();
626     }
generateMetrics(SkGlyph * glyph)627     virtual void generateMetrics(SkGlyph* glyph) {
628         glyph->zeroMetrics();
629     }
generateImage(const SkGlyph & glyph)630     virtual void generateImage(const SkGlyph& glyph) {}
generatePath(const SkGlyph & glyph,SkPath * path)631     virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
generateFontMetrics(SkPaint::FontMetrics * mx,SkPaint::FontMetrics * my)632     virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
633                                      SkPaint::FontMetrics* my) {
634         if (mx) {
635             sk_bzero(mx, sizeof(*mx));
636         }
637         if (my) {
638             sk_bzero(my, sizeof(*my));
639         }
640     }
641 };
642 
Create(const SkDescriptor * desc)643 SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc)
644 {
645     SkScalerContext* c = SkFontHost::CreateScalerContext(desc);
646     if (NULL == c) {
647         c = SkNEW_ARGS(SkScalerContext_Empty, (desc));
648     }
649     return c;
650 }
651 
652