SkScalerContext.cpp revision 7db9fe65b2bb099e9dc41f79cc15b6a7a142bed6
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
32static const uint8_t* gBlackGammaTable;
33static const uint8_t* gWhiteGammaTable;
34
35void 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
44size_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
59void 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
70void 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
118static 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
131SkScalerContext::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
171SkScalerContext::~SkScalerContext() {
172    SkDELETE(fNextContext);
173
174    SkSafeUnref(fPathEffect);
175    SkSafeUnref(fMaskFilter);
176    SkSafeUnref(fRasterizer);
177}
178
179static 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);
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 */
204SkScalerContext* 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
221SkScalerContext* 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 */
243uint16_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
265SkUnichar 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
280void 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
289void 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
368SK_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
379void 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
517void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) {
518    this->internalGetPath(glyph, NULL, path, NULL);
519}
520
521void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx,
522                                     SkPaint::FontMetrics* my) {
523    this->generateFontMetrics(mx, my);
524}
525
526SkUnichar SkScalerContext::generateGlyphToChar(uint16_t glyph) {
527    return 0;
528}
529
530///////////////////////////////////////////////////////////////////////////////
531
532void 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
609void 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
617void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const {
618    m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
619    if (fPreSkewX) {
620        m->postSkew(fPreSkewX, 0);
621    }
622}
623
624void 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
637class SkScalerContext_Empty : public SkScalerContext {
638public:
639    SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {}
640
641protected:
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
667extern SkScalerContext* SkCreateColorScalerContext(const SkDescriptor* desc);
668
669SkScalerContext* 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