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
glyphIDToChar(uint16_t glyphID)267 SkUnichar 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
getAdvance(SkGlyph * glyph)282 void 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
getMetrics(SkGlyph * glyph)291 void 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 glyph->fMaskFormat = fRec.fMaskFormat;
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
getImage(const SkGlyph & origGlyph)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.fID = 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 } else {
456 this->getGlyphContext(*glyph)->generateImage(*glyph);
457 }
458
459 if (fMaskFilter) {
460 SkMask srcM, dstM;
461 SkMatrix matrix;
462
463 // the src glyph image shouldn't be 3D
464 SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
465 glyph->toMask(&srcM);
466 fRec.getMatrixFrom2x2(&matrix);
467
468 if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {
469 int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width());
470 int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height());
471 int dstRB = origGlyph.rowBytes();
472 int srcRB = dstM.fRowBytes;
473
474 const uint8_t* src = (const uint8_t*)dstM.fImage;
475 uint8_t* dst = (uint8_t*)origGlyph.fImage;
476
477 if (SkMask::k3D_Format == dstM.fFormat) {
478 // we have to copy 3 times as much
479 height *= 3;
480 }
481
482 // clean out our glyph, since it may be larger than dstM
483 //sk_bzero(dst, height * dstRB);
484
485 while (--height >= 0) {
486 memcpy(dst, src, width);
487 src += srcRB;
488 dst += dstRB;
489 }
490 SkMask::FreeImage(dstM.fImage);
491 }
492 }
493
494 // check to see if we should filter the alpha channel
495
496 if (NULL == fMaskFilter &&
497 fRec.fMaskFormat != SkMask::kBW_Format &&
498 (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0)
499 {
500 const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGammaTable : gWhiteGammaTable;
501 if (NULL != table)
502 {
503 uint8_t* dst = (uint8_t*)origGlyph.fImage;
504 unsigned rowBytes = origGlyph.rowBytes();
505
506 for (int y = origGlyph.fHeight - 1; y >= 0; --y)
507 {
508 for (int x = origGlyph.fWidth - 1; x >= 0; --x)
509 dst[x] = table[dst[x]];
510 dst += rowBytes;
511 }
512 }
513 }
514 }
515
getPath(const SkGlyph & glyph,SkPath * path)516 void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path)
517 {
518 this->internalGetPath(glyph, NULL, path, NULL);
519 }
520
getFontMetrics(SkPaint::FontMetrics * mx,SkPaint::FontMetrics * my)521 void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetrics* my)
522 {
523 this->generateFontMetrics(mx, my);
524 }
525
generateGlyphToChar(uint16_t glyph)526 SkUnichar SkScalerContext::generateGlyphToChar(uint16_t glyph) {
527 return 0;
528 }
529
530 ///////////////////////////////////////////////////////////////////////
531
internalGetPath(const SkGlyph & glyph,SkPath * fillPath,SkPath * devPath,SkMatrix * fillToDevMatrix)532 void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix)
533 {
534 SkPath path;
535
536 this->getGlyphContext(glyph)->generatePath(glyph, &path);
537
538 if (fRec.fFrameWidth > 0 || fPathEffect != NULL)
539 {
540 // need the path in user-space, with only the point-size applied
541 // so that our stroking and effects will operate the same way they
542 // would if the user had extracted the path themself, and then
543 // called drawPath
544 SkPath localPath;
545 SkMatrix matrix, inverse;
546
547 fRec.getMatrixFrom2x2(&matrix);
548 matrix.invert(&inverse);
549 path.transform(inverse, &localPath);
550 // now localPath is only affected by the paint settings, and not the canvas matrix
551
552 SkScalar width = fRec.fFrameWidth;
553
554 if (fPathEffect)
555 {
556 SkPath effectPath;
557
558 if (fPathEffect->filterPath(&effectPath, localPath, &width))
559 localPath.swap(effectPath);
560 }
561
562 if (width > 0)
563 {
564 SkStroke stroker;
565 SkPath outline;
566
567 stroker.setWidth(width);
568 stroker.setMiterLimit(fRec.fMiterLimit);
569 stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin);
570 stroker.setDoFill(SkToBool(fRec.fFlags & kFrameAndFill_Flag));
571 stroker.strokePath(localPath, &outline);
572 localPath.swap(outline);
573 }
574
575 // now return stuff to the caller
576 if (fillToDevMatrix)
577 *fillToDevMatrix = matrix;
578
579 if (devPath)
580 localPath.transform(matrix, devPath);
581
582 if (fillPath)
583 fillPath->swap(localPath);
584 }
585 else // nothing tricky to do
586 {
587 if (fillToDevMatrix)
588 fillToDevMatrix->reset();
589
590 if (devPath)
591 {
592 if (fillPath == NULL)
593 devPath->swap(path);
594 else
595 *devPath = path;
596 }
597
598 if (fillPath)
599 fillPath->swap(path);
600 }
601
602 if (devPath)
603 devPath->updateBoundsCache();
604 if (fillPath)
605 fillPath->updateBoundsCache();
606 }
607
608
getMatrixFrom2x2(SkMatrix * dst) const609 void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const
610 {
611 dst->reset();
612 dst->setScaleX(fPost2x2[0][0]);
613 dst->setSkewX( fPost2x2[0][1]);
614 dst->setSkewY( fPost2x2[1][0]);
615 dst->setScaleY(fPost2x2[1][1]);
616 }
617
getLocalMatrix(SkMatrix * m) const618 void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const
619 {
620 m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
621 if (fPreSkewX)
622 m->postSkew(fPreSkewX, 0);
623 }
624
getSingleMatrix(SkMatrix * m) const625 void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const
626 {
627 this->getLocalMatrix(m);
628
629 // now concat the device matrix
630 {
631 SkMatrix deviceMatrix;
632 this->getMatrixFrom2x2(&deviceMatrix);
633 m->postConcat(deviceMatrix);
634 }
635 }
636
637 ///////////////////////////////////////////////////////////////////////////////
638
639 #include "SkFontHost.h"
640
641 class SkScalerContext_Empty : public SkScalerContext {
642 public:
SkScalerContext_Empty(const SkDescriptor * desc)643 SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {}
644
645 protected:
generateGlyphCount() const646 virtual unsigned generateGlyphCount() const {
647 return 0;
648 }
generateCharToGlyph(SkUnichar uni)649 virtual uint16_t generateCharToGlyph(SkUnichar uni) {
650 return 0;
651 }
generateAdvance(SkGlyph * glyph)652 virtual void generateAdvance(SkGlyph* glyph) {
653 glyph->zeroMetrics();
654 }
generateMetrics(SkGlyph * glyph)655 virtual void generateMetrics(SkGlyph* glyph) {
656 glyph->zeroMetrics();
657 }
generateImage(const SkGlyph & glyph)658 virtual void generateImage(const SkGlyph& glyph) {}
generatePath(const SkGlyph & glyph,SkPath * path)659 virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
generateFontMetrics(SkPaint::FontMetrics * mx,SkPaint::FontMetrics * my)660 virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
661 SkPaint::FontMetrics* my) {
662 if (mx) {
663 sk_bzero(mx, sizeof(*mx));
664 }
665 if (my) {
666 sk_bzero(my, sizeof(*my));
667 }
668 }
669 };
670
Create(const SkDescriptor * desc)671 SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc)
672 {
673 SkScalerContext* c = SkFontHost::CreateScalerContext(desc);
674 if (NULL == c) {
675 c = SkNEW_ARGS(SkScalerContext_Empty, (desc));
676 }
677 return c;
678 }
679
680