blob: 4996a73f8da28904c5c51b7920c52b748583e950 [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"
bungeman@google.combbe50132012-07-24 20:33:21 +000015#include "SkGlyph.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkMaskFilter.h"
bungeman@google.com97efada2012-07-30 20:40:50 +000017#include "SkMaskGamma.h"
djsollen@google.com2b2ede32012-04-12 13:24:04 +000018#include "SkOrderedReadBuffer.h"
djsollen@google.com29bf8622013-07-31 15:48:10 +000019#include "SkOrderedWriteBuffer.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkPathEffect.h"
21#include "SkRasterizer.h"
reed@google.com045e62d2011-10-24 12:19:46 +000022#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkStroke.h"
24#include "SkThread.h"
25
reed@google.com1fa793f2013-03-26 12:29:05 +000026#ifdef SK_BUILD_FOR_ANDROID
27 #include "SkTypeface_android.h"
28#endif
robertphillips@google.coma22e2112012-08-16 14:58:06 +000029
reed@android.com8a1c16f2008-12-17 15:59:43 +000030#define ComputeBWRowBytes(width) (((unsigned)(width) + 7) >> 3)
31
reed@android.com8a1c16f2008-12-17 15:59:43 +000032void SkGlyph::toMask(SkMask* mask) const {
33 SkASSERT(mask);
34
35 mask->fImage = (uint8_t*)fImage;
36 mask->fBounds.set(fLeft, fTop, fLeft + fWidth, fTop + fHeight);
37 mask->fRowBytes = this->rowBytes();
reed@android.com6c14b432009-03-23 20:11:11 +000038 mask->fFormat = static_cast<SkMask::Format>(fMaskFormat);
reed@android.com8a1c16f2008-12-17 15:59:43 +000039}
40
41size_t SkGlyph::computeImageSize() const {
agl@chromium.org309485b2009-07-21 17:41:32 +000042 const size_t size = this->rowBytes() * fHeight;
43
44 switch (fMaskFormat) {
reed@google.com7db9fe62011-04-11 11:57:54 +000045 case SkMask::k3D_Format:
46 return 3 * size;
47 default:
48 return size;
reed@android.com8a1c16f2008-12-17 15:59:43 +000049 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000050}
51
reed@android.com62900b42009-02-11 15:07:19 +000052void SkGlyph::zeroMetrics() {
53 fAdvanceX = 0;
54 fAdvanceY = 0;
55 fWidth = 0;
56 fHeight = 0;
57 fTop = 0;
58 fLeft = 0;
59 fRsbDelta = 0;
60 fLsbDelta = 0;
61}
62
63///////////////////////////////////////////////////////////////////////////////
64
reed@android.com8a1c16f2008-12-17 15:59:43 +000065#ifdef SK_DEBUG
66 #define DUMP_RECx
67#endif
68
reed@google.com35348222013-10-16 13:05:06 +000069static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag,
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000070 SkFlattenable::Type ft) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 SkFlattenable* obj = NULL;
72 uint32_t len;
73 const void* data = desc->findEntry(tag, &len);
skia.committer@gmail.com583b18a2013-10-24 07:01:59 +000074
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 if (data) {
djsollen@google.com2b2ede32012-04-12 13:24:04 +000076 SkOrderedReadBuffer buffer(data, len);
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000077 obj = buffer.readFlattenable(ft);
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 SkASSERT(buffer.offset() == buffer.size());
79 }
80 return obj;
81}
82
reed@google.com0da48612013-03-19 16:06:52 +000083SkScalerContext::SkScalerContext(SkTypeface* typeface, const SkDescriptor* desc)
bungeman@google.com97efada2012-07-30 20:40:50 +000084 : fRec(*static_cast<const Rec*>(desc->findEntry(kRec_SkDescriptorTag, NULL)))
bungeman@google.coma76de722012-10-26 19:35:54 +000085
bungeman@google.com97efada2012-07-30 20:40:50 +000086 , fBaseGlyphCount(0)
reed@google.com0da48612013-03-19 16:06:52 +000087 , fTypeface(SkRef(typeface))
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000088 , fPathEffect(static_cast<SkPathEffect*>(load_flattenable(desc, kPathEffect_SkDescriptorTag,
89 SkFlattenable::kSkPathEffect_Type)))
90 , fMaskFilter(static_cast<SkMaskFilter*>(load_flattenable(desc, kMaskFilter_SkDescriptorTag,
91 SkFlattenable::kSkMaskFilter_Type)))
92 , fRasterizer(static_cast<SkRasterizer*>(load_flattenable(desc, kRasterizer_SkDescriptorTag,
93 SkFlattenable::kSkRasterizer_Type)))
bungeman@google.coma76de722012-10-26 19:35:54 +000094 // Initialize based on our settings. Subclasses can also force this.
bungeman@google.com97efada2012-07-30 20:40:50 +000095 , fGenerateImageFromPath(fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL)
bungeman@google.coma76de722012-10-26 19:35:54 +000096
bungeman@google.com97efada2012-07-30 20:40:50 +000097 , fNextContext(NULL)
bungeman@google.coma76de722012-10-26 19:35:54 +000098
99 , fPreBlend(fMaskFilter ? SkMaskGamma::PreBlend() : SkScalerContext::GetMaskPreBlend(fRec))
100 , fPreBlendForFilter(fMaskFilter ? SkScalerContext::GetMaskPreBlend(fRec)
101 : SkMaskGamma::PreBlend())
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103#ifdef DUMP_REC
104 desc->assertChecksum();
reed@google.com7db9fe62011-04-11 11:57:54 +0000105 SkDebugf("SkScalarContext checksum %x count %d length %d\n",
106 desc->getChecksum(), desc->getCount(), desc->getLength());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 SkDebugf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\n",
108 rec->fTextSize, rec->fPreScaleX, rec->fPreSkewX, rec->fPost2x2[0][0],
109 rec->fPost2x2[0][1], rec->fPost2x2[1][0], rec->fPost2x2[1][1]);
110 SkDebugf(" frame %g miter %g hints %d framefill %d format %d join %d\n",
111 rec->fFrameWidth, rec->fMiterLimit, rec->fHints, rec->fFrameAndFill,
112 rec->fMaskFormat, rec->fStrokeJoin);
reed@google.com7db9fe62011-04-11 11:57:54 +0000113 SkDebugf(" pathEffect %x maskFilter %x\n",
114 desc->findEntry(kPathEffect_SkDescriptorTag, NULL),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 desc->findEntry(kMaskFilter_SkDescriptorTag, NULL));
116#endif
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000117#ifdef SK_BUILD_FOR_ANDROID
118 uint32_t len;
119 const void* data = desc->findEntry(kAndroidOpts_SkDescriptorTag, &len);
120 if (data) {
121 SkOrderedReadBuffer buffer(data, len);
122 fPaintOptionsAndroid.unflatten(buffer);
123 SkASSERT(buffer.offset() == buffer.size());
124 }
125#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126}
127
128SkScalerContext::~SkScalerContext() {
reed@android.coma14ea0e2009-03-17 17:59:53 +0000129 SkDELETE(fNextContext);
130
reed@google.com82065d62011-02-07 15:30:46 +0000131 SkSafeUnref(fPathEffect);
132 SkSafeUnref(fMaskFilter);
133 SkSafeUnref(fRasterizer);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134}
135
reed@google.com0da48612013-03-19 16:06:52 +0000136// Return the context associated with the next logical typeface, or NULL if
137// there are no more entries in the fallback chain.
reed@google.coma45bcc82013-03-25 14:55:34 +0000138SkScalerContext* SkScalerContext::allocNextContext() const {
reed@google.comd257a7e2013-03-25 16:36:34 +0000139#ifdef SK_BUILD_FOR_ANDROID
reed@google.com1fa793f2013-03-26 12:29:05 +0000140 SkTypeface* newFace = SkAndroidNextLogicalTypeface(fRec.fFontID,
commit-bot@chromium.orgc7a20e42013-05-13 14:09:13 +0000141 fRec.fOrigFontID,
142 fPaintOptionsAndroid);
reed@google.com0da48612013-03-19 16:06:52 +0000143 if (0 == newFace) {
reed@android.coma14ea0e2009-03-17 17:59:53 +0000144 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 }
skia.committer@gmail.com01c34ee2013-03-20 07:01:02 +0000146
reed@google.com0da48612013-03-19 16:06:52 +0000147 SkAutoTUnref<SkTypeface> aur(newFace);
148 uint32_t newFontID = newFace->uniqueID();
reed@android.coma14ea0e2009-03-17 17:59:53 +0000149
djsollen@google.com29bf8622013-07-31 15:48:10 +0000150 SkOrderedWriteBuffer androidBuffer(128);
151 fPaintOptionsAndroid.flatten(androidBuffer);
152
153 SkAutoDescriptor ad(sizeof(fRec) + androidBuffer.size() + SkDescriptor::ComputeOverhead(2));
reed@android.coma14ea0e2009-03-17 17:59:53 +0000154 SkDescriptor* desc = ad.getDesc();
155
156 desc->init();
157 SkScalerContext::Rec* newRec =
158 (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
reed@google.coma45bcc82013-03-25 14:55:34 +0000159 sizeof(fRec), &fRec);
djsollen@google.com29bf8622013-07-31 15:48:10 +0000160 androidBuffer.writeToMemory(desc->addEntry(kAndroidOpts_SkDescriptorTag,
161 androidBuffer.size(), NULL));
162
reed@android.coma14ea0e2009-03-17 17:59:53 +0000163 newRec->fFontID = newFontID;
164 desc->computeChecksum();
165
reed@google.com0da48612013-03-19 16:06:52 +0000166 return newFace->createScalerContext(desc);
reed@google.comd257a7e2013-03-25 16:36:34 +0000167#else
168 return NULL;
169#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170}
171
reed@android.coma14ea0e2009-03-17 17:59:53 +0000172/* Return the next context, creating it if its not already created, but return
173 NULL if the fonthost says there are no more fonts to fallback to.
174 */
175SkScalerContext* SkScalerContext::getNextContext() {
176 SkScalerContext* next = fNextContext;
177 // if next is null, then either it isn't cached yet, or we're at the
178 // end of our possible chain
179 if (NULL == next) {
reed@google.coma45bcc82013-03-25 14:55:34 +0000180 next = this->allocNextContext();
reed@android.coma14ea0e2009-03-17 17:59:53 +0000181 if (NULL == next) {
182 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 }
reed@android.coma14ea0e2009-03-17 17:59:53 +0000184 // next's base is our base + our local count
185 next->setBaseGlyphCount(fBaseGlyphCount + this->getGlyphCount());
186 // cache the answer
187 fNextContext = next;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 }
reed@android.coma14ea0e2009-03-17 17:59:53 +0000189 return next;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190}
191
reed@android.coma14ea0e2009-03-17 17:59:53 +0000192SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
193 unsigned glyphID = glyph.getGlyphID();
194 SkScalerContext* ctx = this;
195 for (;;) {
196 unsigned count = ctx->getGlyphCount();
197 if (glyphID < count) {
198 break;
199 }
200 glyphID -= count;
201 ctx = ctx->getNextContext();
202 if (NULL == ctx) {
scroggo@google.com027038a2012-10-11 19:12:03 +0000203// SkDebugf("--- no context for glyph %x\n", glyph.getGlyphID());
reed@android.coma14ea0e2009-03-17 17:59:53 +0000204 // just return the original context (this)
205 return this;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 }
207 }
208 return ctx;
209}
210
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000211SkScalerContext* SkScalerContext::getContextFromChar(SkUnichar uni,
212 uint16_t* glyphID) {
213 SkScalerContext* ctx = this;
214 for (;;) {
215 const uint16_t glyph = ctx->generateCharToGlyph(uni);
216 if (glyph) {
217 if (NULL != glyphID) {
218 *glyphID = glyph;
219 }
220 break; // found it
221 }
222 ctx = ctx->getNextContext();
223 if (NULL == ctx) {
224 return NULL;
225 }
226 }
227 return ctx;
228}
229
djsollen@google.com60abb072012-02-15 18:49:15 +0000230#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000231SkFontID SkScalerContext::findTypefaceIdForChar(SkUnichar uni) {
232 SkScalerContext* ctx = this->getContextFromChar(uni, NULL);
233 if (NULL != ctx) {
234 return ctx->fRec.fFontID;
235 } else {
236 return 0;
237 }
238}
239
djsollen@google.com60abb072012-02-15 18:49:15 +0000240/* This loops through all available fallback contexts (if needed) until it
241 finds some context that can handle the unichar and return it.
242
243 As this is somewhat expensive operation, it should only be done on the first
244 char of a run.
245 */
246unsigned SkScalerContext::getBaseGlyphCount(SkUnichar uni) {
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000247 SkScalerContext* ctx = this->getContextFromChar(uni, NULL);
248 if (NULL != ctx) {
249 return ctx->fBaseGlyphCount;
250 } else {
251 SkDEBUGF(("--- no context for char %x\n", uni));
252 return this->fBaseGlyphCount;
djsollen@google.com60abb072012-02-15 18:49:15 +0000253 }
djsollen@google.com60abb072012-02-15 18:49:15 +0000254}
255#endif
256
reed@android.coma14ea0e2009-03-17 17:59:53 +0000257/* This loops through all available fallback contexts (if needed) until it
258 finds some context that can handle the unichar. If all fail, returns 0
259 */
260uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000261
262 uint16_t tempID;
263 SkScalerContext* ctx = this->getContextFromChar(uni, &tempID);
264 if (NULL == ctx) {
265 return 0; // no more contexts, return missing glyph
reed@android.coma14ea0e2009-03-17 17:59:53 +0000266 }
267 // add the ctx's base, making glyphID unique for chain of contexts
djsollen@google.com4bd2bdb2013-03-08 18:35:13 +0000268 unsigned glyphID = tempID + ctx->fBaseGlyphCount;
reed@android.coma14ea0e2009-03-17 17:59:53 +0000269 // check for overflow of 16bits, since our glyphID cannot exceed that
270 if (glyphID > 0xFFFF) {
271 glyphID = 0;
272 }
273 return SkToU16(glyphID);
274}
275
reed@android.com9d3a9852010-01-08 14:07:42 +0000276SkUnichar SkScalerContext::glyphIDToChar(uint16_t glyphID) {
277 SkScalerContext* ctx = this;
278 unsigned rangeEnd = 0;
279 do {
280 unsigned rangeStart = rangeEnd;
281
282 rangeEnd += ctx->getGlyphCount();
283 if (rangeStart <= glyphID && glyphID < rangeEnd) {
284 return ctx->generateGlyphToChar(glyphID - rangeStart);
285 }
286 ctx = ctx->getNextContext();
287 } while (NULL != ctx);
288 return 0;
289}
290
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291void SkScalerContext::getAdvance(SkGlyph* glyph) {
292 // mark us as just having a valid advance
293 glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE;
294 // we mark the format before making the call, in case the impl
295 // internally ends up calling its generateMetrics, which is OK
296 // albeit slower than strictly necessary
297 this->getGlyphContext(*glyph)->generateAdvance(glyph);
298}
299
300void SkScalerContext::getMetrics(SkGlyph* glyph) {
301 this->getGlyphContext(*glyph)->generateMetrics(glyph);
302
303 // for now we have separate cache entries for devkerning on and off
304 // in the future we might share caches, but make our measure/draw
305 // code make the distinction. Thus we zap the values if the caller
306 // has not asked for them.
307 if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) {
308 // no devkern, so zap the fields
309 glyph->fLsbDelta = glyph->fRsbDelta = 0;
310 }
311
312 // if either dimension is empty, zap the image bounds of the glyph
313 if (0 == glyph->fWidth || 0 == glyph->fHeight) {
314 glyph->fWidth = 0;
315 glyph->fHeight = 0;
316 glyph->fTop = 0;
317 glyph->fLeft = 0;
318 glyph->fMaskFormat = 0;
319 return;
320 }
reed@google.com82065d62011-02-07 15:30:46 +0000321
reed@google.coma767fa02011-08-05 21:40:26 +0000322 if (fGenerateImageFromPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 SkPath devPath, fillPath;
324 SkMatrix fillToDevMatrix;
325
326 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
327
328 if (fRasterizer) {
329 SkMask mask;
330
331 if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
332 fMaskFilter, &mask,
333 SkMask::kJustComputeBounds_CreateMode)) {
334 glyph->fLeft = mask.fBounds.fLeft;
335 glyph->fTop = mask.fBounds.fTop;
336 glyph->fWidth = SkToU16(mask.fBounds.width());
337 glyph->fHeight = SkToU16(mask.fBounds.height());
338 } else {
senorblanco@chromium.org7767cd82009-11-25 17:14:32 +0000339 goto SK_ERROR;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 }
341 } else {
342 // just use devPath
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 SkIRect ir;
reed@android.comd252db02009-04-01 18:31:44 +0000344 devPath.getBounds().roundOut(&ir);
reed@google.com82065d62011-02-07 15:30:46 +0000345
reed@android.comb757f8d2009-12-08 22:02:26 +0000346 if (ir.isEmpty() || !ir.is16Bit()) {
senorblanco@chromium.org7767cd82009-11-25 17:14:32 +0000347 goto SK_ERROR;
reed@android.comd4577752009-11-21 02:48:11 +0000348 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 glyph->fLeft = ir.fLeft;
350 glyph->fTop = ir.fTop;
351 glyph->fWidth = SkToU16(ir.width());
352 glyph->fHeight = SkToU16(ir.height());
bungeman@google.com0abbff92013-07-27 20:37:56 +0000353
354 if (glyph->fWidth > 0) {
rmistry@google.comd6bab022013-12-02 13:50:38 +0000355 switch (fRec.fMaskFormat) {
356 case SkMask::kLCD16_Format:
357 case SkMask::kLCD32_Format:
358 glyph->fWidth += 2;
359 glyph->fLeft -= 1;
360 break;
361 default:
362 break;
363 }
bungeman@google.com0abbff92013-07-27 20:37:56 +0000364 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 }
366 }
367
robertphillips@google.coma22e2112012-08-16 14:58:06 +0000368 if (SkMask::kARGB32_Format != glyph->fMaskFormat) {
369 glyph->fMaskFormat = fRec.fMaskFormat;
370 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000371
reed@google.com5bdfb332013-05-02 18:55:44 +0000372 // If we are going to create the mask, then we cannot keep the color
373 if ((fGenerateImageFromPath || fMaskFilter) &&
374 SkMask::kARGB32_Format == glyph->fMaskFormat) {
375 glyph->fMaskFormat = SkMask::kA8_Format;
376 }
377
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 if (fMaskFilter) {
379 SkMask src, dst;
380 SkMatrix matrix;
381
382 glyph->toMask(&src);
383 fRec.getMatrixFrom2x2(&matrix);
384
385 src.fImage = NULL; // only want the bounds from the filter
386 if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) {
reed@google.com8136d582012-09-21 17:38:06 +0000387 if (dst.fBounds.isEmpty() || !dst.fBounds.is16Bit()) {
388 goto SK_ERROR;
389 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390 SkASSERT(dst.fImage == NULL);
391 glyph->fLeft = dst.fBounds.fLeft;
392 glyph->fTop = dst.fBounds.fTop;
393 glyph->fWidth = SkToU16(dst.fBounds.width());
394 glyph->fHeight = SkToU16(dst.fBounds.height());
395 glyph->fMaskFormat = dst.fFormat;
396 }
397 }
reed@android.comd4577752009-11-21 02:48:11 +0000398 return;
reed@google.com82065d62011-02-07 15:30:46 +0000399
senorblanco@chromium.org7767cd82009-11-25 17:14:32 +0000400SK_ERROR:
reed@android.comd4577752009-11-21 02:48:11 +0000401 // draw nothing 'cause we failed
402 glyph->fLeft = 0;
403 glyph->fTop = 0;
404 glyph->fWidth = 0;
405 glyph->fHeight = 0;
reed@android.comb757f8d2009-12-08 22:02:26 +0000406 // put a valid value here, in case it was earlier set to
407 // MASK_FORMAT_JUST_ADVANCE
408 glyph->fMaskFormat = fRec.fMaskFormat;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409}
410
bungeman@google.com0abbff92013-07-27 20:37:56 +0000411#define SK_SHOW_TEXT_BLIT_COVERAGE 0
bungeman@google.coma76de722012-10-26 19:35:54 +0000412
413static void applyLUTToA8Mask(const SkMask& mask, const uint8_t* lut) {
414 uint8_t* SK_RESTRICT dst = (uint8_t*)mask.fImage;
415 unsigned rowBytes = mask.fRowBytes;
416
417 for (int y = mask.fBounds.height() - 1; y >= 0; --y) {
418 for (int x = mask.fBounds.width() - 1; x >= 0; --x) {
419 dst[x] = lut[dst[x]];
420 }
421 dst += rowBytes;
422 }
423}
424
bungeman@google.com97efada2012-07-30 20:40:50 +0000425template<bool APPLY_PREBLEND>
bungeman@google.com0abbff92013-07-27 20:37:56 +0000426static void pack4xHToLCD16(const SkBitmap& src, const SkMask& dst,
bungeman@google.coma76de722012-10-26 19:35:54 +0000427 const SkMaskGamma::PreBlend& maskPreBlend) {
bungeman@google.com0abbff92013-07-27 20:37:56 +0000428#define SAMPLES_PER_PIXEL 4
429#define LCD_PER_PIXEL 3
reed@google.comcf598b12011-08-22 21:13:23 +0000430 SkASSERT(SkBitmap::kA8_Config == src.config());
431 SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000432
bungeman@google.com0abbff92013-07-27 20:37:56 +0000433 const int sample_width = src.width();
434 const int height = src.height();
435
reed@google.comcf598b12011-08-22 21:13:23 +0000436 uint16_t* dstP = (uint16_t*)dst.fImage;
437 size_t dstRB = dst.fRowBytes;
bungeman@google.com0abbff92013-07-27 20:37:56 +0000438 // An N tap FIR is defined by
439 // out[n] = coeff[0]*x[n] + coeff[1]*x[n-1] + ... + coeff[N]*x[n-N]
440 // or
441 // out[n] = sum(i, 0, N, coeff[i]*x[n-i])
442
443 // The strategy is to use one FIR (different coefficients) for each of r, g, and b.
444 // This means using every 4th FIR output value of each FIR and discarding the rest.
445 // The FIRs are aligned, and the coefficients reach 5 samples to each side of their 'center'.
446 // (For r and b this is technically incorrect, but the coeffs outside round to zero anyway.)
skia.committer@gmail.com27e21fe2013-07-28 07:01:03 +0000447
bungeman@google.com0abbff92013-07-27 20:37:56 +0000448 // These are in some fixed point repesentation.
449 // Adding up to more than one simulates ink spread.
450 // For implementation reasons, these should never add up to more than two.
451
452 // Coefficients determined by a gausian where 5 samples = 3 std deviations (0x110 'contrast').
453 // Calculated using tools/generate_fir_coeff.py
454 // With this one almost no fringing is ever seen, but it is imperceptibly blurry.
455 // The lcd smoothed text is almost imperceptibly different from gray,
456 // but is still sharper on small stems and small rounded corners than gray.
457 // This also seems to be about as wide as one can get and only have a three pixel kernel.
458 // TODO: caculate these at runtime so parameters can be adjusted (esp contrast).
459 static const unsigned int coefficients[LCD_PER_PIXEL][SAMPLES_PER_PIXEL*3] = {
460 //The red subpixel is centered inside the first sample (at 1/6 pixel), and is shifted.
461 { 0x03, 0x0b, 0x1c, 0x33, 0x40, 0x39, 0x24, 0x10, 0x05, 0x01, 0x00, 0x00, },
462 //The green subpixel is centered between two samples (at 1/2 pixel), so is symetric
463 { 0x00, 0x02, 0x08, 0x16, 0x2b, 0x3d, 0x3d, 0x2b, 0x16, 0x08, 0x02, 0x00, },
464 //The blue subpixel is centered inside the last sample (at 5/6 pixel), and is shifted.
465 { 0x00, 0x00, 0x01, 0x05, 0x10, 0x24, 0x39, 0x40, 0x33, 0x1c, 0x0b, 0x03, },
466 };
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000467
reed@google.comcf598b12011-08-22 21:13:23 +0000468 for (int y = 0; y < height; ++y) {
469 const uint8_t* srcP = src.getAddr8(0, y);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000470
471 // TODO: this fir filter implementation is straight forward, but slow.
472 // It should be possible to make it much faster.
473 for (int sample_x = -4, pixel_x = 0; sample_x < sample_width + 4; sample_x += 4, ++pixel_x) {
474 int fir[LCD_PER_PIXEL] = { 0 };
475 for (int sample_index = SkMax32(0, sample_x - 4), coeff_index = sample_index - (sample_x - 4)
476 ; sample_index < SkMin32(sample_x + 8, sample_width)
477 ; ++sample_index, ++coeff_index)
478 {
479 int sample_value = srcP[sample_index];
480 for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpxl_index) {
481 fir[subpxl_index] += coefficients[subpxl_index][coeff_index] * sample_value;
482 }
483 }
484 for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpxl_index) {
485 fir[subpxl_index] /= 0x100;
486 fir[subpxl_index] = SkMin32(fir[subpxl_index], 255);
487 }
488
489 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(fir[0], maskPreBlend.fR);
490 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(fir[1], maskPreBlend.fG);
491 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(fir[2], maskPreBlend.fB);
492#if SK_SHOW_TEXT_BLIT_COVERAGE
493 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10);
494#endif
495 dstP[pixel_x] = SkPack888ToRGB16(r, g, b);
reed@google.coma767fa02011-08-05 21:40:26 +0000496 }
reed@google.comcf598b12011-08-22 21:13:23 +0000497 dstP = (uint16_t*)((char*)dstP + dstRB);
498 }
499}
500
bungeman@google.com97efada2012-07-30 20:40:50 +0000501template<bool APPLY_PREBLEND>
bungeman@google.com0abbff92013-07-27 20:37:56 +0000502static void pack4xHToLCD32(const SkBitmap& src, const SkMask& dst,
bungeman@google.coma76de722012-10-26 19:35:54 +0000503 const SkMaskGamma::PreBlend& maskPreBlend) {
reed@google.comcf598b12011-08-22 21:13:23 +0000504 SkASSERT(SkBitmap::kA8_Config == src.config());
505 SkASSERT(SkMask::kLCD32_Format == dst.fFormat);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000506
reed@google.comcf598b12011-08-22 21:13:23 +0000507 const int width = dst.fBounds.width();
508 const int height = dst.fBounds.height();
509 SkPMColor* dstP = (SkPMColor*)dst.fImage;
510 size_t dstRB = dst.fRowBytes;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000511
reed@google.comcf598b12011-08-22 21:13:23 +0000512 for (int y = 0; y < height; ++y) {
513 const uint8_t* srcP = src.getAddr8(0, y);
bungeman@google.com0abbff92013-07-27 20:37:56 +0000514
515 // TODO: need to use fir filter here as well.
reed@google.comcf598b12011-08-22 21:13:23 +0000516 for (int x = 0; x < width; ++x) {
bungeman@google.coma76de722012-10-26 19:35:54 +0000517 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fR);
518 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fG);
519 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fB);
bungeman@google.com97efada2012-07-30 20:40:50 +0000520 dstP[x] = SkPackARGB32(0xFF, r, g, b);
reed@google.comcf598b12011-08-22 21:13:23 +0000521 }
reed@google.coma767fa02011-08-05 21:40:26 +0000522 dstP = (SkPMColor*)((char*)dstP + dstRB);
523 }
524}
525
rmistry@google.comd6bab022013-12-02 13:50:38 +0000526static inline int convert_8_to_1(unsigned byte) {
527 SkASSERT(byte <= 0xFF);
528 return byte >> 7;
529}
530
531static uint8_t pack_8_to_1(const uint8_t alpha[8]) {
532 unsigned bits = 0;
533 for (int i = 0; i < 8; ++i) {
534 bits <<= 1;
535 bits |= convert_8_to_1(alpha[i]);
536 }
537 return SkToU8(bits);
538}
539
540static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) {
541 const int height = mask.fBounds.height();
542 const int width = mask.fBounds.width();
543 const int octs = width >> 3;
544 const int leftOverBits = width & 7;
545
546 uint8_t* dst = mask.fImage;
547 const int dstPad = mask.fRowBytes - SkAlign8(width)/8;
548 SkASSERT(dstPad >= 0);
549
550 const int srcPad = srcRB - width;
551 SkASSERT(srcPad >= 0);
552
553 for (int y = 0; y < height; ++y) {
554 for (int i = 0; i < octs; ++i) {
555 *dst++ = pack_8_to_1(src);
556 src += 8;
557 }
558 if (leftOverBits > 0) {
559 unsigned bits = 0;
560 int shift = 7;
561 for (int i = 0; i < leftOverBits; ++i, --shift) {
562 bits |= convert_8_to_1(*src++ >> 7) << shift;
563 }
564 *dst++ = bits;
565 }
566 src += srcPad;
567 dst += dstPad;
568 }
569}
570
bungeman@google.coma76de722012-10-26 19:35:54 +0000571static void generateMask(const SkMask& mask, const SkPath& path,
572 const SkMaskGamma::PreBlend& maskPreBlend) {
rmistry@google.comd6bab022013-12-02 13:50:38 +0000573 SkPaint paint;
reed@google.comcf598b12011-08-22 21:13:23 +0000574
575 int srcW = mask.fBounds.width();
576 int srcH = mask.fBounds.height();
577 int dstW = srcW;
578 int dstH = srcH;
579 int dstRB = mask.fRowBytes;
580
581 SkMatrix matrix;
582 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
583 -SkIntToScalar(mask.fBounds.fTop));
584
rmistry@google.comd6bab022013-12-02 13:50:38 +0000585 SkBitmap::Config config = SkBitmap::kA8_Config;
586 paint.setAntiAlias(SkMask::kBW_Format != mask.fFormat);
587 switch (mask.fFormat) {
588 case SkMask::kBW_Format:
589 dstRB = 0; // signals we need a copy
590 break;
591 case SkMask::kA8_Format:
592 break;
593 case SkMask::kLCD16_Format:
594 case SkMask::kLCD32_Format:
595 // TODO: trigger off LCD orientation
596 dstW = 4*dstW - 8;
597 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft + 1),
598 -SkIntToScalar(mask.fBounds.fTop));
599 matrix.postScale(SkIntToScalar(4), SK_Scalar1);
600 dstRB = 0; // signals we need a copy
601 break;
602 default:
603 SkDEBUGFAIL("unexpected mask format");
reed@google.comcf598b12011-08-22 21:13:23 +0000604 }
605
reed@google.com045e62d2011-10-24 12:19:46 +0000606 SkRasterClip clip;
607 clip.setRect(SkIRect::MakeWH(dstW, dstH));
reed@google.comcf598b12011-08-22 21:13:23 +0000608
609 SkBitmap bm;
610 bm.setConfig(config, dstW, dstH, dstRB);
611
612 if (0 == dstRB) {
reed@google.com9be57272012-12-17 14:21:38 +0000613 if (!bm.allocPixels()) {
614 // can't allocate offscreen, so empty the mask and return
615 sk_bzero(mask.fImage, mask.computeImageSize());
616 return;
617 }
reed@google.comcf598b12011-08-22 21:13:23 +0000618 bm.lockPixels();
619 } else {
620 bm.setPixels(mask.fImage);
621 }
622 sk_bzero(bm.getPixels(), bm.getSafeSize());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000623
reed@google.comcf598b12011-08-22 21:13:23 +0000624 SkDraw draw;
reed@google.com045e62d2011-10-24 12:19:46 +0000625 draw.fRC = &clip;
626 draw.fClip = &clip.bwRgn();
reed@google.comcf598b12011-08-22 21:13:23 +0000627 draw.fMatrix = &matrix;
628 draw.fBitmap = &bm;
629 draw.drawPath(path, paint);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000630
bungeman@google.com0c5f3762012-10-26 20:48:54 +0000631 switch (mask.fFormat) {
rmistry@google.comd6bab022013-12-02 13:50:38 +0000632 case SkMask::kBW_Format:
633 packA8ToA1(mask, bm.getAddr8(0, 0), bm.rowBytes());
634 break;
bungeman@google.com0c5f3762012-10-26 20:48:54 +0000635 case SkMask::kA8_Format:
636 if (maskPreBlend.isApplicable()) {
637 applyLUTToA8Mask(mask, maskPreBlend.fG);
638 }
639 break;
640 case SkMask::kLCD16_Format:
641 if (maskPreBlend.isApplicable()) {
bungeman@google.com0abbff92013-07-27 20:37:56 +0000642 pack4xHToLCD16<true>(bm, mask, maskPreBlend);
bungeman@google.com0c5f3762012-10-26 20:48:54 +0000643 } else {
bungeman@google.com0abbff92013-07-27 20:37:56 +0000644 pack4xHToLCD16<false>(bm, mask, maskPreBlend);
bungeman@google.com0c5f3762012-10-26 20:48:54 +0000645 }
646 break;
647 case SkMask::kLCD32_Format:
648 if (maskPreBlend.isApplicable()) {
bungeman@google.com0abbff92013-07-27 20:37:56 +0000649 pack4xHToLCD32<true>(bm, mask, maskPreBlend);
bungeman@google.com0c5f3762012-10-26 20:48:54 +0000650 } else {
bungeman@google.com0abbff92013-07-27 20:37:56 +0000651 pack4xHToLCD32<false>(bm, mask, maskPreBlend);
bungeman@google.com0c5f3762012-10-26 20:48:54 +0000652 }
653 break;
654 default:
655 break;
reed@google.comcf598b12011-08-22 21:13:23 +0000656 }
657}
658
reed@google.com5bdfb332013-05-02 18:55:44 +0000659static void extract_alpha(const SkMask& dst,
660 const SkPMColor* srcRow, size_t srcRB) {
661 int width = dst.fBounds.width();
662 int height = dst.fBounds.height();
663 int dstRB = dst.fRowBytes;
664 uint8_t* dstRow = dst.fImage;
665
666 for (int y = 0; y < height; ++y) {
667 for (int x = 0; x < width; ++x) {
668 dstRow[x] = SkGetPackedA32(srcRow[x]);
669 }
670 // zero any padding on each row
671 for (int x = width; x < dstRB; ++x) {
672 dstRow[x] = 0;
673 }
674 dstRow += dstRB;
675 srcRow = (const SkPMColor*)((const char*)srcRow + srcRB);
676 }
677}
678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679void SkScalerContext::getImage(const SkGlyph& origGlyph) {
680 const SkGlyph* glyph = &origGlyph;
681 SkGlyph tmpGlyph;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000682
reed@google.com5bdfb332013-05-02 18:55:44 +0000683 // in case we need to call generateImage on a mask-format that is different
684 // (i.e. larger) than what our caller allocated by looking at origGlyph.
685 SkAutoMalloc tmpGlyphImageStorage;
686
687 // If we are going to draw-from-path, then we cannot generate color, since
688 // the path only makes a mask. This case should have been caught up in
689 // generateMetrics().
690 SkASSERT(!fGenerateImageFromPath ||
691 SkMask::kARGB32_Format != origGlyph.fMaskFormat);
692
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693 if (fMaskFilter) { // restore the prefilter bounds
reed@google.comce2b1af2011-02-18 13:00:40 +0000694 tmpGlyph.init(origGlyph.fID);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000695
696 // need the original bounds, sans our maskfilter
697 SkMaskFilter* mf = fMaskFilter;
698 fMaskFilter = NULL; // temp disable
699 this->getMetrics(&tmpGlyph);
700 fMaskFilter = mf; // restore
701
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 // we need the prefilter bounds to be <= filter bounds
703 SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
704 SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
reed@google.com5bdfb332013-05-02 18:55:44 +0000705
706 if (tmpGlyph.fMaskFormat == origGlyph.fMaskFormat) {
707 tmpGlyph.fImage = origGlyph.fImage;
708 } else {
709 tmpGlyphImageStorage.reset(tmpGlyph.computeImageSize());
710 tmpGlyph.fImage = tmpGlyphImageStorage.get();
711 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 glyph = &tmpGlyph;
713 }
714
reed@google.coma767fa02011-08-05 21:40:26 +0000715 if (fGenerateImageFromPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 SkPath devPath, fillPath;
717 SkMatrix fillToDevMatrix;
reed@google.comcf598b12011-08-22 21:13:23 +0000718 SkMask mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719
720 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
reed@google.comcf598b12011-08-22 21:13:23 +0000721 glyph->toMask(&mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722
723 if (fRasterizer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 mask.fFormat = SkMask::kA8_Format;
reed@android.com4516f472009-06-29 16:25:36 +0000725 sk_bzero(glyph->fImage, mask.computeImageSize());
reed@google.com82065d62011-02-07 15:30:46 +0000726
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
728 fMaskFilter, &mask,
729 SkMask::kJustRenderImage_CreateMode)) {
730 return;
731 }
bungeman@google.coma76de722012-10-26 19:35:54 +0000732 if (fPreBlend.isApplicable()) {
733 applyLUTToA8Mask(mask, fPreBlend.fG);
bungeman@google.com97efada2012-07-30 20:40:50 +0000734 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 } else {
reed@google.com5bdfb332013-05-02 18:55:44 +0000736 SkASSERT(SkMask::kARGB32_Format != mask.fFormat);
bungeman@google.coma76de722012-10-26 19:35:54 +0000737 generateMask(mask, devPath, fPreBlend);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 }
739 } else {
bungeman@google.coma76de722012-10-26 19:35:54 +0000740 this->getGlyphContext(*glyph)->generateImage(*glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 }
742
743 if (fMaskFilter) {
744 SkMask srcM, dstM;
745 SkMatrix matrix;
746
747 // the src glyph image shouldn't be 3D
748 SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat);
reed@google.com5bdfb332013-05-02 18:55:44 +0000749
750 SkAutoSMalloc<32*32> a8storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751 glyph->toMask(&srcM);
reed@google.com5bdfb332013-05-02 18:55:44 +0000752 if (SkMask::kARGB32_Format == srcM.fFormat) {
753 // now we need to extract the alpha-channel from the glyph's image
754 // and copy it into a temp buffer, and then point srcM at that temp.
755 srcM.fFormat = SkMask::kA8_Format;
756 srcM.fRowBytes = SkAlign4(srcM.fBounds.width());
757 size_t size = srcM.computeImageSize();
758 a8storage.reset(size);
759 srcM.fImage = (uint8_t*)a8storage.get();
760 extract_alpha(srcM,
761 (const SkPMColor*)glyph->fImage, glyph->rowBytes());
762 }
skia.committer@gmail.com2fd42c42013-05-03 07:01:00 +0000763
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 fRec.getMatrixFrom2x2(&matrix);
765
766 if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) {
767 int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width());
768 int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height());
769 int dstRB = origGlyph.rowBytes();
770 int srcRB = dstM.fRowBytes;
reed@google.com82065d62011-02-07 15:30:46 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 const uint8_t* src = (const uint8_t*)dstM.fImage;
773 uint8_t* dst = (uint8_t*)origGlyph.fImage;
774
775 if (SkMask::k3D_Format == dstM.fFormat) {
776 // we have to copy 3 times as much
777 height *= 3;
778 }
779
780 // clean out our glyph, since it may be larger than dstM
reed@android.com4516f472009-06-29 16:25:36 +0000781 //sk_bzero(dst, height * dstRB);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782
783 while (--height >= 0) {
784 memcpy(dst, src, width);
785 src += srcRB;
786 dst += dstRB;
787 }
788 SkMask::FreeImage(dstM.fImage);
bungeman@google.com97efada2012-07-30 20:40:50 +0000789
bungeman@google.coma76de722012-10-26 19:35:54 +0000790 if (fPreBlendForFilter.isApplicable()) {
791 applyLUTToA8Mask(srcM, fPreBlendForFilter.fG);
792 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 }
794 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795}
796
reed@google.com7db9fe62011-04-11 11:57:54 +0000797void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 this->internalGetPath(glyph, NULL, path, NULL);
799}
800
reed@google.com0a01f5a2013-05-08 14:19:08 +0000801void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* fm) {
802 // All of this complexity should go away when we change generateFontMetrics
803 // to just take one parameter (since it knows if it is vertical or not)
804 SkPaint::FontMetrics* mx = NULL;
805 SkPaint::FontMetrics* my = NULL;
806 if (fRec.fFlags & kVertical_Flag) {
807 mx = fm;
808 } else {
809 my = fm;
810 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 this->generateFontMetrics(mx, my);
812}
813
reed@android.com9d3a9852010-01-08 14:07:42 +0000814SkUnichar SkScalerContext::generateGlyphToChar(uint16_t glyph) {
815 return 0;
816}
817
reed@google.com7db9fe62011-04-11 11:57:54 +0000818///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819
reed@google.com7db9fe62011-04-11 11:57:54 +0000820void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
821 SkPath* devPath, SkMatrix* fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 SkPath path;
823
824 this->getGlyphContext(glyph)->generatePath(glyph, &path);
reed@google.com82065d62011-02-07 15:30:46 +0000825
reed@google.comcf598b12011-08-22 21:13:23 +0000826 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
827 SkFixed dx = glyph.getSubXFixed();
828 SkFixed dy = glyph.getSubYFixed();
829 if (dx | dy) {
830 path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy));
831 }
832 }
833
reed@google.com7db9fe62011-04-11 11:57:54 +0000834 if (fRec.fFrameWidth > 0 || fPathEffect != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 // need the path in user-space, with only the point-size applied
836 // so that our stroking and effects will operate the same way they
837 // would if the user had extracted the path themself, and then
838 // called drawPath
839 SkPath localPath;
840 SkMatrix matrix, inverse;
841
842 fRec.getMatrixFrom2x2(&matrix);
bungeman@google.com9575fb82012-04-09 20:49:03 +0000843 if (!matrix.invert(&inverse)) {
844 // assume fillPath and devPath are already empty.
845 return;
846 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 path.transform(inverse, &localPath);
848 // now localPath is only affected by the paint settings, and not the canvas matrix
849
reed@google.comfd4be262012-05-25 01:04:12 +0000850 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000851
reed@google.comfd4be262012-05-25 01:04:12 +0000852 if (fRec.fFrameWidth > 0) {
853 rec.setStrokeStyle(fRec.fFrameWidth,
854 SkToBool(fRec.fFlags & kFrameAndFill_Flag));
855 // glyphs are always closed contours, so cap type is ignored,
856 // so we just pass something.
857 rec.setStrokeParams(SkPaint::kButt_Cap,
858 (SkPaint::Join)fRec.fStrokeJoin,
859 fRec.fMiterLimit);
860 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000861
reed@google.com7db9fe62011-04-11 11:57:54 +0000862 if (fPathEffect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 SkPath effectPath;
reed@google.com4bbdeac2013-01-24 21:03:11 +0000864 if (fPathEffect->filterPath(&effectPath, localPath, &rec, NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 localPath.swap(effectPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000866 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 }
868
reed@google.comfd4be262012-05-25 01:04:12 +0000869 if (rec.needToApply()) {
870 SkPath strokePath;
871 if (rec.applyToPath(&strokePath, localPath)) {
872 localPath.swap(strokePath);
873 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 }
reed@google.com82065d62011-02-07 15:30:46 +0000875
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876 // now return stuff to the caller
reed@google.com7db9fe62011-04-11 11:57:54 +0000877 if (fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 *fillToDevMatrix = matrix;
reed@google.com7db9fe62011-04-11 11:57:54 +0000879 }
880 if (devPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 localPath.transform(matrix, devPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000882 }
883 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 fillPath->swap(localPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000885 }
886 } else { // nothing tricky to do
887 if (fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 fillToDevMatrix->reset();
reed@google.com7db9fe62011-04-11 11:57:54 +0000889 }
890 if (devPath) {
891 if (fillPath == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 devPath->swap(path);
reed@google.com7db9fe62011-04-11 11:57:54 +0000893 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 *devPath = path;
reed@google.com7db9fe62011-04-11 11:57:54 +0000895 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 }
reed@google.com82065d62011-02-07 15:30:46 +0000897
reed@google.com7db9fe62011-04-11 11:57:54 +0000898 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 fillPath->swap(path);
reed@google.com7db9fe62011-04-11 11:57:54 +0000900 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 }
reed@google.com82065d62011-02-07 15:30:46 +0000902
reed@google.com7db9fe62011-04-11 11:57:54 +0000903 if (devPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 devPath->updateBoundsCache();
reed@google.com7db9fe62011-04-11 11:57:54 +0000905 }
906 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 fillPath->updateBoundsCache();
reed@google.com7db9fe62011-04-11 11:57:54 +0000908 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909}
910
911
reed@google.coma9d4e842012-08-14 19:13:55 +0000912void SkScalerContextRec::getMatrixFrom2x2(SkMatrix* dst) const {
bungeman@google.com50dd4102013-01-14 18:25:55 +0000913 dst->setAll(fPost2x2[0][0], fPost2x2[0][1], 0,
914 fPost2x2[1][0], fPost2x2[1][1], 0,
915 0, 0, SkScalarToPersp(SK_Scalar1));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916}
917
reed@google.coma9d4e842012-08-14 19:13:55 +0000918void SkScalerContextRec::getLocalMatrix(SkMatrix* m) const {
reed@google.comed43dff2013-06-04 16:56:27 +0000919 SkPaint::SetTextMatrix(m, fTextSize, fPreScaleX, fPreSkewX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920}
921
reed@google.coma9d4e842012-08-14 19:13:55 +0000922void SkScalerContextRec::getSingleMatrix(SkMatrix* m) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 this->getLocalMatrix(m);
924
925 // now concat the device matrix
reed@google.com7db9fe62011-04-11 11:57:54 +0000926 SkMatrix deviceMatrix;
927 this->getMatrixFrom2x2(&deviceMatrix);
928 m->postConcat(deviceMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929}
930
reed@google.com2e684782011-08-24 15:38:46 +0000931SkAxisAlignment SkComputeAxisAlignmentForHText(const SkMatrix& matrix) {
932 SkASSERT(!matrix.hasPerspective());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000933
reed@google.com2e684782011-08-24 15:38:46 +0000934 if (0 == matrix[SkMatrix::kMSkewY]) {
935 return kX_SkAxisAlignment;
936 }
937 if (0 == matrix[SkMatrix::kMScaleX]) {
938 return kY_SkAxisAlignment;
939 }
940 return kNone_SkAxisAlignment;
941}
942
reed@android.com62900b42009-02-11 15:07:19 +0000943///////////////////////////////////////////////////////////////////////////////
944
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945#include "SkFontHost.h"
946
reed@android.com62900b42009-02-11 15:07:19 +0000947class SkScalerContext_Empty : public SkScalerContext {
948public:
reed@google.com0da48612013-03-19 16:06:52 +0000949 SkScalerContext_Empty(SkTypeface* face, const SkDescriptor* desc)
950 : SkScalerContext(face, desc) {}
reed@android.com62900b42009-02-11 15:07:19 +0000951
952protected:
bungeman@google.coma76de722012-10-26 19:35:54 +0000953 virtual unsigned generateGlyphCount() SK_OVERRIDE {
reed@android.com62900b42009-02-11 15:07:19 +0000954 return 0;
955 }
bungeman@google.coma76de722012-10-26 19:35:54 +0000956 virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE {
reed@android.com62900b42009-02-11 15:07:19 +0000957 return 0;
958 }
bungeman@google.coma76de722012-10-26 19:35:54 +0000959 virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE {
reed@android.com62900b42009-02-11 15:07:19 +0000960 glyph->zeroMetrics();
961 }
bungeman@google.coma76de722012-10-26 19:35:54 +0000962 virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE {
reed@android.com62900b42009-02-11 15:07:19 +0000963 glyph->zeroMetrics();
964 }
bungeman@google.coma76de722012-10-26 19:35:54 +0000965 virtual void generateImage(const SkGlyph& glyph) SK_OVERRIDE {}
966 virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE {}
reed@android.com62900b42009-02-11 15:07:19 +0000967 virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
bungeman@google.coma76de722012-10-26 19:35:54 +0000968 SkPaint::FontMetrics* my) SK_OVERRIDE {
reed@android.com62900b42009-02-11 15:07:19 +0000969 if (mx) {
reed@android.com4516f472009-06-29 16:25:36 +0000970 sk_bzero(mx, sizeof(*mx));
reed@android.com62900b42009-02-11 15:07:19 +0000971 }
972 if (my) {
reed@android.com4516f472009-06-29 16:25:36 +0000973 sk_bzero(my, sizeof(*my));
reed@android.com62900b42009-02-11 15:07:19 +0000974 }
975 }
976};
977
reed@android.comf2b98d62010-12-20 18:26:13 +0000978extern SkScalerContext* SkCreateColorScalerContext(const SkDescriptor* desc);
979
reed@google.com84e22d82013-07-10 15:38:20 +0000980SkScalerContext* SkTypeface::createScalerContext(const SkDescriptor* desc,
981 bool allowFailure) const {
982 SkScalerContext* c = this->onCreateScalerContext(desc);
983
984 if (!c && !allowFailure) {
reed@google.com0da48612013-03-19 16:06:52 +0000985 c = SkNEW_ARGS(SkScalerContext_Empty,
986 (const_cast<SkTypeface*>(this), desc));
reed@android.com62900b42009-02-11 15:07:19 +0000987 }
988 return c;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989}