blob: 2cce8ab1c670ed0ef89f527999d5166124d97af3 [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
caryclark@google.com803eceb2012-06-06 12:09:34 +0000344#if 0 // UNUSED
reed@google.coma767fa02011-08-05 21:40:26 +0000345static bool isLCD(const SkScalerContext::Rec& rec) {
346 return SkMask::kLCD16_Format == rec.fMaskFormat ||
347 SkMask::kLCD32_Format == rec.fMaskFormat;
348}
caryclark@google.com803eceb2012-06-06 12:09:34 +0000349#endif
reed@google.coma767fa02011-08-05 21:40:26 +0000350
caryclark@google.com803eceb2012-06-06 12:09:34 +0000351#if 0 // UNUSED
reed@google.coma767fa02011-08-05 21:40:26 +0000352static uint16_t a8_to_rgb565(unsigned a8) {
353 return SkPackRGB16(a8 >> 3, a8 >> 2, a8 >> 3);
354}
355
reed@google.comcf598b12011-08-22 21:13:23 +0000356static void copyToLCD16(const SkBitmap& src, const SkMask& dst) {
reed@google.coma767fa02011-08-05 21:40:26 +0000357 SkASSERT(SkBitmap::kA8_Config == src.config());
reed@google.comcf598b12011-08-22 21:13:23 +0000358 SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
reed@google.coma767fa02011-08-05 21:40:26 +0000359
reed@google.comcf598b12011-08-22 21:13:23 +0000360 const int width = dst.fBounds.width();
361 const int height = dst.fBounds.height();
reed@google.coma767fa02011-08-05 21:40:26 +0000362 const uint8_t* srcP = src.getAddr8(0, 0);
363 size_t srcRB = src.rowBytes();
364 uint16_t* dstP = (uint16_t*)dst.fImage;
reed@google.comcf598b12011-08-22 21:13:23 +0000365 size_t dstRB = dst.fRowBytes;
366 for (int y = 0; y < height; ++y) {
367 for (int x = 0; x < width; ++x) {
reed@google.coma767fa02011-08-05 21:40:26 +0000368 dstP[x] = a8_to_rgb565(srcP[x]);
369 }
370 srcP += srcRB;
371 dstP = (uint16_t*)((char*)dstP + dstRB);
372 }
373}
caryclark@google.com803eceb2012-06-06 12:09:34 +0000374#endif
reed@google.coma767fa02011-08-05 21:40:26 +0000375
reed@google.comcf598b12011-08-22 21:13:23 +0000376#define SK_FREETYPE_LCD_LERP 160
reed@google.coma767fa02011-08-05 21:40:26 +0000377
reed@google.comcf598b12011-08-22 21:13:23 +0000378static int lerp(int start, int end) {
379 SkASSERT((unsigned)SK_FREETYPE_LCD_LERP <= 256);
380 return start + ((end - start) * (SK_FREETYPE_LCD_LERP) >> 8);
381}
382
383static uint16_t packLCD16(unsigned r, unsigned g, unsigned b) {
384 if (SK_FREETYPE_LCD_LERP) {
385 // want (a+b+c)/3, but we approx to avoid the divide
386 unsigned ave = (5 * (r + g + b) + g) >> 4;
387 r = lerp(r, ave);
388 g = lerp(g, ave);
389 b = lerp(b, ave);
390 }
391 return SkPackRGB16(r >> 3, g >> 2, b >> 3);
392}
393
394static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst) {
395 SkASSERT(SkBitmap::kA8_Config == src.config());
396 SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
397
398 const int width = dst.fBounds.width();
399 const int height = dst.fBounds.height();
400 uint16_t* dstP = (uint16_t*)dst.fImage;
401 size_t dstRB = dst.fRowBytes;
402 for (int y = 0; y < height; ++y) {
403 const uint8_t* srcP = src.getAddr8(0, y);
404 for (int x = 0; x < width; ++x) {
405 unsigned r = *srcP++;
406 unsigned g = *srcP++;
407 unsigned b = *srcP++;
408 dstP[x] = packLCD16(r, g, b);
reed@google.coma767fa02011-08-05 21:40:26 +0000409 }
reed@google.comcf598b12011-08-22 21:13:23 +0000410 dstP = (uint16_t*)((char*)dstP + dstRB);
411 }
412}
413
414static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst) {
415 SkASSERT(SkBitmap::kA8_Config == src.config());
416 SkASSERT(SkMask::kLCD32_Format == dst.fFormat);
417
418 const int width = dst.fBounds.width();
419 const int height = dst.fBounds.height();
420 SkPMColor* dstP = (SkPMColor*)dst.fImage;
421 size_t dstRB = dst.fRowBytes;
422 for (int y = 0; y < height; ++y) {
423 const uint8_t* srcP = src.getAddr8(0, y);
424 for (int x = 0; x < width; ++x) {
425 unsigned r = *srcP++;
426 unsigned g = *srcP++;
427 unsigned b = *srcP++;
428 unsigned a = SkMax32(SkMax32(r, g), b);
429 dstP[x] = SkPackARGB32(a, r, g, b);
430 }
reed@google.coma767fa02011-08-05 21:40:26 +0000431 dstP = (SkPMColor*)((char*)dstP + dstRB);
432 }
433}
434
reed@google.comcf598b12011-08-22 21:13:23 +0000435static void generateMask(const SkMask& mask, const SkPath& path) {
436 SkBitmap::Config config;
437 SkPaint paint;
438
439 int srcW = mask.fBounds.width();
440 int srcH = mask.fBounds.height();
441 int dstW = srcW;
442 int dstH = srcH;
443 int dstRB = mask.fRowBytes;
444
445 SkMatrix matrix;
446 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
447 -SkIntToScalar(mask.fBounds.fTop));
448
449 if (SkMask::kBW_Format == mask.fFormat) {
450 config = SkBitmap::kA1_Config;
451 paint.setAntiAlias(false);
452 } else {
453 config = SkBitmap::kA8_Config;
454 paint.setAntiAlias(true);
455 switch (mask.fFormat) {
456 case SkMask::kA8_Format:
457 break;
458 case SkMask::kLCD16_Format:
459 case SkMask::kLCD32_Format:
460 // TODO: trigger off LCD orientation
461 dstW *= 3;
462 matrix.postScale(SkIntToScalar(3), SK_Scalar1);
463 dstRB = 0; // signals we need a copy
464 break;
465 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000466 SkDEBUGFAIL("unexpected mask format");
reed@google.comcf598b12011-08-22 21:13:23 +0000467 }
468 }
469
reed@google.com045e62d2011-10-24 12:19:46 +0000470 SkRasterClip clip;
471 clip.setRect(SkIRect::MakeWH(dstW, dstH));
reed@google.comcf598b12011-08-22 21:13:23 +0000472
473 SkBitmap bm;
474 bm.setConfig(config, dstW, dstH, dstRB);
475
476 if (0 == dstRB) {
477 bm.allocPixels();
478 bm.lockPixels();
479 } else {
480 bm.setPixels(mask.fImage);
481 }
482 sk_bzero(bm.getPixels(), bm.getSafeSize());
483
484 SkDraw draw;
485 sk_bzero(&draw, sizeof(draw));
reed@google.com045e62d2011-10-24 12:19:46 +0000486 draw.fRC = &clip;
487 draw.fClip = &clip.bwRgn();
reed@google.comcf598b12011-08-22 21:13:23 +0000488 draw.fMatrix = &matrix;
489 draw.fBitmap = &bm;
490 draw.drawPath(path, paint);
491
492 if (0 == dstRB) {
493 switch (mask.fFormat) {
494 case SkMask::kLCD16_Format:
495 pack3xHToLCD16(bm, mask);
496 break;
497 case SkMask::kLCD32_Format:
498 pack3xHToLCD32(bm, mask);
499 break;
500 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000501 SkDEBUGFAIL("bad format for copyback");
reed@google.comcf598b12011-08-22 21:13:23 +0000502 }
503 }
504}
505
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506void SkScalerContext::getImage(const SkGlyph& origGlyph) {
507 const SkGlyph* glyph = &origGlyph;
508 SkGlyph tmpGlyph;
509
510 if (fMaskFilter) { // restore the prefilter bounds
reed@google.comce2b1af2011-02-18 13:00:40 +0000511 tmpGlyph.init(origGlyph.fID);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512
513 // need the original bounds, sans our maskfilter
514 SkMaskFilter* mf = fMaskFilter;
515 fMaskFilter = NULL; // temp disable
516 this->getMetrics(&tmpGlyph);
517 fMaskFilter = mf; // restore
518
519 tmpGlyph.fImage = origGlyph.fImage;
520
521 // we need the prefilter bounds to be <= filter bounds
522 SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
523 SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
524 glyph = &tmpGlyph;
525 }
526
reed@google.coma767fa02011-08-05 21:40:26 +0000527 if (fGenerateImageFromPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 SkPath devPath, fillPath;
529 SkMatrix fillToDevMatrix;
reed@google.comcf598b12011-08-22 21:13:23 +0000530 SkMask mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531
532 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
reed@google.comcf598b12011-08-22 21:13:23 +0000533 glyph->toMask(&mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534
535 if (fRasterizer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 mask.fFormat = SkMask::kA8_Format;
reed@android.com4516f472009-06-29 16:25:36 +0000537 sk_bzero(glyph->fImage, mask.computeImageSize());
reed@google.com82065d62011-02-07 15:30:46 +0000538
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
540 fMaskFilter, &mask,
541 SkMask::kJustRenderImage_CreateMode)) {
542 return;
543 }
544 } else {
reed@google.comcf598b12011-08-22 21:13:23 +0000545 generateMask(mask, devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 }
547 } else {
548 this->getGlyphContext(*glyph)->generateImage(*glyph);
549 }
550
551 if (fMaskFilter) {
552 SkMask srcM, dstM;
553 SkMatrix matrix;
554
555 // the src glyph image shouldn't be 3D
556 SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
557 glyph->toMask(&srcM);
558 fRec.getMatrixFrom2x2(&matrix);
559
560 if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {
561 int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width());
562 int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height());
563 int dstRB = origGlyph.rowBytes();
564 int srcRB = dstM.fRowBytes;
reed@google.com82065d62011-02-07 15:30:46 +0000565
reed@android.com8a1c16f2008-12-17 15:59:43 +0000566 const uint8_t* src = (const uint8_t*)dstM.fImage;
567 uint8_t* dst = (uint8_t*)origGlyph.fImage;
568
569 if (SkMask::k3D_Format == dstM.fFormat) {
570 // we have to copy 3 times as much
571 height *= 3;
572 }
573
574 // clean out our glyph, since it may be larger than dstM
reed@android.com4516f472009-06-29 16:25:36 +0000575 //sk_bzero(dst, height * dstRB);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576
577 while (--height >= 0) {
578 memcpy(dst, src, width);
579 src += srcRB;
580 dst += dstRB;
581 }
582 SkMask::FreeImage(dstM.fImage);
583 }
584 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585}
586
reed@google.com7db9fe62011-04-11 11:57:54 +0000587void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 this->internalGetPath(glyph, NULL, path, NULL);
589}
590
reed@google.com7db9fe62011-04-11 11:57:54 +0000591void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx,
592 SkPaint::FontMetrics* my) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 this->generateFontMetrics(mx, my);
594}
595
reed@android.com9d3a9852010-01-08 14:07:42 +0000596SkUnichar SkScalerContext::generateGlyphToChar(uint16_t glyph) {
597 return 0;
598}
599
reed@google.com7db9fe62011-04-11 11:57:54 +0000600///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601
reed@google.com7db9fe62011-04-11 11:57:54 +0000602void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
603 SkPath* devPath, SkMatrix* fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 SkPath path;
605
606 this->getGlyphContext(glyph)->generatePath(glyph, &path);
reed@google.com82065d62011-02-07 15:30:46 +0000607
reed@google.comcf598b12011-08-22 21:13:23 +0000608 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
609 SkFixed dx = glyph.getSubXFixed();
610 SkFixed dy = glyph.getSubYFixed();
611 if (dx | dy) {
612 path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy));
613 }
614 }
615
reed@google.com7db9fe62011-04-11 11:57:54 +0000616 if (fRec.fFrameWidth > 0 || fPathEffect != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 // need the path in user-space, with only the point-size applied
618 // so that our stroking and effects will operate the same way they
619 // would if the user had extracted the path themself, and then
620 // called drawPath
621 SkPath localPath;
622 SkMatrix matrix, inverse;
623
624 fRec.getMatrixFrom2x2(&matrix);
bungeman@google.com9575fb82012-04-09 20:49:03 +0000625 if (!matrix.invert(&inverse)) {
626 // assume fillPath and devPath are already empty.
627 return;
628 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 path.transform(inverse, &localPath);
630 // now localPath is only affected by the paint settings, and not the canvas matrix
631
reed@google.comfd4be262012-05-25 01:04:12 +0000632 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
633
634 if (fRec.fFrameWidth > 0) {
635 rec.setStrokeStyle(fRec.fFrameWidth,
636 SkToBool(fRec.fFlags & kFrameAndFill_Flag));
637 // glyphs are always closed contours, so cap type is ignored,
638 // so we just pass something.
639 rec.setStrokeParams(SkPaint::kButt_Cap,
640 (SkPaint::Join)fRec.fStrokeJoin,
641 fRec.fMiterLimit);
642 }
643
reed@google.com7db9fe62011-04-11 11:57:54 +0000644 if (fPathEffect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 SkPath effectPath;
reed@google.comfd4be262012-05-25 01:04:12 +0000646 if (fPathEffect->filterPath(&effectPath, localPath, &rec)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 localPath.swap(effectPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000648 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 }
650
reed@google.comfd4be262012-05-25 01:04:12 +0000651 if (rec.needToApply()) {
652 SkPath strokePath;
653 if (rec.applyToPath(&strokePath, localPath)) {
654 localPath.swap(strokePath);
655 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 }
reed@google.com82065d62011-02-07 15:30:46 +0000657
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 // now return stuff to the caller
reed@google.com7db9fe62011-04-11 11:57:54 +0000659 if (fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 *fillToDevMatrix = matrix;
reed@google.com7db9fe62011-04-11 11:57:54 +0000661 }
662 if (devPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 localPath.transform(matrix, devPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000664 }
665 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 fillPath->swap(localPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000667 }
668 } else { // nothing tricky to do
669 if (fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 fillToDevMatrix->reset();
reed@google.com7db9fe62011-04-11 11:57:54 +0000671 }
672 if (devPath) {
673 if (fillPath == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 devPath->swap(path);
reed@google.com7db9fe62011-04-11 11:57:54 +0000675 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 *devPath = path;
reed@google.com7db9fe62011-04-11 11:57:54 +0000677 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 }
reed@google.com82065d62011-02-07 15:30:46 +0000679
reed@google.com7db9fe62011-04-11 11:57:54 +0000680 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681 fillPath->swap(path);
reed@google.com7db9fe62011-04-11 11:57:54 +0000682 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 }
reed@google.com82065d62011-02-07 15:30:46 +0000684
reed@google.com7db9fe62011-04-11 11:57:54 +0000685 if (devPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 devPath->updateBoundsCache();
reed@google.com7db9fe62011-04-11 11:57:54 +0000687 }
688 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689 fillPath->updateBoundsCache();
reed@google.com7db9fe62011-04-11 11:57:54 +0000690 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691}
692
693
reed@google.com7db9fe62011-04-11 11:57:54 +0000694void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695 dst->reset();
696 dst->setScaleX(fPost2x2[0][0]);
697 dst->setSkewX( fPost2x2[0][1]);
698 dst->setSkewY( fPost2x2[1][0]);
699 dst->setScaleY(fPost2x2[1][1]);
700}
701
reed@google.com7db9fe62011-04-11 11:57:54 +0000702void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
reed@google.com7db9fe62011-04-11 11:57:54 +0000704 if (fPreSkewX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000705 m->postSkew(fPreSkewX, 0);
reed@google.com7db9fe62011-04-11 11:57:54 +0000706 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707}
708
reed@google.com7db9fe62011-04-11 11:57:54 +0000709void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 this->getLocalMatrix(m);
711
712 // now concat the device matrix
reed@google.com7db9fe62011-04-11 11:57:54 +0000713 SkMatrix deviceMatrix;
714 this->getMatrixFrom2x2(&deviceMatrix);
715 m->postConcat(deviceMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716}
717
reed@google.com2e684782011-08-24 15:38:46 +0000718SkAxisAlignment SkComputeAxisAlignmentForHText(const SkMatrix& matrix) {
719 SkASSERT(!matrix.hasPerspective());
720
721 if (0 == matrix[SkMatrix::kMSkewY]) {
722 return kX_SkAxisAlignment;
723 }
724 if (0 == matrix[SkMatrix::kMScaleX]) {
725 return kY_SkAxisAlignment;
726 }
727 return kNone_SkAxisAlignment;
728}
729
reed@android.com62900b42009-02-11 15:07:19 +0000730///////////////////////////////////////////////////////////////////////////////
731
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732#include "SkFontHost.h"
733
reed@android.com62900b42009-02-11 15:07:19 +0000734class SkScalerContext_Empty : public SkScalerContext {
735public:
736 SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {}
737
738protected:
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000739 virtual unsigned generateGlyphCount() {
reed@android.com62900b42009-02-11 15:07:19 +0000740 return 0;
741 }
742 virtual uint16_t generateCharToGlyph(SkUnichar uni) {
743 return 0;
744 }
745 virtual void generateAdvance(SkGlyph* glyph) {
746 glyph->zeroMetrics();
747 }
748 virtual void generateMetrics(SkGlyph* glyph) {
749 glyph->zeroMetrics();
750 }
751 virtual void generateImage(const SkGlyph& glyph) {}
752 virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
753 virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
754 SkPaint::FontMetrics* my) {
755 if (mx) {
reed@android.com4516f472009-06-29 16:25:36 +0000756 sk_bzero(mx, sizeof(*mx));
reed@android.com62900b42009-02-11 15:07:19 +0000757 }
758 if (my) {
reed@android.com4516f472009-06-29 16:25:36 +0000759 sk_bzero(my, sizeof(*my));
reed@android.com62900b42009-02-11 15:07:19 +0000760 }
761 }
762};
763
reed@android.comf2b98d62010-12-20 18:26:13 +0000764extern SkScalerContext* SkCreateColorScalerContext(const SkDescriptor* desc);
765
reed@google.com7db9fe62011-04-11 11:57:54 +0000766SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000767 SkScalerContext* c = NULL; //SkCreateColorScalerContext(desc);
768 if (NULL == c) {
769 c = SkFontHost::CreateScalerContext(desc);
770 }
reed@android.com62900b42009-02-11 15:07:19 +0000771 if (NULL == c) {
772 c = SkNEW_ARGS(SkScalerContext_Empty, (desc));
773 }
774 return c;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775}
776