blob: 10729057c64720be10f3fb285174d4cdd8b61140 [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPathEffect.h"
20#include "SkRasterizer.h"
reed@google.com045e62d2011-10-24 12:19:46 +000021#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkStroke.h"
23#include "SkThread.h"
24
robertphillips@google.coma22e2112012-08-16 14:58:06 +000025
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#define ComputeBWRowBytes(width) (((unsigned)(width) + 7) >> 3)
27
reed@android.com8a1c16f2008-12-17 15:59:43 +000028void SkGlyph::toMask(SkMask* mask) const {
29 SkASSERT(mask);
30
31 mask->fImage = (uint8_t*)fImage;
32 mask->fBounds.set(fLeft, fTop, fLeft + fWidth, fTop + fHeight);
33 mask->fRowBytes = this->rowBytes();
reed@android.com6c14b432009-03-23 20:11:11 +000034 mask->fFormat = static_cast<SkMask::Format>(fMaskFormat);
reed@android.com8a1c16f2008-12-17 15:59:43 +000035}
36
37size_t SkGlyph::computeImageSize() const {
agl@chromium.org309485b2009-07-21 17:41:32 +000038 const size_t size = this->rowBytes() * fHeight;
39
40 switch (fMaskFormat) {
reed@google.com7db9fe62011-04-11 11:57:54 +000041 case SkMask::k3D_Format:
42 return 3 * size;
43 default:
44 return size;
reed@android.com8a1c16f2008-12-17 15:59:43 +000045 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000046}
47
reed@android.com62900b42009-02-11 15:07:19 +000048void SkGlyph::zeroMetrics() {
49 fAdvanceX = 0;
50 fAdvanceY = 0;
51 fWidth = 0;
52 fHeight = 0;
53 fTop = 0;
54 fLeft = 0;
55 fRsbDelta = 0;
56 fLsbDelta = 0;
57}
58
59///////////////////////////////////////////////////////////////////////////////
60
reed@android.com8a1c16f2008-12-17 15:59:43 +000061#ifdef SK_DEBUG
62 #define DUMP_RECx
63#endif
64
65static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) {
66 SkFlattenable* obj = NULL;
67 uint32_t len;
68 const void* data = desc->findEntry(tag, &len);
69
70 if (data) {
djsollen@google.com2b2ede32012-04-12 13:24:04 +000071 SkOrderedReadBuffer buffer(data, len);
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 obj = buffer.readFlattenable();
73 SkASSERT(buffer.offset() == buffer.size());
74 }
75 return obj;
76}
77
78SkScalerContext::SkScalerContext(const SkDescriptor* desc)
bungeman@google.com97efada2012-07-30 20:40:50 +000079 : fRec(*static_cast<const Rec*>(desc->findEntry(kRec_SkDescriptorTag, NULL)))
80 , fBaseGlyphCount(0)
81 , fPathEffect(static_cast<SkPathEffect*>(load_flattenable(desc, kPathEffect_SkDescriptorTag)))
82 , fMaskFilter(static_cast<SkMaskFilter*>(load_flattenable(desc, kMaskFilter_SkDescriptorTag)))
83 , fRasterizer(static_cast<SkRasterizer*>(load_flattenable(desc, kRasterizer_SkDescriptorTag)))
84 // initialize based on our settings. subclasses can also force this
85 , fGenerateImageFromPath(fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL)
86 , fNextContext(NULL)
87 , fMaskPreBlend(SkScalerContext::GetMaskPreBlend(fRec))
reed@android.com8a1c16f2008-12-17 15:59:43 +000088{
reed@android.com8a1c16f2008-12-17 15:59:43 +000089#ifdef DUMP_REC
90 desc->assertChecksum();
reed@google.com7db9fe62011-04-11 11:57:54 +000091 SkDebugf("SkScalarContext checksum %x count %d length %d\n",
92 desc->getChecksum(), desc->getCount(), desc->getLength());
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 SkDebugf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\n",
94 rec->fTextSize, rec->fPreScaleX, rec->fPreSkewX, rec->fPost2x2[0][0],
95 rec->fPost2x2[0][1], rec->fPost2x2[1][0], rec->fPost2x2[1][1]);
96 SkDebugf(" frame %g miter %g hints %d framefill %d format %d join %d\n",
97 rec->fFrameWidth, rec->fMiterLimit, rec->fHints, rec->fFrameAndFill,
98 rec->fMaskFormat, rec->fStrokeJoin);
reed@google.com7db9fe62011-04-11 11:57:54 +000099 SkDebugf(" pathEffect %x maskFilter %x\n",
100 desc->findEntry(kPathEffect_SkDescriptorTag, NULL),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 desc->findEntry(kMaskFilter_SkDescriptorTag, NULL));
102#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103}
104
105SkScalerContext::~SkScalerContext() {
reed@android.coma14ea0e2009-03-17 17:59:53 +0000106 SkDELETE(fNextContext);
107
reed@google.com82065d62011-02-07 15:30:46 +0000108 SkSafeUnref(fPathEffect);
109 SkSafeUnref(fMaskFilter);
110 SkSafeUnref(fRasterizer);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111}
112
reed@android.coma14ea0e2009-03-17 17:59:53 +0000113static SkScalerContext* allocNextContext(const SkScalerContext::Rec& rec) {
114 // fonthost will determine the next possible font to search, based
115 // on the current font in fRec. It will return NULL if ctx is our
116 // last font that can be searched (i.e. ultimate fallback font)
reed@google.com7d26c592011-06-13 13:01:10 +0000117 uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID, rec.fOrigFontID);
reed@android.coma14ea0e2009-03-17 17:59:53 +0000118 if (0 == newFontID) {
119 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120 }
reed@android.coma14ea0e2009-03-17 17:59:53 +0000121
122 SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
123 SkDescriptor* desc = ad.getDesc();
124
125 desc->init();
126 SkScalerContext::Rec* newRec =
127 (SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
128 sizeof(rec), &rec);
129 newRec->fFontID = newFontID;
130 desc->computeChecksum();
131
132 return SkFontHost::CreateScalerContext(desc);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133}
134
reed@android.coma14ea0e2009-03-17 17:59:53 +0000135/* Return the next context, creating it if its not already created, but return
136 NULL if the fonthost says there are no more fonts to fallback to.
137 */
138SkScalerContext* SkScalerContext::getNextContext() {
139 SkScalerContext* next = fNextContext;
140 // if next is null, then either it isn't cached yet, or we're at the
141 // end of our possible chain
142 if (NULL == next) {
143 next = allocNextContext(fRec);
144 if (NULL == next) {
145 return NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 }
reed@android.coma14ea0e2009-03-17 17:59:53 +0000147 // next's base is our base + our local count
148 next->setBaseGlyphCount(fBaseGlyphCount + this->getGlyphCount());
149 // cache the answer
150 fNextContext = next;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151 }
reed@android.coma14ea0e2009-03-17 17:59:53 +0000152 return next;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153}
154
reed@android.coma14ea0e2009-03-17 17:59:53 +0000155SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
156 unsigned glyphID = glyph.getGlyphID();
157 SkScalerContext* ctx = this;
158 for (;;) {
159 unsigned count = ctx->getGlyphCount();
160 if (glyphID < count) {
161 break;
162 }
163 glyphID -= count;
164 ctx = ctx->getNextContext();
165 if (NULL == ctx) {
166 SkDebugf("--- no context for glyph %x\n", glyph.getGlyphID());
167 // just return the original context (this)
168 return this;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169 }
170 }
171 return ctx;
172}
173
djsollen@google.com60abb072012-02-15 18:49:15 +0000174#ifdef SK_BUILD_FOR_ANDROID
175/* This loops through all available fallback contexts (if needed) until it
176 finds some context that can handle the unichar and return it.
177
178 As this is somewhat expensive operation, it should only be done on the first
179 char of a run.
180 */
181unsigned SkScalerContext::getBaseGlyphCount(SkUnichar uni) {
182 SkScalerContext* ctx = this;
183 unsigned glyphID;
184 for (;;) {
185 glyphID = ctx->generateCharToGlyph(uni);
186 if (glyphID) {
187 break; // found it
188 }
189 ctx = ctx->getNextContext();
190 if (NULL == ctx) {
191 SkDebugf("--- no context for char %x\n", uni);
192 // just return the original context (this)
193 return this->fBaseGlyphCount;
194 }
195 }
196 return ctx->fBaseGlyphCount;
197}
198#endif
199
reed@android.coma14ea0e2009-03-17 17:59:53 +0000200/* This loops through all available fallback contexts (if needed) until it
201 finds some context that can handle the unichar. If all fail, returns 0
202 */
203uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
204 SkScalerContext* ctx = this;
205 unsigned glyphID;
206 for (;;) {
207 glyphID = ctx->generateCharToGlyph(uni);
208 if (glyphID) {
209 break; // found it
210 }
211 ctx = ctx->getNextContext();
212 if (NULL == ctx) {
213 return 0; // no more contexts, return missing glyph
214 }
215 }
216 // add the ctx's base, making glyphID unique for chain of contexts
217 glyphID += ctx->fBaseGlyphCount;
218 // check for overflow of 16bits, since our glyphID cannot exceed that
219 if (glyphID > 0xFFFF) {
220 glyphID = 0;
221 }
222 return SkToU16(glyphID);
223}
224
reed@android.com9d3a9852010-01-08 14:07:42 +0000225SkUnichar SkScalerContext::glyphIDToChar(uint16_t glyphID) {
226 SkScalerContext* ctx = this;
227 unsigned rangeEnd = 0;
228 do {
229 unsigned rangeStart = rangeEnd;
230
231 rangeEnd += ctx->getGlyphCount();
232 if (rangeStart <= glyphID && glyphID < rangeEnd) {
233 return ctx->generateGlyphToChar(glyphID - rangeStart);
234 }
235 ctx = ctx->getNextContext();
236 } while (NULL != ctx);
237 return 0;
238}
239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240void SkScalerContext::getAdvance(SkGlyph* glyph) {
241 // mark us as just having a valid advance
242 glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE;
243 // we mark the format before making the call, in case the impl
244 // internally ends up calling its generateMetrics, which is OK
245 // albeit slower than strictly necessary
246 this->getGlyphContext(*glyph)->generateAdvance(glyph);
247}
248
249void SkScalerContext::getMetrics(SkGlyph* glyph) {
250 this->getGlyphContext(*glyph)->generateMetrics(glyph);
251
252 // for now we have separate cache entries for devkerning on and off
253 // in the future we might share caches, but make our measure/draw
254 // code make the distinction. Thus we zap the values if the caller
255 // has not asked for them.
256 if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) {
257 // no devkern, so zap the fields
258 glyph->fLsbDelta = glyph->fRsbDelta = 0;
259 }
260
261 // if either dimension is empty, zap the image bounds of the glyph
262 if (0 == glyph->fWidth || 0 == glyph->fHeight) {
263 glyph->fWidth = 0;
264 glyph->fHeight = 0;
265 glyph->fTop = 0;
266 glyph->fLeft = 0;
267 glyph->fMaskFormat = 0;
268 return;
269 }
reed@google.com82065d62011-02-07 15:30:46 +0000270
reed@google.coma767fa02011-08-05 21:40:26 +0000271 if (fGenerateImageFromPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 SkPath devPath, fillPath;
273 SkMatrix fillToDevMatrix;
274
275 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
276
277 if (fRasterizer) {
278 SkMask mask;
279
280 if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
281 fMaskFilter, &mask,
282 SkMask::kJustComputeBounds_CreateMode)) {
283 glyph->fLeft = mask.fBounds.fLeft;
284 glyph->fTop = mask.fBounds.fTop;
285 glyph->fWidth = SkToU16(mask.fBounds.width());
286 glyph->fHeight = SkToU16(mask.fBounds.height());
287 } else {
senorblanco@chromium.org7767cd82009-11-25 17:14:32 +0000288 goto SK_ERROR;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 }
290 } else {
291 // just use devPath
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 SkIRect ir;
reed@android.comd252db02009-04-01 18:31:44 +0000293 devPath.getBounds().roundOut(&ir);
reed@google.com82065d62011-02-07 15:30:46 +0000294
reed@android.comb757f8d2009-12-08 22:02:26 +0000295 if (ir.isEmpty() || !ir.is16Bit()) {
senorblanco@chromium.org7767cd82009-11-25 17:14:32 +0000296 goto SK_ERROR;
reed@android.comd4577752009-11-21 02:48:11 +0000297 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 glyph->fLeft = ir.fLeft;
299 glyph->fTop = ir.fTop;
300 glyph->fWidth = SkToU16(ir.width());
301 glyph->fHeight = SkToU16(ir.height());
302 }
303 }
304
robertphillips@google.coma22e2112012-08-16 14:58:06 +0000305 if (SkMask::kARGB32_Format != glyph->fMaskFormat) {
306 glyph->fMaskFormat = fRec.fMaskFormat;
307 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308
309 if (fMaskFilter) {
310 SkMask src, dst;
311 SkMatrix matrix;
312
313 glyph->toMask(&src);
314 fRec.getMatrixFrom2x2(&matrix);
315
316 src.fImage = NULL; // only want the bounds from the filter
317 if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) {
318 SkASSERT(dst.fImage == NULL);
319 glyph->fLeft = dst.fBounds.fLeft;
320 glyph->fTop = dst.fBounds.fTop;
321 glyph->fWidth = SkToU16(dst.fBounds.width());
322 glyph->fHeight = SkToU16(dst.fBounds.height());
323 glyph->fMaskFormat = dst.fFormat;
324 }
325 }
reed@android.comd4577752009-11-21 02:48:11 +0000326 return;
reed@google.com82065d62011-02-07 15:30:46 +0000327
senorblanco@chromium.org7767cd82009-11-25 17:14:32 +0000328SK_ERROR:
reed@android.comd4577752009-11-21 02:48:11 +0000329 // draw nothing 'cause we failed
330 glyph->fLeft = 0;
331 glyph->fTop = 0;
332 glyph->fWidth = 0;
333 glyph->fHeight = 0;
reed@android.comb757f8d2009-12-08 22:02:26 +0000334 // put a valid value here, in case it was earlier set to
335 // MASK_FORMAT_JUST_ADVANCE
336 glyph->fMaskFormat = fRec.fMaskFormat;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337}
338
bungeman@google.com97efada2012-07-30 20:40:50 +0000339template<bool APPLY_PREBLEND>
340static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst, SkMaskGamma::PreBlend* maskPreBlend) {
reed@google.comcf598b12011-08-22 21:13:23 +0000341 SkASSERT(SkBitmap::kA8_Config == src.config());
342 SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000343
reed@google.comcf598b12011-08-22 21:13:23 +0000344 const int width = dst.fBounds.width();
345 const int height = dst.fBounds.height();
346 uint16_t* dstP = (uint16_t*)dst.fImage;
347 size_t dstRB = dst.fRowBytes;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000348
bungeman@google.com97efada2012-07-30 20:40:50 +0000349 const uint8_t* maskPreBlendR = NULL;
350 const uint8_t* maskPreBlendG = NULL;
351 const uint8_t* maskPreBlendB = NULL;
352 if (APPLY_PREBLEND) {
353 maskPreBlendR = maskPreBlend->fR;
354 maskPreBlendG = maskPreBlend->fG;
355 maskPreBlendB = maskPreBlend->fB;
356 }
357
reed@google.comcf598b12011-08-22 21:13:23 +0000358 for (int y = 0; y < height; ++y) {
359 const uint8_t* srcP = src.getAddr8(0, y);
360 for (int x = 0; x < width; ++x) {
bungeman@google.com97efada2012-07-30 20:40:50 +0000361 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendR);
362 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendG);
363 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendB);
364 dstP[x] = SkPack888ToRGB16(r, g, b);
reed@google.coma767fa02011-08-05 21:40:26 +0000365 }
reed@google.comcf598b12011-08-22 21:13:23 +0000366 dstP = (uint16_t*)((char*)dstP + dstRB);
367 }
368}
369
bungeman@google.com97efada2012-07-30 20:40:50 +0000370template<bool APPLY_PREBLEND>
371static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst, SkMaskGamma::PreBlend* maskPreBlend) {
reed@google.comcf598b12011-08-22 21:13:23 +0000372 SkASSERT(SkBitmap::kA8_Config == src.config());
373 SkASSERT(SkMask::kLCD32_Format == dst.fFormat);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000374
reed@google.comcf598b12011-08-22 21:13:23 +0000375 const int width = dst.fBounds.width();
376 const int height = dst.fBounds.height();
377 SkPMColor* dstP = (SkPMColor*)dst.fImage;
378 size_t dstRB = dst.fRowBytes;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000379
bungeman@google.com97efada2012-07-30 20:40:50 +0000380 const uint8_t* maskPreBlendR = NULL;
381 const uint8_t* maskPreBlendG = NULL;
382 const uint8_t* maskPreBlendB = NULL;
383 if (APPLY_PREBLEND) {
384 maskPreBlendR = maskPreBlend->fR;
385 maskPreBlendG = maskPreBlend->fG;
386 maskPreBlendB = maskPreBlend->fB;
387 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000388
reed@google.comcf598b12011-08-22 21:13:23 +0000389 for (int y = 0; y < height; ++y) {
390 const uint8_t* srcP = src.getAddr8(0, y);
391 for (int x = 0; x < width; ++x) {
bungeman@google.com97efada2012-07-30 20:40:50 +0000392 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendR);
393 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendG);
394 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendB);
395 dstP[x] = SkPackARGB32(0xFF, r, g, b);
reed@google.comcf598b12011-08-22 21:13:23 +0000396 }
reed@google.coma767fa02011-08-05 21:40:26 +0000397 dstP = (SkPMColor*)((char*)dstP + dstRB);
398 }
399}
400
bungeman@google.com97efada2012-07-30 20:40:50 +0000401static void generateMask(const SkMask& mask, const SkPath& path, SkMaskGamma::PreBlend* maskPreBlend) {
reed@google.comcf598b12011-08-22 21:13:23 +0000402 SkBitmap::Config config;
403 SkPaint paint;
404
405 int srcW = mask.fBounds.width();
406 int srcH = mask.fBounds.height();
407 int dstW = srcW;
408 int dstH = srcH;
409 int dstRB = mask.fRowBytes;
410
411 SkMatrix matrix;
412 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
413 -SkIntToScalar(mask.fBounds.fTop));
414
415 if (SkMask::kBW_Format == mask.fFormat) {
416 config = SkBitmap::kA1_Config;
417 paint.setAntiAlias(false);
418 } else {
419 config = SkBitmap::kA8_Config;
420 paint.setAntiAlias(true);
421 switch (mask.fFormat) {
422 case SkMask::kA8_Format:
423 break;
424 case SkMask::kLCD16_Format:
425 case SkMask::kLCD32_Format:
426 // TODO: trigger off LCD orientation
427 dstW *= 3;
428 matrix.postScale(SkIntToScalar(3), SK_Scalar1);
429 dstRB = 0; // signals we need a copy
430 break;
431 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000432 SkDEBUGFAIL("unexpected mask format");
reed@google.comcf598b12011-08-22 21:13:23 +0000433 }
434 }
435
reed@google.com045e62d2011-10-24 12:19:46 +0000436 SkRasterClip clip;
437 clip.setRect(SkIRect::MakeWH(dstW, dstH));
reed@google.comcf598b12011-08-22 21:13:23 +0000438
439 SkBitmap bm;
440 bm.setConfig(config, dstW, dstH, dstRB);
441
442 if (0 == dstRB) {
443 bm.allocPixels();
444 bm.lockPixels();
445 } else {
446 bm.setPixels(mask.fImage);
447 }
448 sk_bzero(bm.getPixels(), bm.getSafeSize());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000449
reed@google.comcf598b12011-08-22 21:13:23 +0000450 SkDraw draw;
451 sk_bzero(&draw, sizeof(draw));
reed@google.com045e62d2011-10-24 12:19:46 +0000452 draw.fRC = &clip;
453 draw.fClip = &clip.bwRgn();
reed@google.comcf598b12011-08-22 21:13:23 +0000454 draw.fMatrix = &matrix;
455 draw.fBitmap = &bm;
456 draw.drawPath(path, paint);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000457
reed@google.comcf598b12011-08-22 21:13:23 +0000458 if (0 == dstRB) {
459 switch (mask.fFormat) {
460 case SkMask::kLCD16_Format:
bungeman@google.com97efada2012-07-30 20:40:50 +0000461 if (maskPreBlend) {
462 pack3xHToLCD16<true>(bm, mask, maskPreBlend);
463 } else {
464 pack3xHToLCD16<false>(bm, mask, maskPreBlend);
465 }
reed@google.comcf598b12011-08-22 21:13:23 +0000466 break;
467 case SkMask::kLCD32_Format:
bungeman@google.com97efada2012-07-30 20:40:50 +0000468 if (maskPreBlend) {
469 pack3xHToLCD32<true>(bm, mask, maskPreBlend);
470 } else {
471 pack3xHToLCD32<false>(bm, mask, maskPreBlend);
472 }
reed@google.comcf598b12011-08-22 21:13:23 +0000473 break;
474 default:
tomhudson@google.com0c00f212011-12-28 14:59:50 +0000475 SkDEBUGFAIL("bad format for copyback");
reed@google.comcf598b12011-08-22 21:13:23 +0000476 }
477 }
478}
479
bungeman@google.com97efada2012-07-30 20:40:50 +0000480static void applyLUTToA8Glyph(const SkGlyph& glyph, const uint8_t* lut) {
481 uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
482 unsigned rowBytes = glyph.rowBytes();
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000483
bungeman@google.com97efada2012-07-30 20:40:50 +0000484 for (int y = glyph.fHeight - 1; y >= 0; --y) {
485 for (int x = glyph.fWidth - 1; x >= 0; --x) {
486 dst[x] = lut[dst[x]];
487 }
488 dst += rowBytes;
489 }
490}
491
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492void SkScalerContext::getImage(const SkGlyph& origGlyph) {
493 const SkGlyph* glyph = &origGlyph;
494 SkGlyph tmpGlyph;
bungeman@google.com97efada2012-07-30 20:40:50 +0000495 SkMaskGamma::PreBlend* maskPreBlend = &fMaskPreBlend;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000496
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 if (fMaskFilter) { // restore the prefilter bounds
reed@google.comce2b1af2011-02-18 13:00:40 +0000498 tmpGlyph.init(origGlyph.fID);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499
500 // need the original bounds, sans our maskfilter
501 SkMaskFilter* mf = fMaskFilter;
502 fMaskFilter = NULL; // temp disable
503 this->getMetrics(&tmpGlyph);
504 fMaskFilter = mf; // restore
505
506 tmpGlyph.fImage = origGlyph.fImage;
507
508 // we need the prefilter bounds to be <= filter bounds
509 SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth);
510 SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight);
511 glyph = &tmpGlyph;
bungeman@google.com97efada2012-07-30 20:40:50 +0000512 maskPreBlend = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 }
514
reed@google.coma767fa02011-08-05 21:40:26 +0000515 if (fGenerateImageFromPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 SkPath devPath, fillPath;
517 SkMatrix fillToDevMatrix;
reed@google.comcf598b12011-08-22 21:13:23 +0000518 SkMask mask;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519
520 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix);
reed@google.comcf598b12011-08-22 21:13:23 +0000521 glyph->toMask(&mask);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522
523 if (fRasterizer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 mask.fFormat = SkMask::kA8_Format;
reed@android.com4516f472009-06-29 16:25:36 +0000525 sk_bzero(glyph->fImage, mask.computeImageSize());
reed@google.com82065d62011-02-07 15:30:46 +0000526
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL,
528 fMaskFilter, &mask,
529 SkMask::kJustRenderImage_CreateMode)) {
530 return;
531 }
bungeman@google.com97efada2012-07-30 20:40:50 +0000532 //apply maskPreBlend to a8 (if not NULL)
533 if (maskPreBlend) {
534 applyLUTToA8Glyph(*glyph, maskPreBlend->fG);
535 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 } else {
bungeman@google.com97efada2012-07-30 20:40:50 +0000537 generateMask(mask, devPath, maskPreBlend);
538 //apply maskPreBlend to a8 (if not NULL) -- already applied to lcd.
539 if (maskPreBlend && mask.fFormat == SkMask::kA8_Format) {
540 applyLUTToA8Glyph(*glyph, maskPreBlend->fG);
541 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 }
543 } else {
bungeman@google.com97efada2012-07-30 20:40:50 +0000544 this->getGlyphContext(*glyph)->generateImage(*glyph, maskPreBlend);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 }
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);
bungeman@google.com97efada2012-07-30 20:40:50 +0000579
580 /* Pre-blend is not currently applied to filtered text.
581 The primary filter is blur, for which contrast makes no sense,
582 and for which the destination guess error is more visible. */
583 //applyLUTToA8Glyph(origGlyph, fMaskPreBlend.fG);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 }
585 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586}
587
reed@google.com7db9fe62011-04-11 11:57:54 +0000588void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 this->internalGetPath(glyph, NULL, path, NULL);
590}
591
reed@google.com7db9fe62011-04-11 11:57:54 +0000592void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx,
593 SkPaint::FontMetrics* my) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 this->generateFontMetrics(mx, my);
595}
596
reed@android.com9d3a9852010-01-08 14:07:42 +0000597SkUnichar SkScalerContext::generateGlyphToChar(uint16_t glyph) {
598 return 0;
599}
600
reed@google.com7db9fe62011-04-11 11:57:54 +0000601///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602
reed@google.com7db9fe62011-04-11 11:57:54 +0000603void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
604 SkPath* devPath, SkMatrix* fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 SkPath path;
606
607 this->getGlyphContext(glyph)->generatePath(glyph, &path);
reed@google.com82065d62011-02-07 15:30:46 +0000608
reed@google.comcf598b12011-08-22 21:13:23 +0000609 if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
610 SkFixed dx = glyph.getSubXFixed();
611 SkFixed dy = glyph.getSubYFixed();
612 if (dx | dy) {
613 path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy));
614 }
615 }
616
reed@google.com7db9fe62011-04-11 11:57:54 +0000617 if (fRec.fFrameWidth > 0 || fPathEffect != NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 // need the path in user-space, with only the point-size applied
619 // so that our stroking and effects will operate the same way they
620 // would if the user had extracted the path themself, and then
621 // called drawPath
622 SkPath localPath;
623 SkMatrix matrix, inverse;
624
625 fRec.getMatrixFrom2x2(&matrix);
bungeman@google.com9575fb82012-04-09 20:49:03 +0000626 if (!matrix.invert(&inverse)) {
627 // assume fillPath and devPath are already empty.
628 return;
629 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 path.transform(inverse, &localPath);
631 // now localPath is only affected by the paint settings, and not the canvas matrix
632
reed@google.comfd4be262012-05-25 01:04:12 +0000633 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000634
reed@google.comfd4be262012-05-25 01:04:12 +0000635 if (fRec.fFrameWidth > 0) {
636 rec.setStrokeStyle(fRec.fFrameWidth,
637 SkToBool(fRec.fFlags & kFrameAndFill_Flag));
638 // glyphs are always closed contours, so cap type is ignored,
639 // so we just pass something.
640 rec.setStrokeParams(SkPaint::kButt_Cap,
641 (SkPaint::Join)fRec.fStrokeJoin,
642 fRec.fMiterLimit);
643 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000644
reed@google.com7db9fe62011-04-11 11:57:54 +0000645 if (fPathEffect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 SkPath effectPath;
reed@google.comfd4be262012-05-25 01:04:12 +0000647 if (fPathEffect->filterPath(&effectPath, localPath, &rec)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 localPath.swap(effectPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000649 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 }
651
reed@google.comfd4be262012-05-25 01:04:12 +0000652 if (rec.needToApply()) {
653 SkPath strokePath;
654 if (rec.applyToPath(&strokePath, localPath)) {
655 localPath.swap(strokePath);
656 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 }
reed@google.com82065d62011-02-07 15:30:46 +0000658
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 // now return stuff to the caller
reed@google.com7db9fe62011-04-11 11:57:54 +0000660 if (fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 *fillToDevMatrix = matrix;
reed@google.com7db9fe62011-04-11 11:57:54 +0000662 }
663 if (devPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 localPath.transform(matrix, devPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000665 }
666 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 fillPath->swap(localPath);
reed@google.com7db9fe62011-04-11 11:57:54 +0000668 }
669 } else { // nothing tricky to do
670 if (fillToDevMatrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 fillToDevMatrix->reset();
reed@google.com7db9fe62011-04-11 11:57:54 +0000672 }
673 if (devPath) {
674 if (fillPath == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 devPath->swap(path);
reed@google.com7db9fe62011-04-11 11:57:54 +0000676 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 *devPath = 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 (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 fillPath->swap(path);
reed@google.com7db9fe62011-04-11 11:57:54 +0000683 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 }
reed@google.com82065d62011-02-07 15:30:46 +0000685
reed@google.com7db9fe62011-04-11 11:57:54 +0000686 if (devPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 devPath->updateBoundsCache();
reed@google.com7db9fe62011-04-11 11:57:54 +0000688 }
689 if (fillPath) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 fillPath->updateBoundsCache();
reed@google.com7db9fe62011-04-11 11:57:54 +0000691 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692}
693
694
reed@google.coma9d4e842012-08-14 19:13:55 +0000695void SkScalerContextRec::getMatrixFrom2x2(SkMatrix* dst) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696 dst->reset();
697 dst->setScaleX(fPost2x2[0][0]);
698 dst->setSkewX( fPost2x2[0][1]);
699 dst->setSkewY( fPost2x2[1][0]);
700 dst->setScaleY(fPost2x2[1][1]);
701}
702
reed@google.coma9d4e842012-08-14 19:13:55 +0000703void SkScalerContextRec::getLocalMatrix(SkMatrix* m) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
reed@google.com7db9fe62011-04-11 11:57:54 +0000705 if (fPreSkewX) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 m->postSkew(fPreSkewX, 0);
reed@google.com7db9fe62011-04-11 11:57:54 +0000707 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708}
709
reed@google.coma9d4e842012-08-14 19:13:55 +0000710void SkScalerContextRec::getSingleMatrix(SkMatrix* m) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 this->getLocalMatrix(m);
712
713 // now concat the device matrix
reed@google.com7db9fe62011-04-11 11:57:54 +0000714 SkMatrix deviceMatrix;
715 this->getMatrixFrom2x2(&deviceMatrix);
716 m->postConcat(deviceMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717}
718
reed@google.com2e684782011-08-24 15:38:46 +0000719SkAxisAlignment SkComputeAxisAlignmentForHText(const SkMatrix& matrix) {
720 SkASSERT(!matrix.hasPerspective());
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000721
reed@google.com2e684782011-08-24 15:38:46 +0000722 if (0 == matrix[SkMatrix::kMSkewY]) {
723 return kX_SkAxisAlignment;
724 }
725 if (0 == matrix[SkMatrix::kMScaleX]) {
726 return kY_SkAxisAlignment;
727 }
728 return kNone_SkAxisAlignment;
729}
730
reed@android.com62900b42009-02-11 15:07:19 +0000731///////////////////////////////////////////////////////////////////////////////
732
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733#include "SkFontHost.h"
734
reed@android.com62900b42009-02-11 15:07:19 +0000735class SkScalerContext_Empty : public SkScalerContext {
736public:
737 SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {}
738
739protected:
ctguil@chromium.org0bc7bf52011-03-04 19:04:57 +0000740 virtual unsigned generateGlyphCount() {
reed@android.com62900b42009-02-11 15:07:19 +0000741 return 0;
742 }
743 virtual uint16_t generateCharToGlyph(SkUnichar uni) {
744 return 0;
745 }
746 virtual void generateAdvance(SkGlyph* glyph) {
747 glyph->zeroMetrics();
748 }
749 virtual void generateMetrics(SkGlyph* glyph) {
750 glyph->zeroMetrics();
751 }
bungeman@google.com97efada2012-07-30 20:40:50 +0000752 virtual void generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) {}
reed@android.com62900b42009-02-11 15:07:19 +0000753 virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
754 virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
755 SkPaint::FontMetrics* my) {
756 if (mx) {
reed@android.com4516f472009-06-29 16:25:36 +0000757 sk_bzero(mx, sizeof(*mx));
reed@android.com62900b42009-02-11 15:07:19 +0000758 }
759 if (my) {
reed@android.com4516f472009-06-29 16:25:36 +0000760 sk_bzero(my, sizeof(*my));
reed@android.com62900b42009-02-11 15:07:19 +0000761 }
762 }
763};
764
reed@android.comf2b98d62010-12-20 18:26:13 +0000765extern SkScalerContext* SkCreateColorScalerContext(const SkDescriptor* desc);
766
reed@google.com7db9fe62011-04-11 11:57:54 +0000767SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc) {
robertphillips@google.coma22e2112012-08-16 14:58:06 +0000768 SkScalerContext* c = NULL; //SkCreateColorScalerContext(desc);
769 if (NULL == c) {
770 c = SkFontHost::CreateScalerContext(desc);
771 }
reed@android.com62900b42009-02-11 15:07:19 +0000772 if (NULL == c) {
773 c = SkNEW_ARGS(SkScalerContext_Empty, (desc));
774 }
775 return c;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776}
777