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