blob: d47c314834cef056f1d747d139d4fd792e887914 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkScalerContext.h"
agl@chromium.org309485b2009-07-21 17:41:32 +000011#include "SkColorPriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkDescriptor.h"
13#include "SkDraw.h"
14#include "SkFontHost.h"
15#include "SkMaskFilter.h"
djsollen@google.com2b2ede32012-04-12 13:24:04 +000016#include "SkOrderedReadBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkPathEffect.h"
18#include "SkRasterizer.h"
reed@google.com045e62d2011-10-24 12:19:46 +000019#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkStroke.h"
21#include "SkThread.h"
22
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#define ComputeBWRowBytes(width) (((unsigned)(width) + 7) >> 3)
24
reed@android.com8a1c16f2008-12-17 15:59:43 +000025void SkGlyph::toMask(SkMask* mask) const {
26 SkASSERT(mask);
27
28 mask->fImage = (uint8_t*)fImage;
29 mask->fBounds.set(fLeft, fTop, fLeft + fWidth, fTop + fHeight);
30 mask->fRowBytes = this->rowBytes();
reed@android.com6c14b432009-03-23 20:11:11 +000031 mask->fFormat = static_cast<SkMask::Format>(fMaskFormat);
reed@android.com8a1c16f2008-12-17 15:59:43 +000032}
33
34size_t SkGlyph::computeImageSize() const {
agl@chromium.org309485b2009-07-21 17:41:32 +000035 const size_t size = this->rowBytes() * fHeight;
36
37 switch (fMaskFormat) {
reed@google.com7db9fe62011-04-11 11:57:54 +000038 case SkMask::k3D_Format:
39 return 3 * size;
40 default:
41 return size;
reed@android.com8a1c16f2008-12-17 15:59:43 +000042 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000043}
44
reed@android.com62900b42009-02-11 15:07:19 +000045void SkGlyph::zeroMetrics() {
46 fAdvanceX = 0;
47 fAdvanceY = 0;
48 fWidth = 0;
49 fHeight = 0;
50 fTop = 0;
51 fLeft = 0;
52 fRsbDelta = 0;
53 fLsbDelta = 0;
54}
55
56///////////////////////////////////////////////////////////////////////////////
57
reed@android.com8a1c16f2008-12-17 15:59:43 +000058#ifdef SK_DEBUG
59 #define DUMP_RECx
60#endif
61
62static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) {
63 SkFlattenable* obj = NULL;
64 uint32_t len;
65 const void* data = desc->findEntry(tag, &len);
66
67 if (data) {
djsollen@google.com2b2ede32012-04-12 13:24:04 +000068 SkOrderedReadBuffer buffer(data, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 obj = buffer.readFlattenable();
70 SkASSERT(buffer.offset() == buffer.size());
71 }
72 return obj;
73}
74
75SkScalerContext::SkScalerContext(const SkDescriptor* desc)
76 : fPathEffect(NULL), fMaskFilter(NULL)
77{
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 fBaseGlyphCount = 0;
reed@android.coma14ea0e2009-03-17 17:59:53 +000079 fNextContext = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
81 const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL);
82 SkASSERT(rec);
83
84 fRec = *rec;
85
86#ifdef DUMP_REC
87 desc->assertChecksum();
reed@google.com7db9fe62011-04-11 11:57:54 +000088 SkDebugf("SkScalarContext checksum %x count %d length %d\n",
89 desc->getChecksum(), desc->getCount(), desc->getLength());
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 SkDebugf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\n",
91 rec->fTextSize, rec->fPreScaleX, rec->fPreSkewX, rec->fPost2x2[0][0],
92 rec->fPost2x2[0][1], rec->fPost2x2[1][0], rec->fPost2x2[1][1]);
93 SkDebugf(" frame %g miter %g hints %d framefill %d format %d join %d\n",
94 rec->fFrameWidth, rec->fMiterLimit, rec->fHints, rec->fFrameAndFill,
95 rec->fMaskFormat, rec->fStrokeJoin);
reed@google.com7db9fe62011-04-11 11:57:54 +000096 SkDebugf(" pathEffect %x maskFilter %x\n",
97 desc->findEntry(kPathEffect_SkDescriptorTag, NULL),
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 desc->findEntry(kMaskFilter_SkDescriptorTag, NULL));
99#endif
100
101 fPathEffect = (SkPathEffect*)load_flattenable(desc, kPathEffect_SkDescriptorTag);
102 fMaskFilter = (SkMaskFilter*)load_flattenable(desc, kMaskFilter_SkDescriptorTag);
103 fRasterizer = (SkRasterizer*)load_flattenable(desc, kRasterizer_SkDescriptorTag);
reed@google.coma767fa02011-08-05 21:40:26 +0000104
105 // initialize based on our settings. subclasses can also force this
106 fGenerateImageFromPath = fRec.fFrameWidth > 0 || fPathEffect != NULL ||
107 fRasterizer != NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108}
109
110SkScalerContext::~SkScalerContext() {
reed@android.coma14ea0e2009-03-17 17:59:53 +0000111 SkDELETE(fNextContext);
112
reed@google.com82065d62011-02-07 15:30:46 +0000113 SkSafeUnref(fPathEffect);
114 SkSafeUnref(fMaskFilter);
115 SkSafeUnref(fRasterizer);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116}
117
reed@android.coma14ea0e2009-03-17 17:59:53 +0000118static SkScalerContext* allocNextContext(const SkScalerContext::Rec& rec) {
119 // fonthost will determine the next possible font to search, based
120 // on the current font in fRec. It will return NULL if ctx is our
121 // last font that can be searched (i.e. ultimate fallback font)
reed@google.com7d26c592011-06-13 13:01:10 +0000122 uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID, rec.fOrigFontID);
reed@android.coma14ea0e2009-03-17 17:59:53 +0000123 if (0 == newFontID) {
124 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 }
reed@android.coma14ea0e2009-03-17 17:59:53 +0000126
127 SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
128 SkDescriptor* desc = ad.getDesc();
129
130 desc->init();
131 SkScalerContext::Rec* newRec =
132 (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
133 sizeof(rec), &rec);
134 newRec->fFontID = newFontID;
135 desc->computeChecksum();
136
137 return SkFontHost::CreateScalerContext(desc);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138}
139
reed@android.coma14ea0e2009-03-17 17:59:53 +0000140/* Return the next context, creating it if its not already created, but return
141 NULL if the fonthost says there are no more fonts to fallback to.
142 */
143SkScalerContext* SkScalerContext::getNextContext() {
144 SkScalerContext* next = fNextContext;
145 // if next is null, then either it isn't cached yet, or we're at the
146 // end of our possible chain
147 if (NULL == next) {
148 next = allocNextContext(fRec);
149 if (NULL == next) {
150 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 }
reed@android.coma14ea0e2009-03-17 17:59:53 +0000152 // next's base is our base + our local count
153 next->setBaseGlyphCount(fBaseGlyphCount + this->getGlyphCount());
154 // cache the answer
155 fNextContext = next;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 }
reed@android.coma14ea0e2009-03-17 17:59:53 +0000157 return next;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158}
159
reed@android.coma14ea0e2009-03-17 17:59:53 +0000160SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
161 unsigned glyphID = glyph.getGlyphID();
162 SkScalerContext* ctx = this;
163 for (;;) {
164 unsigned count = ctx->getGlyphCount();
165 if (glyphID < count) {
166 break;
167 }
168 glyphID -= count;
169 ctx = ctx->getNextContext();
170 if (NULL == ctx) {
171 SkDebugf("--- no context for glyph %x\n", glyph.getGlyphID());
172 // just return the original context (this)
173 return this;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 }
175 }
176 return ctx;
177}
178
djsollen@google.com60abb072012-02-15 18:49:15 +0000179#ifdef SK_BUILD_FOR_ANDROID
180/* This loops through all available fallback contexts (if needed) until it
181 finds some context that can handle the unichar and return it.
182
183 As this is somewhat expensive operation, it should only be done on the first
184 char of a run.
185 */
186unsigned SkScalerContext::getBaseGlyphCount(SkUnichar uni) {
187 SkScalerContext* ctx = this;
188 unsigned glyphID;
189 for (;;) {
190 glyphID = ctx->generateCharToGlyph(uni);
191 if (glyphID) {
192 break; // found it
193 }
194 ctx = ctx->getNextContext();
195 if (NULL == ctx) {
196 SkDebugf("--- no context for char %x\n", uni);
197 // just return the original context (this)
198 return this->fBaseGlyphCount;
199 }
200 }
201 return ctx->fBaseGlyphCount;
202}
203#endif
204
reed@android.coma14ea0e2009-03-17 17:59:53 +0000205/* This loops through all available fallback contexts (if needed) until it
206 finds some context that can handle the unichar. If all fail, returns 0
207 */
208uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
209 SkScalerContext* ctx = this;
210 unsigned glyphID;
211 for (;;) {
212 glyphID = ctx->generateCharToGlyph(uni);
213 if (glyphID) {
214 break; // found it
215 }
216 ctx = ctx->getNextContext();
217 if (NULL == ctx) {
218 return 0; // no more contexts, return missing glyph
219 }
220 }
221 // add the ctx's base, making glyphID unique for chain of contexts
222 glyphID += ctx->fBaseGlyphCount;
223 // check for overflow of 16bits, since our glyphID cannot exceed that
224 if (glyphID > 0xFFFF) {
225 glyphID = 0;
226 }
227 return SkToU16(glyphID);
228}
229
reed@android.com9d3a9852010-01-08 14:07:42 +0000230SkUnichar SkScalerContext::glyphIDToChar(uint16_t glyphID) {
231 SkScalerContext* ctx = this;
232 unsigned rangeEnd = 0;
233 do {
234 unsigned rangeStart = rangeEnd;
235
236 rangeEnd += ctx->getGlyphCount();
237 if (rangeStart <= glyphID && glyphID < rangeEnd) {
238 return ctx->generateGlyphToChar(glyphID - rangeStart);
239 }
240 ctx = ctx->getNextContext();
241 } while (NULL != ctx);
242 return 0;
243}
244
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245void SkScalerContext::getAdvance(SkGlyph* glyph) {
246 // mark us as just having a valid advance
247 glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE;
248 // we mark the format before making the call, in case the impl
249 // internally ends up calling its generateMetrics, which is OK
250 // albeit slower than strictly necessary
251 this->getGlyphContext(*glyph)->generateAdvance(glyph);
252}
253
254void SkScalerContext::getMetrics(SkGlyph* glyph) {
255 this->getGlyphContext(*glyph)->generateMetrics(glyph);
256
257 // for now we have separate cache entries for devkerning on and off
258 // in the future we might share caches, but make our measure/draw
259 // code make the distinction. Thus we zap the values if the caller
260 // has not asked for them.
261 if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) {
262 // no devkern, so zap the fields
263 glyph->fLsbDelta = glyph->fRsbDelta = 0;
264 }
265
266 // if either dimension is empty, zap the image bounds of the glyph
267 if (0 == glyph->fWidth || 0 == glyph->fHeight) {
268 glyph->fWidth = 0;
269 glyph->fHeight = 0;
270 glyph->fTop = 0;
271 glyph->fLeft = 0;
272 glyph->fMaskFormat = 0;
273 return;
274 }
reed@google.com82065d62011-02-07 15:30:46 +0000275
reed@google.coma767fa02011-08-05 21:40:26 +0000276 if (fGenerateImageFromPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 SkPath devPath, fillPath;
278 SkMatrix fillToDevMatrix;
279
280 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
281
282 if (fRasterizer) {
283 SkMask mask;
284
285 if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
286 fMaskFilter, &mask,
287 SkMask::kJustComputeBounds_CreateMode)) {
288 glyph->fLeft = mask.fBounds.fLeft;
289 glyph->fTop = mask.fBounds.fTop;
290 glyph->fWidth = SkToU16(mask.fBounds.width());
291 glyph->fHeight = SkToU16(mask.fBounds.height());
292 } else {
senorblanco@chromium.org7767cd82009-11-25 17:14:32 +0000293 goto SK_ERROR;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 }
295 } else {
296 // just use devPath
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 SkIRect ir;
reed@android.comd252db02009-04-01 18:31:44 +0000298 devPath.getBounds().roundOut(&ir);
reed@google.com82065d62011-02-07 15:30:46 +0000299
reed@android.comb757f8d2009-12-08 22:02:26 +0000300 if (ir.isEmpty() || !ir.is16Bit()) {
senorblanco@chromium.org7767cd82009-11-25 17:14:32 +0000301 goto SK_ERROR;
reed@android.comd4577752009-11-21 02:48:11 +0000302 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 glyph->fLeft = ir.fLeft;
304 glyph->fTop = ir.fTop;
305 glyph->fWidth = SkToU16(ir.width());
306 glyph->fHeight = SkToU16(ir.height());
307 }
308 }
309
reed@android.comf2b98d62010-12-20 18:26:13 +0000310 if (SkMask::kARGB32_Format != glyph->fMaskFormat) {
311 glyph->fMaskFormat = fRec.fMaskFormat;
312 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313
314 if (fMaskFilter) {
315 SkMask src, dst;
316 SkMatrix matrix;
317
318 glyph->toMask(&src);
319 fRec.getMatrixFrom2x2(&matrix);
320
321 src.fImage = NULL; // only want the bounds from the filter
322 if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) {
323 SkASSERT(dst.fImage == NULL);
324 glyph->fLeft = dst.fBounds.fLeft;
325 glyph->fTop = dst.fBounds.fTop;
326 glyph->fWidth = SkToU16(dst.fBounds.width());
327 glyph->fHeight = SkToU16(dst.fBounds.height());
328 glyph->fMaskFormat = dst.fFormat;
329 }
330 }
reed@android.comd4577752009-11-21 02:48:11 +0000331 return;
reed@google.com82065d62011-02-07 15:30:46 +0000332
senorblanco@chromium.org7767cd82009-11-25 17:14:32 +0000333SK_ERROR:
reed@android.comd4577752009-11-21 02:48:11 +0000334 // draw nothing 'cause we failed
335 glyph->fLeft = 0;
336 glyph->fTop = 0;
337 glyph->fWidth = 0;
338 glyph->fHeight = 0;
reed@android.comb757f8d2009-12-08 22:02:26 +0000339 // put a valid value here, in case it was earlier set to
340 // MASK_FORMAT_JUST_ADVANCE
341 glyph->fMaskFormat = fRec.fMaskFormat;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342}
343
reed@google.coma767fa02011-08-05 21:40:26 +0000344static bool isLCD(const SkScalerContext::Rec& rec) {
345 return SkMask::kLCD16_Format == rec.fMaskFormat ||
346 SkMask::kLCD32_Format == rec.fMaskFormat;
347}
348
349static uint16_t a8_to_rgb565(unsigned a8) {
350 return SkPackRGB16(a8 >> 3, a8 >> 2, a8 >> 3);
351}
352
reed@google.comcf598b12011-08-22 21:13:23 +0000353static void copyToLCD16(const SkBitmap& src, const SkMask& dst) {
reed@google.coma767fa02011-08-05 21:40:26 +0000354 SkASSERT(SkBitmap::kA8_Config == src.config());
reed@google.comcf598b12011-08-22 21:13:23 +0000355 SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
reed@google.coma767fa02011-08-05 21:40:26 +0000356
reed@google.comcf598b12011-08-22 21:13:23 +0000357 const int width = dst.fBounds.width();
358 const int height = dst.fBounds.height();
reed@google.coma767fa02011-08-05 21:40:26 +0000359 const uint8_t* srcP = src.getAddr8(0, 0);
360 size_t srcRB = src.rowBytes();
361 uint16_t* dstP = (uint16_t*)dst.fImage;
reed@google.comcf598b12011-08-22 21:13:23 +0000362 size_t dstRB = dst.fRowBytes;
363 for (int y = 0; y < height; ++y) {
364 for (int x = 0; x < width; ++x) {
reed@google.coma767fa02011-08-05 21:40:26 +0000365 dstP[x] = a8_to_rgb565(srcP[x]);
366 }
367 srcP += srcRB;
368 dstP = (uint16_t*)((char*)dstP + dstRB);
369 }
370}
371
reed@google.comcf598b12011-08-22 21:13:23 +0000372#define SK_FREETYPE_LCD_LERP 160
reed@google.coma767fa02011-08-05 21:40:26 +0000373
reed@google.comcf598b12011-08-22 21:13:23 +0000374static int lerp(int start, int end) {
375 SkASSERT((unsigned)SK_FREETYPE_LCD_LERP <= 256);
376 return start + ((end - start) * (SK_FREETYPE_LCD_LERP) >> 8);
377}
378
379static uint16_t packLCD16(unsigned r, unsigned g, unsigned b) {
380 if (SK_FREETYPE_LCD_LERP) {
381 // want (a+b+c)/3, but we approx to avoid the divide
382 unsigned ave = (5 * (r + g + b) + g) >> 4;
383 r = lerp(r, ave);
384 g = lerp(g, ave);
385 b = lerp(b, ave);
386 }
387 return SkPackRGB16(r >> 3, g >> 2, b >> 3);
388}
389
390static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst) {
391 SkASSERT(SkBitmap::kA8_Config == src.config());
392 SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
393
394 const int width = dst.fBounds.width();
395 const int height = dst.fBounds.height();
396 uint16_t* dstP = (uint16_t*)dst.fImage;
397 size_t dstRB = dst.fRowBytes;
398 for (int y = 0; y < height; ++y) {
399 const uint8_t* srcP = src.getAddr8(0, y);
400 for (int x = 0; x < width; ++x) {
401 unsigned r = *srcP++;
402 unsigned g = *srcP++;
403 unsigned b = *srcP++;
404 dstP[x] = packLCD16(r, g, b);
reed@google.coma767fa02011-08-05 21:40:26 +0000405 }
reed@google.comcf598b12011-08-22 21:13:23 +0000406 dstP = (uint16_t*)((char*)dstP + dstRB);
407 }
408}
409
410static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst) {
411 SkASSERT(SkBitmap::kA8_Config == src.config());
412 SkASSERT(SkMask::kLCD32_Format == dst.fFormat);
413
414 const int width = dst.fBounds.width();
415 const int height = dst.fBounds.height();
416 SkPMColor* dstP = (SkPMColor*)dst.fImage;
417 size_t dstRB = dst.fRowBytes;
418 for (int y = 0; y < height; ++y) {
419 const uint8_t* srcP = src.getAddr8(0, y);
420 for (int x = 0; x < width; ++x) {
421 unsigned r = *srcP++;
422 unsigned g = *srcP++;
423 unsigned b = *srcP++;
424 unsigned a = SkMax32(SkMax32(r, g), b);
425 dstP[x] = SkPackARGB32(a, r, g, b);
426 }
reed@google.coma767fa02011-08-05 21:40:26 +0000427 dstP = (SkPMColor*)((char*)dstP + dstRB);
428 }
429}
430
reed@google.comcf598b12011-08-22 21:13:23 +0000431static void generateMask(const SkMask& mask, const SkPath& path) {
432 SkBitmap::Config config;
433 SkPaint paint;
434
435 int srcW = mask.fBounds.width();
436 int srcH = mask.fBounds.height();
437 int dstW = srcW;
438 int dstH = srcH;
439 int dstRB = mask.fRowBytes;
440
441 SkMatrix matrix;
442 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
443 -SkIntToScalar(mask.fBounds.fTop));
444
445 if (SkMask::kBW_Format == mask.fFormat) {
446 config = SkBitmap::kA1_Config;
447 paint.setAntiAlias(false);
448 } else {
449 config = SkBitmap::kA8_Config;
450 paint.setAntiAlias(true);
451 switch (mask.fFormat) {
452 case SkMask::kA8_Format:
453 break;
454 case SkMask::kLCD16_Format:
455 case SkMask::kLCD32_Format:
456 // TODO: trigger off LCD orientation
457 dstW *= 3;
458 matrix.postScale(SkIntToScalar(3), SK_Scalar1);
459 dstRB = 0; // signals we need a copy
460 break;
461 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000462 SkDEBUGFAIL("unexpected mask format");
reed@google.comcf598b12011-08-22 21:13:23 +0000463 }
464 }
465
reed@google.com045e62d2011-10-24 12:19:46 +0000466 SkRasterClip clip;
467 clip.setRect(SkIRect::MakeWH(dstW, dstH));
reed@google.comcf598b12011-08-22 21:13:23 +0000468
469 SkBitmap bm;
470 bm.setConfig(config, dstW, dstH, dstRB);
471
472 if (0 == dstRB) {
473 bm.allocPixels();
474 bm.lockPixels();
475 } else {
476 bm.setPixels(mask.fImage);
477 }
478 sk_bzero(bm.getPixels(), bm.getSafeSize());
479
480 SkDraw draw;
481 sk_bzero(&draw, sizeof(draw));
reed@google.com045e62d2011-10-24 12:19:46 +0000482 draw.fRC = &clip;
483 draw.fClip = &clip.bwRgn();
reed@google.comcf598b12011-08-22 21:13:23 +0000484 draw.fMatrix = &matrix;
485 draw.fBitmap = &bm;
486 draw.drawPath(path, paint);
487
488 if (0 == dstRB) {
489 switch (mask.fFormat) {
490 case SkMask::kLCD16_Format:
491 pack3xHToLCD16(bm, mask);
492 break;
493 case SkMask::kLCD32_Format:
494 pack3xHToLCD32(bm, mask);
495 break;
496 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000497 SkDEBUGFAIL("bad format for copyback");
reed@google.comcf598b12011-08-22 21:13:23 +0000498 }
499 }
500}
501
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502void SkScalerContext::getImage(const SkGlyph& origGlyph) {
503 const SkGlyph* glyph = &origGlyph;
504 SkGlyph tmpGlyph;
505
506 if (fMaskFilter) { // restore the prefilter bounds
reed@google.comce2b1af2011-02-18 13:00:40 +0000507 tmpGlyph.init(origGlyph.fID);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508
509 // need the original bounds, sans our maskfilter
510 SkMaskFilter* mf = fMaskFilter;
511 fMaskFilter = NULL; // temp disable
512 this->getMetrics(&tmpGlyph);
513 fMaskFilter = mf; // restore
514
515 tmpGlyph.fImage = origGlyph.fImage;
516
517 // we need the prefilter bounds to be <= filter bounds
518 SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
519 SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
520 glyph = &tmpGlyph;
521 }
522
reed@google.coma767fa02011-08-05 21:40:26 +0000523 if (fGenerateImageFromPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 SkPath devPath, fillPath;
525 SkMatrix fillToDevMatrix;
reed@google.comcf598b12011-08-22 21:13:23 +0000526 SkMask mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527
528 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
reed@google.comcf598b12011-08-22 21:13:23 +0000529 glyph->toMask(&mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530
531 if (fRasterizer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 mask.fFormat = SkMask::kA8_Format;
reed@android.com4516f472009-06-29 16:25:36 +0000533 sk_bzero(glyph->fImage, mask.computeImageSize());
reed@google.com82065d62011-02-07 15:30:46 +0000534
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
536 fMaskFilter, &mask,
537 SkMask::kJustRenderImage_CreateMode)) {
538 return;
539 }
540 } else {
reed@google.comcf598b12011-08-22 21:13:23 +0000541 generateMask(mask, devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 }
543 } else {
544 this->getGlyphContext(*glyph)->generateImage(*glyph);
545 }
546
547 if (fMaskFilter) {
548 SkMask srcM, dstM;
549 SkMatrix matrix;
550
551 // the src glyph image shouldn't be 3D
552 SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
553 glyph->toMask(&srcM);
554 fRec.getMatrixFrom2x2(&matrix);
555
556 if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {
557 int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width());
558 int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height());
559 int dstRB = origGlyph.rowBytes();
560 int srcRB = dstM.fRowBytes;
reed@google.com82065d62011-02-07 15:30:46 +0000561
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 const uint8_t* src = (const uint8_t*)dstM.fImage;
563 uint8_t* dst = (uint8_t*)origGlyph.fImage;
564
565 if (SkMask::k3D_Format == dstM.fFormat) {
566 // we have to copy 3 times as much
567 height *= 3;
568 }
569
570 // clean out our glyph, since it may be larger than dstM
reed@android.com4516f472009-06-29 16:25:36 +0000571 //sk_bzero(dst, height * dstRB);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000572
573 while (--height >= 0) {
574 memcpy(dst, src, width);
575 src += srcRB;
576 dst += dstRB;
577 }
578 SkMask::FreeImage(dstM.fImage);
579 }
580 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581}
582
reed@google.com7db9fe62011-04-11 11:57:54 +0000583void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 this->internalGetPath(glyph, NULL, path, NULL);
585}
586
reed@google.com7db9fe62011-04-11 11:57:54 +0000587void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx,
588 SkPaint::FontMetrics* my) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 this->generateFontMetrics(mx, my);
590}
591
reed@android.com9d3a9852010-01-08 14:07:42 +0000592SkUnichar SkScalerContext::generateGlyphToChar(uint16_t glyph) {
593 return 0;
594}
595
reed@google.com7db9fe62011-04-11 11:57:54 +0000596///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597
reed@google.com7db9fe62011-04-11 11:57:54 +0000598void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
599 SkPath* devPath, SkMatrix* fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 SkPath path;
601
602 this->getGlyphContext(glyph)->generatePath(glyph, &path);
reed@google.com82065d62011-02-07 15:30:46 +0000603
reed@google.comcf598b12011-08-22 21:13:23 +0000604 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
605 SkFixed dx = glyph.getSubXFixed();
606 SkFixed dy = glyph.getSubYFixed();
607 if (dx | dy) {
608 path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy));
609 }
610 }
611
reed@google.com7db9fe62011-04-11 11:57:54 +0000612 if (fRec.fFrameWidth > 0 || fPathEffect != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 // need the path in user-space, with only the point-size applied
614 // so that our stroking and effects will operate the same way they
615 // would if the user had extracted the path themself, and then
616 // called drawPath
617 SkPath localPath;
618 SkMatrix matrix, inverse;
619
620 fRec.getMatrixFrom2x2(&matrix);
bungeman@google.com9575fb82012-04-09 20:49:03 +0000621 if (!matrix.invert(&inverse)) {
622 // assume fillPath and devPath are already empty.
623 return;
624 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 path.transform(inverse, &localPath);
626 // now localPath is only affected by the paint settings, and not the canvas matrix
627
reed@google.com97972722012-05-24 20:13:57 +0000628 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
629
630 if (fRec.fFrameWidth > 0) {
631 rec.setStrokeStyle(fRec.fFrameWidth,
632 SkToBool(fRec.fFlags & kFrameAndFill_Flag));
633 // glyphs are always closed contours, so cap type is ignored,
634 // so we just pass something.
635 rec.setStrokeParams(SkPaint::kButt_Cap,
636 (SkPaint::Join)fRec.fStrokeJoin,
637 fRec.fMiterLimit);
638 }
639
reed@google.com7db9fe62011-04-11 11:57:54 +0000640 if (fPathEffect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 SkPath effectPath;
reed@google.com97972722012-05-24 20:13:57 +0000642 if (fPathEffect->filterPath(&effectPath, localPath, &rec)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000643 localPath.swap(effectPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000644 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 }
646
reed@google.com97972722012-05-24 20:13:57 +0000647 if (rec.needToApply()) {
648 SkPath strokePath;
649 if (rec.applyToPath(&strokePath, localPath)) {
650 localPath.swap(strokePath);
651 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 }
reed@google.com82065d62011-02-07 15:30:46 +0000653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 // now return stuff to the caller
reed@google.com7db9fe62011-04-11 11:57:54 +0000655 if (fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 *fillToDevMatrix = matrix;
reed@google.com7db9fe62011-04-11 11:57:54 +0000657 }
658 if (devPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 localPath.transform(matrix, devPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000660 }
661 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 fillPath->swap(localPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000663 }
664 } else { // nothing tricky to do
665 if (fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 fillToDevMatrix->reset();
reed@google.com7db9fe62011-04-11 11:57:54 +0000667 }
668 if (devPath) {
669 if (fillPath == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 devPath->swap(path);
reed@google.com7db9fe62011-04-11 11:57:54 +0000671 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672 *devPath = path;
reed@google.com7db9fe62011-04-11 11:57:54 +0000673 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 }
reed@google.com82065d62011-02-07 15:30:46 +0000675
reed@google.com7db9fe62011-04-11 11:57:54 +0000676 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 fillPath->swap(path);
reed@google.com7db9fe62011-04-11 11:57:54 +0000678 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 }
reed@google.com82065d62011-02-07 15:30:46 +0000680
reed@google.com7db9fe62011-04-11 11:57:54 +0000681 if (devPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 devPath->updateBoundsCache();
reed@google.com7db9fe62011-04-11 11:57:54 +0000683 }
684 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685 fillPath->updateBoundsCache();
reed@google.com7db9fe62011-04-11 11:57:54 +0000686 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
689
reed@google.com7db9fe62011-04-11 11:57:54 +0000690void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691 dst->reset();
692 dst->setScaleX(fPost2x2[0][0]);
693 dst->setSkewX( fPost2x2[0][1]);
694 dst->setSkewY( fPost2x2[1][0]);
695 dst->setScaleY(fPost2x2[1][1]);
696}
697
reed@google.com7db9fe62011-04-11 11:57:54 +0000698void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
reed@google.com7db9fe62011-04-11 11:57:54 +0000700 if (fPreSkewX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 m->postSkew(fPreSkewX, 0);
reed@google.com7db9fe62011-04-11 11:57:54 +0000702 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703}
704
reed@google.com7db9fe62011-04-11 11:57:54 +0000705void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 this->getLocalMatrix(m);
707
708 // now concat the device matrix
reed@google.com7db9fe62011-04-11 11:57:54 +0000709 SkMatrix deviceMatrix;
710 this->getMatrixFrom2x2(&deviceMatrix);
711 m->postConcat(deviceMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712}
713
reed@google.com2e684782011-08-24 15:38:46 +0000714SkAxisAlignment SkComputeAxisAlignmentForHText(const SkMatrix& matrix) {
715 SkASSERT(!matrix.hasPerspective());
716
717 if (0 == matrix[SkMatrix::kMSkewY]) {
718 return kX_SkAxisAlignment;
719 }
720 if (0 == matrix[SkMatrix::kMScaleX]) {
721 return kY_SkAxisAlignment;
722 }
723 return kNone_SkAxisAlignment;
724}
725
reed@android.com62900b42009-02-11 15:07:19 +0000726///////////////////////////////////////////////////////////////////////////////
727
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728#include "SkFontHost.h"
729
reed@android.com62900b42009-02-11 15:07:19 +0000730class SkScalerContext_Empty : public SkScalerContext {
731public:
732 SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {}
733
734protected:
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000735 virtual unsigned generateGlyphCount() {
reed@android.com62900b42009-02-11 15:07:19 +0000736 return 0;
737 }
738 virtual uint16_t generateCharToGlyph(SkUnichar uni) {
739 return 0;
740 }
741 virtual void generateAdvance(SkGlyph* glyph) {
742 glyph->zeroMetrics();
743 }
744 virtual void generateMetrics(SkGlyph* glyph) {
745 glyph->zeroMetrics();
746 }
747 virtual void generateImage(const SkGlyph& glyph) {}
748 virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
749 virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
750 SkPaint::FontMetrics* my) {
751 if (mx) {
reed@android.com4516f472009-06-29 16:25:36 +0000752 sk_bzero(mx, sizeof(*mx));
reed@android.com62900b42009-02-11 15:07:19 +0000753 }
754 if (my) {
reed@android.com4516f472009-06-29 16:25:36 +0000755 sk_bzero(my, sizeof(*my));
reed@android.com62900b42009-02-11 15:07:19 +0000756 }
757 }
758};
759
reed@android.comf2b98d62010-12-20 18:26:13 +0000760extern SkScalerContext* SkCreateColorScalerContext(const SkDescriptor* desc);
761
reed@google.com7db9fe62011-04-11 11:57:54 +0000762SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000763 SkScalerContext* c = NULL; //SkCreateColorScalerContext(desc);
764 if (NULL == c) {
765 c = SkFontHost::CreateScalerContext(desc);
766 }
reed@android.com62900b42009-02-11 15:07:19 +0000767 if (NULL == c) {
768 c = SkNEW_ARGS(SkScalerContext_Empty, (desc));
769 }
770 return c;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771}
772