blob: 50012960493a956a2f4a1be95c834e7f070c3af4 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/sgl/SkDraw.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
reed@google.coma76de3d2011-01-13 18:30:42 +00005** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
reed@android.com8a1c16f2008-12-17 15:59:43 +00008**
reed@google.coma76de3d2011-01-13 18:30:42 +00009** http://www.apache.org/licenses/LICENSE-2.0
reed@android.com8a1c16f2008-12-17 15:59:43 +000010**
reed@google.coma76de3d2011-01-13 18:30:42 +000011** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
reed@android.com8a1c16f2008-12-17 15:59:43 +000015** limitations under the License.
16*/
17
18#include "SkDraw.h"
19#include "SkBlitter.h"
20#include "SkBounder.h"
21#include "SkCanvas.h"
22#include "SkColorPriv.h"
23#include "SkDevice.h"
24#include "SkMaskFilter.h"
25#include "SkPaint.h"
26#include "SkPathEffect.h"
27#include "SkRasterizer.h"
28#include "SkScan.h"
29#include "SkShader.h"
30#include "SkStroke.h"
31#include "SkTemplatesPriv.h"
vandebo@chromium.org28be72b2010-11-11 21:37:00 +000032#include "SkTextFormatParams.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000033#include "SkUtils.h"
34
35#include "SkAutoKern.h"
36#include "SkBitmapProcShader.h"
37#include "SkDrawProcs.h"
38
39//#define TRACE_BITMAP_DRAWS
40
reed@android.com8a1c16f2008-12-17 15:59:43 +000041#define kBlitterStorageLongCount (sizeof(SkBitmapProcShader) >> 2)
42
reed@google.comfd4236e2011-07-25 21:16:22 +000043/** Helper for allocating small blitters on the stack.
44 */
reed@google.com40c2ba22011-07-25 19:41:22 +000045class SkAutoBlitterChoose : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000046public:
reed@google.com1d6ee0b2011-07-05 17:44:56 +000047 SkAutoBlitterChoose() {
48 fBlitter = NULL;
49 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000050 SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix,
51 const SkPaint& paint) {
52 fBlitter = SkBlitter::Choose(device, matrix, paint,
53 fStorage, sizeof(fStorage));
54 }
reed@google.com1d6ee0b2011-07-05 17:44:56 +000055
reed@android.com8a1c16f2008-12-17 15:59:43 +000056 ~SkAutoBlitterChoose();
57
58 SkBlitter* operator->() { return fBlitter; }
59 SkBlitter* get() const { return fBlitter; }
60
reed@google.com1d6ee0b2011-07-05 17:44:56 +000061 void choose(const SkBitmap& device, const SkMatrix& matrix,
62 const SkPaint& paint) {
63 SkASSERT(!fBlitter);
64 fBlitter = SkBlitter::Choose(device, matrix, paint,
65 fStorage, sizeof(fStorage));
66 }
67
reed@android.com8a1c16f2008-12-17 15:59:43 +000068private:
69 SkBlitter* fBlitter;
70 uint32_t fStorage[kBlitterStorageLongCount];
71};
72
73SkAutoBlitterChoose::~SkAutoBlitterChoose() {
74 if ((void*)fBlitter == (void*)fStorage) {
75 fBlitter->~SkBlitter();
76 } else {
77 SkDELETE(fBlitter);
78 }
79}
80
reed@google.com40c2ba22011-07-25 19:41:22 +000081/**
82 * Since we are providing the storage for the shader (to avoid the perf cost
83 * of calling new) we insist that in our destructor we can account for all
84 * owners of the shader.
85 */
86class SkAutoBitmapShaderInstall : SkNoncopyable {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087public:
reed@google.com40c2ba22011-07-25 19:41:22 +000088 SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint& paint)
89 : fPaint(paint) /* makes a copy of the paint */ {
90 fPaint.setShader(SkShader::CreateBitmapShader(src,
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
92 fStorage, sizeof(fStorage)));
reed@google.com40c2ba22011-07-25 19:41:22 +000093 // we deliberately left the shader with an owner-count of 2
94 SkASSERT(2 == fPaint.getShader()->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 }
reed@google.com82065d62011-02-07 15:30:46 +000096
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 ~SkAutoBitmapShaderInstall() {
reed@google.com40c2ba22011-07-25 19:41:22 +000098 SkShader* shader = fPaint.getShader();
99 // since we manually destroy shader, we insist that owners == 2
100 SkASSERT(2 == shader->getRefCnt());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101
reed@google.com40c2ba22011-07-25 19:41:22 +0000102 fPaint.setShader(NULL); // unref the shader by 1
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103
reed@google.com40c2ba22011-07-25 19:41:22 +0000104 // now destroy to take care of the 2nd owner-count
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 if ((void*)shader == (void*)fStorage) {
106 shader->~SkShader();
107 } else {
108 SkDELETE(shader);
109 }
110 }
reed@google.com82065d62011-02-07 15:30:46 +0000111
reed@google.com40c2ba22011-07-25 19:41:22 +0000112 // return the new paint that has the shader applied
113 const SkPaint& paintWithShader() const { return fPaint; }
114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115private:
reed@google.com40c2ba22011-07-25 19:41:22 +0000116 SkPaint fPaint; // copy of caller's paint (which we then modify)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 uint32_t fStorage[kBlitterStorageLongCount];
118};
119
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120///////////////////////////////////////////////////////////////////////////////
121
reed@android.comf2b98d62010-12-20 18:26:13 +0000122SkDraw::SkDraw() {
123 sk_bzero(this, sizeof(*this));
124}
125
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126SkDraw::SkDraw(const SkDraw& src) {
127 memcpy(this, &src, sizeof(*this));
128}
129
130///////////////////////////////////////////////////////////////////////////////
131
132typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
133
134static void D_Clear_BitmapXferProc(void* pixels, size_t bytes, uint32_t) {
reed@android.com4516f472009-06-29 16:25:36 +0000135 sk_bzero(pixels, bytes);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136}
137
138static void D_Dst_BitmapXferProc(void*, size_t, uint32_t data) {}
139
140static void D32_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
141 sk_memset32((uint32_t*)pixels, data, bytes >> 2);
142}
143
144static void D16_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
145 sk_memset16((uint16_t*)pixels, data, bytes >> 1);
146}
147
148static void DA8_Src_BitmapXferProc(void* pixels, size_t bytes, uint32_t data) {
149 memset(pixels, data, bytes);
150}
151
152static BitmapXferProc ChooseBitmapXferProc(const SkBitmap& bitmap,
153 const SkPaint& paint,
154 uint32_t* data) {
155 // todo: we can apply colorfilter up front if no shader, so we wouldn't
156 // need to abort this fastpath
157 if (paint.getShader() || paint.getColorFilter()) {
158 return NULL;
159 }
160
reed@android.com845fdac2009-06-23 03:01:32 +0000161 SkXfermode::Mode mode;
162 if (!SkXfermode::IsMode(paint.getXfermode(), &mode)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 return NULL;
164 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000165
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 SkColor color = paint.getColor();
reed@google.coma76de3d2011-01-13 18:30:42 +0000167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 // collaps modes based on color...
reed@android.com845fdac2009-06-23 03:01:32 +0000169 if (SkXfermode::kSrcOver_Mode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170 unsigned alpha = SkColorGetA(color);
171 if (0 == alpha) {
reed@android.com845fdac2009-06-23 03:01:32 +0000172 mode = SkXfermode::kDst_Mode;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 } else if (0xFF == alpha) {
reed@android.com845fdac2009-06-23 03:01:32 +0000174 mode = SkXfermode::kSrc_Mode;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 }
176 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 switch (mode) {
reed@android.com845fdac2009-06-23 03:01:32 +0000179 case SkXfermode::kClear_Mode:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180// SkDebugf("--- D_Clear_BitmapXferProc\n");
181 return D_Clear_BitmapXferProc; // ignore data
reed@android.com845fdac2009-06-23 03:01:32 +0000182 case SkXfermode::kDst_Mode:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183// SkDebugf("--- D_Dst_BitmapXferProc\n");
184 return D_Dst_BitmapXferProc; // ignore data
reed@android.com845fdac2009-06-23 03:01:32 +0000185 case SkXfermode::kSrc_Mode: {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 /*
reed@google.coma76de3d2011-01-13 18:30:42 +0000187 should I worry about dithering for the lower depths?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 */
189 SkPMColor pmc = SkPreMultiplyColor(color);
190 switch (bitmap.config()) {
191 case SkBitmap::kARGB_8888_Config:
192 if (data) {
193 *data = pmc;
194 }
195// SkDebugf("--- D32_Src_BitmapXferProc\n");
196 return D32_Src_BitmapXferProc;
197 case SkBitmap::kARGB_4444_Config:
198 if (data) {
199 *data = SkPixel32ToPixel4444(pmc);
200 }
201// SkDebugf("--- D16_Src_BitmapXferProc\n");
202 return D16_Src_BitmapXferProc;
203 case SkBitmap::kRGB_565_Config:
204 if (data) {
205 *data = SkPixel32ToPixel16(pmc);
206 }
207// SkDebugf("--- D16_Src_BitmapXferProc\n");
208 return D16_Src_BitmapXferProc;
209 case SkBitmap::kA8_Config:
210 if (data) {
211 *data = SkGetPackedA32(pmc);
212 }
213// SkDebugf("--- DA8_Src_BitmapXferProc\n");
214 return DA8_Src_BitmapXferProc;
215 default:
216 break;
217 }
218 break;
219 }
220 default:
221 break;
222 }
223 return NULL;
224}
225
226static void CallBitmapXferProc(const SkBitmap& bitmap, const SkIRect& rect,
227 BitmapXferProc proc, uint32_t procData) {
228 int shiftPerPixel;
229 switch (bitmap.config()) {
230 case SkBitmap::kARGB_8888_Config:
231 shiftPerPixel = 2;
232 break;
233 case SkBitmap::kARGB_4444_Config:
234 case SkBitmap::kRGB_565_Config:
235 shiftPerPixel = 1;
236 break;
237 case SkBitmap::kA8_Config:
238 shiftPerPixel = 0;
239 break;
240 default:
241 SkASSERT(!"Can't use xferproc on this config");
242 return;
243 }
244
245 uint8_t* pixels = (uint8_t*)bitmap.getPixels();
246 SkASSERT(pixels);
247 const size_t rowBytes = bitmap.rowBytes();
248 const int widthBytes = rect.width() << shiftPerPixel;
249
250 // skip down to the first scanline and X position
251 pixels += rect.fTop * rowBytes + (rect.fLeft << shiftPerPixel);
252 for (int scans = rect.height() - 1; scans >= 0; --scans) {
253 proc(pixels, widthBytes, procData);
254 pixels += rowBytes;
255 }
256}
257
258void SkDraw::drawPaint(const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000259 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260
261 if (fClip->isEmpty()) {
262 return;
263 }
264
265 SkIRect devRect;
266 devRect.set(0, 0, fBitmap->width(), fBitmap->height());
267 if (fBounder && !fBounder->doIRect(devRect)) {
268 return;
269 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000270
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 /* If we don't have a shader (i.e. we're just a solid color) we may
272 be faster to operate directly on the device bitmap, rather than invoking
273 a blitter. Esp. true for xfermodes, which require a colorshader to be
274 present, which is just redundant work. Since we're drawing everywhere
275 in the clip, we don't have to worry about antialiasing.
276 */
277 uint32_t procData = 0; // to avoid the warning
278 BitmapXferProc proc = ChooseBitmapXferProc(*fBitmap, paint, &procData);
279 if (proc) {
280 if (D_Dst_BitmapXferProc == proc) { // nothing to do
281 return;
282 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000283
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 SkRegion::Iterator iter(*fClip);
285 while (!iter.done()) {
286 CallBitmapXferProc(*fBitmap, iter.rect(), proc, procData);
287 iter.next();
288 }
289 } else {
290 // normal case: use a blitter
291 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
292 SkScan::FillIRect(devRect, fClip, blitter.get());
293 }
294}
295
296///////////////////////////////////////////////////////////////////////////////
297
298struct PtProcRec {
299 SkCanvas::PointMode fMode;
300 const SkPaint* fPaint;
301 const SkRegion* fClip;
reed@google.coma76de3d2011-01-13 18:30:42 +0000302
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 // computed values
304 SkFixed fRadius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000305
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 typedef void (*Proc)(const PtProcRec&, const SkPoint devPts[], int count,
307 SkBlitter*);
308
309 bool init(SkCanvas::PointMode, const SkPaint&, const SkMatrix* matrix,
reed@google.coma76de3d2011-01-13 18:30:42 +0000310 const SkRegion* clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 Proc chooseProc(SkBlitter* blitter);
312};
313
314static void bw_pt_rect_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
315 int count, SkBlitter* blitter) {
316 SkASSERT(rec.fClip->isRect());
317 const SkIRect& r = rec.fClip->getBounds();
reed@google.coma76de3d2011-01-13 18:30:42 +0000318
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 for (int i = 0; i < count; i++) {
320 int x = SkScalarFloor(devPts[i].fX);
321 int y = SkScalarFloor(devPts[i].fY);
322 if (r.contains(x, y)) {
323 blitter->blitH(x, y, 1);
324 }
325 }
326}
327
328static void bw_pt_rect_16_hair_proc(const PtProcRec& rec,
329 const SkPoint devPts[], int count,
330 SkBlitter* blitter) {
331 SkASSERT(rec.fClip->isRect());
332 const SkIRect& r = rec.fClip->getBounds();
333 uint32_t value;
334 const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value);
335 SkASSERT(bitmap);
reed@google.coma76de3d2011-01-13 18:30:42 +0000336
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 uint16_t* addr = bitmap->getAddr16(0, 0);
338 int rb = bitmap->rowBytes();
339
340 for (int i = 0; i < count; i++) {
341 int x = SkScalarFloor(devPts[i].fX);
342 int y = SkScalarFloor(devPts[i].fY);
343 if (r.contains(x, y)) {
344// *bitmap->getAddr16(x, y) = SkToU16(value);
345 ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
346 }
347 }
348}
349
350static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
351 int count, SkBlitter* blitter) {
352 for (int i = 0; i < count; i++) {
353 int x = SkScalarFloor(devPts[i].fX);
354 int y = SkScalarFloor(devPts[i].fY);
355 if (rec.fClip->contains(x, y)) {
356 blitter->blitH(x, y, 1);
357 }
358 }
359}
360
361static void bw_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
362 int count, SkBlitter* blitter) {
363 for (int i = 0; i < count; i += 2) {
364 SkScan::HairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
365 }
366}
367
368static void bw_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
369 int count, SkBlitter* blitter) {
370 for (int i = 0; i < count - 1; i++) {
371 SkScan::HairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
372 }
373}
374
375// aa versions
376
377static void aa_line_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
378 int count, SkBlitter* blitter) {
379 for (int i = 0; i < count; i += 2) {
380 SkScan::AntiHairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
381 }
382}
383
384static void aa_poly_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
385 int count, SkBlitter* blitter) {
386 for (int i = 0; i < count - 1; i++) {
387 SkScan::AntiHairLine(devPts[i], devPts[i+1], rec.fClip, blitter);
388 }
389}
390
391// square procs (strokeWidth > 0 but matrix is square-scale (sx == sy)
392
393static void bw_square_proc(const PtProcRec& rec, const SkPoint devPts[],
394 int count, SkBlitter* blitter) {
395 const SkFixed radius = rec.fRadius;
396 for (int i = 0; i < count; i++) {
397 SkFixed x = SkScalarToFixed(devPts[i].fX);
398 SkFixed y = SkScalarToFixed(devPts[i].fY);
reed@google.coma76de3d2011-01-13 18:30:42 +0000399
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 SkXRect r;
401 r.fLeft = x - radius;
402 r.fTop = y - radius;
403 r.fRight = x + radius;
404 r.fBottom = y + radius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000405
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406 SkScan::FillXRect(r, rec.fClip, blitter);
407 }
408}
409
410static void aa_square_proc(const PtProcRec& rec, const SkPoint devPts[],
411 int count, SkBlitter* blitter) {
412 const SkFixed radius = rec.fRadius;
413 for (int i = 0; i < count; i++) {
414 SkFixed x = SkScalarToFixed(devPts[i].fX);
415 SkFixed y = SkScalarToFixed(devPts[i].fY);
reed@google.coma76de3d2011-01-13 18:30:42 +0000416
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 SkXRect r;
418 r.fLeft = x - radius;
419 r.fTop = y - radius;
420 r.fRight = x + radius;
421 r.fBottom = y + radius;
reed@google.coma76de3d2011-01-13 18:30:42 +0000422
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 SkScan::AntiFillXRect(r, rec.fClip, blitter);
424 }
425}
426
reed@android.comb4f404a2009-07-10 17:02:17 +0000427// If this guy returns true, then chooseProc() must return a valid proc
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428bool PtProcRec::init(SkCanvas::PointMode mode, const SkPaint& paint,
429 const SkMatrix* matrix, const SkRegion* clip) {
430 if (paint.getPathEffect()) {
431 return false;
432 }
433 SkScalar width = paint.getStrokeWidth();
434 if (0 == width) {
435 fMode = mode;
436 fPaint = &paint;
437 fClip = clip;
438 fRadius = SK_Fixed1 >> 1;
439 return true;
440 }
reed@android.comb4f404a2009-07-10 17:02:17 +0000441 if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
442 matrix->rectStaysRect() && SkCanvas::kPoints_PointMode == mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 SkScalar sx = matrix->get(SkMatrix::kMScaleX);
444 SkScalar sy = matrix->get(SkMatrix::kMScaleY);
445 if (SkScalarNearlyZero(sx - sy)) {
446 if (sx < 0) {
447 sx = -sx;
448 }
449
450 fMode = mode;
451 fPaint = &paint;
452 fClip = clip;
453 fRadius = SkScalarToFixed(SkScalarMul(width, sx)) >> 1;
454 return true;
455 }
456 }
457 return false;
458}
459
460PtProcRec::Proc PtProcRec::chooseProc(SkBlitter* blitter) {
reed@android.comb4f404a2009-07-10 17:02:17 +0000461 Proc proc = NULL;
reed@google.coma76de3d2011-01-13 18:30:42 +0000462
reed@android.com8a1c16f2008-12-17 15:59:43 +0000463 // for our arrays
464 SkASSERT(0 == SkCanvas::kPoints_PointMode);
465 SkASSERT(1 == SkCanvas::kLines_PointMode);
466 SkASSERT(2 == SkCanvas::kPolygon_PointMode);
467 SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
468
469 // first check for hairlines
470 if (0 == fPaint->getStrokeWidth()) {
471 if (fPaint->isAntiAlias()) {
472 static const Proc gAAProcs[] = {
473 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
474 };
475 proc = gAAProcs[fMode];
476 } else {
477 if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
478 uint32_t value;
479 const SkBitmap* bm = blitter->justAnOpaqueColor(&value);
480 if (bm && bm->config() == SkBitmap::kRGB_565_Config) {
481 proc = bw_pt_rect_16_hair_proc;
482 } else {
483 proc = bw_pt_rect_hair_proc;
484 }
485 } else {
486 static Proc gBWProcs[] = {
487 bw_pt_hair_proc, bw_line_hair_proc, bw_poly_hair_proc
488 };
489 proc = gBWProcs[fMode];
490 }
491 }
reed@android.comb4f404a2009-07-10 17:02:17 +0000492 } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 SkASSERT(SkCanvas::kPoints_PointMode == fMode);
494 if (fPaint->isAntiAlias()) {
495 proc = aa_square_proc;
496 } else {
497 proc = bw_square_proc;
498 }
499 }
500 return proc;
501}
502
503static bool bounder_points(SkBounder* bounder, SkCanvas::PointMode mode,
504 size_t count, const SkPoint pts[],
505 const SkPaint& paint, const SkMatrix& matrix) {
506 SkIRect ibounds;
507 SkRect bounds;
508 SkScalar inset = paint.getStrokeWidth();
509
510 bounds.set(pts, count);
511 bounds.inset(-inset, -inset);
512 matrix.mapRect(&bounds);
513
514 bounds.roundOut(&ibounds);
515 return bounder->doIRect(ibounds);
516}
517
518// each of these costs 8-bytes of stack space, so don't make it too large
519// must be even for lines/polygon to work
520#define MAX_DEV_PTS 32
521
522void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
reed@android.comf2b98d62010-12-20 18:26:13 +0000523 const SkPoint pts[], const SkPaint& paint,
524 bool forceUseDevice) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 // if we're in lines mode, force count to be even
526 if (SkCanvas::kLines_PointMode == mode) {
527 count &= ~(size_t)1;
528 }
529
530 if ((long)count <= 0) {
531 return;
532 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 SkASSERT(pts != NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000535 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +0000536
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 // nothing to draw
538 if (fClip->isEmpty() ||
539 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
540 return;
541 }
542
reed@google.comfd4236e2011-07-25 21:16:22 +0000543 if (fBounder) {
544 if (!bounder_points(fBounder, mode, count, pts, paint, *fMatrix)) {
545 return;
546 }
547
548 // clear the bounder and call this again, so we don't invoke the bounder
549 // later if we happen to call ourselves for drawRect, drawPath, etc.
550 SkDraw noBounder(*this);
551 noBounder.fBounder = NULL;
552 noBounder.drawPoints(mode, count, pts, paint, forceUseDevice);
553 return;
554 }
555
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 PtProcRec rec;
reed@android.comf2b98d62010-12-20 18:26:13 +0000557 if (!forceUseDevice && rec.init(mode, paint, fMatrix, fClip)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
559
560 SkPoint devPts[MAX_DEV_PTS];
561 const SkMatrix* matrix = fMatrix;
562 SkBlitter* bltr = blitter.get();
563 PtProcRec::Proc proc = rec.chooseProc(bltr);
564 // we have to back up subsequent passes if we're in polygon mode
565 const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
reed@google.coma76de3d2011-01-13 18:30:42 +0000566
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 do {
568 size_t n = count;
569 if (n > MAX_DEV_PTS) {
570 n = MAX_DEV_PTS;
571 }
572 matrix->mapPoints(devPts, pts, n);
573 proc(rec, devPts, n, bltr);
574 pts += n - backup;
575 SkASSERT(count >= n);
576 count -= n;
577 if (count > 0) {
578 count += backup;
579 }
580 } while (count != 0);
581 } else {
582 switch (mode) {
583 case SkCanvas::kPoints_PointMode: {
584 // temporarily mark the paint as filling.
reed@google.com40c2ba22011-07-25 19:41:22 +0000585 SkPaint newPaint(paint);
586 newPaint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587
reed@google.com40c2ba22011-07-25 19:41:22 +0000588 SkScalar width = newPaint.getStrokeWidth();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 SkScalar radius = SkScalarHalf(width);
reed@google.coma76de3d2011-01-13 18:30:42 +0000590
reed@google.com40c2ba22011-07-25 19:41:22 +0000591 if (newPaint.getStrokeCap() == SkPaint::kRound_Cap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 SkPath path;
593 SkMatrix preMatrix;
reed@google.coma76de3d2011-01-13 18:30:42 +0000594
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 path.addCircle(0, 0, radius);
596 for (size_t i = 0; i < count; i++) {
597 preMatrix.setTranslate(pts[i].fX, pts[i].fY);
598 // pass true for the last point, since we can modify
599 // then path then
reed@android.comf2b98d62010-12-20 18:26:13 +0000600 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000601 fDevice->drawPath(*this, path, newPaint, &preMatrix,
reed@android.comf2b98d62010-12-20 18:26:13 +0000602 (count-1) == i);
603 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000604 this->drawPath(path, newPaint, &preMatrix,
605 (count-1) == i);
reed@android.comf2b98d62010-12-20 18:26:13 +0000606 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 }
608 } else {
609 SkRect r;
reed@google.coma76de3d2011-01-13 18:30:42 +0000610
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 for (size_t i = 0; i < count; i++) {
612 r.fLeft = pts[i].fX - radius;
613 r.fTop = pts[i].fY - radius;
614 r.fRight = r.fLeft + width;
615 r.fBottom = r.fTop + width;
reed@android.comf2b98d62010-12-20 18:26:13 +0000616 if (fDevice) {
reed@google.com40c2ba22011-07-25 19:41:22 +0000617 fDevice->drawRect(*this, r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000618 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +0000619 this->drawRect(r, newPaint);
reed@android.comf2b98d62010-12-20 18:26:13 +0000620 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 }
622 }
623 break;
624 }
625 case SkCanvas::kLines_PointMode:
626 case SkCanvas::kPolygon_PointMode: {
627 count -= 1;
628 SkPath path;
629 SkPaint p(paint);
630 p.setStyle(SkPaint::kStroke_Style);
631 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
632 for (size_t i = 0; i < count; i += inc) {
633 path.moveTo(pts[i]);
634 path.lineTo(pts[i+1]);
reed@android.comf2b98d62010-12-20 18:26:13 +0000635 if (fDevice) {
636 fDevice->drawPath(*this, path, p, NULL, true);
637 } else {
638 this->drawPath(path, p, NULL, true);
639 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 path.rewind();
641 }
642 break;
643 }
644 }
645 }
646}
647
648static inline SkPoint* as_lefttop(SkRect* r) {
649 return (SkPoint*)(void*)r;
650}
651
652static inline SkPoint* as_rightbottom(SkRect* r) {
653 return ((SkPoint*)(void*)r) + 1;
654}
655
reed@google.com761fb622011-04-04 18:58:05 +0000656static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
657 SkPoint* strokeSize) {
658 if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
659 paint.getStrokeMiter() < SK_ScalarSqrt2) {
660 return false;
661 }
reed@google.com62ab7ad2011-04-05 14:08:25 +0000662
reed@google.com761fb622011-04-04 18:58:05 +0000663 SkASSERT(matrix.rectStaysRect());
664 SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
665 matrix.mapVectors(strokeSize, &pt, 1);
reed@google.com61153382011-04-05 13:05:18 +0000666 strokeSize->fX = SkScalarAbs(strokeSize->fX);
667 strokeSize->fY = SkScalarAbs(strokeSize->fY);
reed@google.com761fb622011-04-04 18:58:05 +0000668 return true;
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000669}
670
reed@google.com62ab7ad2011-04-05 14:08:25 +0000671SkDraw::RectType SkDraw::ComputeRectType(const SkPaint& paint,
672 const SkMatrix& matrix,
673 SkPoint* strokeSize) {
674 RectType rtype;
675 const SkScalar width = paint.getStrokeWidth();
676 const bool zeroWidth = (0 == width);
677 SkPaint::Style style = paint.getStyle();
678
679 if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
680 style = SkPaint::kFill_Style;
681 }
682
683 if (paint.getPathEffect() || paint.getMaskFilter() ||
684 paint.getRasterizer() || !matrix.rectStaysRect() ||
685 SkPaint::kStrokeAndFill_Style == style) {
686 rtype = kPath_RectType;
687 } else if (SkPaint::kFill_Style == style) {
688 rtype = kFill_RectType;
689 } else if (zeroWidth) {
690 rtype = kHair_RectType;
691 } else if (easy_rect_join(paint, matrix, strokeSize)) {
692 rtype = kStroke_RectType;
693 } else {
694 rtype = kPath_RectType;
695 }
696 return rtype;
697}
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000698
reed@google.com40c2ba22011-07-25 19:41:22 +0000699static SkPoint* rect_points(SkRect& r, int index) {
700 SkASSERT((unsigned)index < 2);
701 return &((SkPoint*)(void*)&r)[index];
702}
703
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000705 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706
707 // nothing to draw
708 if (fClip->isEmpty() ||
709 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
710 return;
711 }
712
reed@google.com761fb622011-04-04 18:58:05 +0000713 SkPoint strokeSize;
reed@google.com62ab7ad2011-04-05 14:08:25 +0000714 RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000715
reed@google.com5bff8d22011-04-04 14:47:09 +0000716#ifdef SK_DISABLE_FAST_AA_STROKE_RECT
reed@google.com62ab7ad2011-04-05 14:08:25 +0000717 if (kStroke_RectType == rtype && paint.isAntiAlias()) {
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000718 rtype = kPath_RectType;
719 }
reed@google.com62ab7ad2011-04-05 14:08:25 +0000720#endif
721
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000722 if (kPath_RectType == rtype) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 SkPath tmp;
724 tmp.addRect(rect);
725 tmp.setFillType(SkPath::kWinding_FillType);
reed@android.com187d5592009-07-08 14:03:56 +0000726 this->drawPath(tmp, paint, NULL, true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 return;
728 }
729
730 const SkMatrix& matrix = *fMatrix;
731 SkRect devRect;
732
733 // transform rect into devRect
734 {
735 matrix.mapXY(rect.fLeft, rect.fTop, rect_points(devRect, 0));
736 matrix.mapXY(rect.fRight, rect.fBottom, rect_points(devRect, 1));
737 devRect.sort();
738 }
739
740 if (fBounder && !fBounder->doRect(devRect, paint)) {
741 return;
742 }
743
744 // look for the quick exit, before we build a blitter
745 {
746 SkIRect ir;
747 devRect.roundOut(&ir);
reed@android.com55e76b22009-11-23 21:46:47 +0000748 if (paint.getStyle() != SkPaint::kFill_Style) {
749 // extra space for hairlines
750 ir.inset(-1, -1);
751 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 if (fClip->quickReject(ir))
753 return;
754 }
755
756 SkAutoBlitterChoose blitterStorage(*fBitmap, matrix, paint);
757 SkBlitter* blitter = blitterStorage.get();
758 const SkRegion* clip = fClip;
759
reed@android.comb641c9f22010-03-25 14:31:46 +0000760 // we want to "fill" if we are kFill or kStrokeAndFill, since in the latter
761 // case we are also hairline (if we've gotten to here), which devolves to
762 // effectively just kFill
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000763 switch (rtype) {
764 case kFill_RectType:
765 if (paint.isAntiAlias()) {
766 SkScan::AntiFillRect(devRect, clip, blitter);
767 } else {
768 SkScan::FillRect(devRect, clip, blitter);
769 }
770 break;
771 case kStroke_RectType:
772 if (paint.isAntiAlias()) {
reed@google.com761fb622011-04-04 18:58:05 +0000773 SkScan::AntiFrameRect(devRect, strokeSize, clip, blitter);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000774 } else {
reed@google.com761fb622011-04-04 18:58:05 +0000775 SkScan::FrameRect(devRect, strokeSize, clip, blitter);
mike@reedtribe.org7ff678b2011-04-04 14:38:12 +0000776 }
777 break;
778 case kHair_RectType:
779 if (paint.isAntiAlias()) {
780 SkScan::AntiHairRect(devRect, clip, blitter);
781 } else {
782 SkScan::HairRect(devRect, clip, blitter);
783 }
784 break;
785 default:
786 SkASSERT(!"bad rtype");
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 }
788}
789
790void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const {
791 if (srcM.fBounds.isEmpty()) {
792 return;
793 }
794
795 SkMask dstM;
796 const SkMask* mask = &srcM;
797
798 dstM.fImage = NULL;
799 SkAutoMaskImage ami(&dstM, false);
800
801 if (paint.getMaskFilter() &&
802 paint.getMaskFilter()->filterMask(&dstM, srcM, *fMatrix, NULL)) {
803 mask = &dstM;
804 }
805
806 if (fBounder && !fBounder->doIRect(mask->fBounds)) {
807 return;
808 }
809
810 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
811
812 blitter->blitMaskRegion(*mask, *fClip);
813}
814
815class SkAutoPaintRestoreColorStrokeWidth {
816public:
817 SkAutoPaintRestoreColorStrokeWidth(const SkPaint& paint) {
818 fPaint = (SkPaint*)&paint;
819 fColor = paint.getColor();
820 fWidth = paint.getStrokeWidth();
821 }
822 ~SkAutoPaintRestoreColorStrokeWidth() {
823 fPaint->setColor(fColor);
824 fPaint->setStrokeWidth(fWidth);
825 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000826
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827private:
828 SkPaint* fPaint;
829 SkColor fColor;
830 SkScalar fWidth;
831};
832
reed@android.comebdeeb82009-09-03 21:45:49 +0000833static SkScalar fast_len(const SkVector& vec) {
834 SkScalar x = SkScalarAbs(vec.fX);
835 SkScalar y = SkScalarAbs(vec.fY);
836 if (x < y) {
837 SkTSwap(x, y);
838 }
839 return x + SkScalarHalf(y);
840}
841
842// our idea is to return true if there is no appreciable skew or non-square scale
843// for that we'll transform (0,1) and (1,0), and check that the resulting dot-prod
844// is nearly one
845static bool map_radius(const SkMatrix& matrix, SkScalar* value) {
tomhudson@google.com8d430182011-06-06 19:11:19 +0000846 if (matrix.hasPerspective()) {
reed@android.comebdeeb82009-09-03 21:45:49 +0000847 return false;
848 }
849 SkVector src[2], dst[2];
850 src[0].set(*value, 0);
851 src[1].set(0, *value);
852 matrix.mapVectors(dst, src, 2);
853 SkScalar len0 = fast_len(dst[0]);
854 SkScalar len1 = fast_len(dst[1]);
agl@chromium.org652807b2010-04-27 15:47:34 +0000855 if (len0 <= SK_Scalar1 && len1 <= SK_Scalar1) {
reed@android.comebdeeb82009-09-03 21:45:49 +0000856 *value = SkScalarAve(len0, len1);
857 return true;
858 }
859 return false;
860}
861
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& paint,
863 const SkMatrix* prePathMatrix, bool pathIsMutable) const {
reed@android.comf2b98d62010-12-20 18:26:13 +0000864 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865
866 // nothing to draw
867 if (fClip->isEmpty() ||
868 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
869 return;
870 }
871
872 SkPath* pathPtr = (SkPath*)&origSrcPath;
873 bool doFill = true;
874 SkPath tmpPath;
875 SkMatrix tmpMatrix;
876 const SkMatrix* matrix = fMatrix;
877
878 if (prePathMatrix) {
879 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style ||
880 paint.getRasterizer()) {
881 SkPath* result = pathPtr;
reed@google.coma76de3d2011-01-13 18:30:42 +0000882
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 if (!pathIsMutable) {
884 result = &tmpPath;
885 pathIsMutable = true;
886 }
887 pathPtr->transform(*prePathMatrix, result);
888 pathPtr = result;
889 } else {
890 if (!tmpMatrix.setConcat(*matrix, *prePathMatrix)) {
891 // overflow
892 return;
893 }
894 matrix = &tmpMatrix;
895 }
896 }
897 // at this point we're done with prePathMatrix
898 SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
reed@google.coma76de3d2011-01-13 18:30:42 +0000899
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 /*
901 If the device thickness < 1.0, then make it a hairline, and
902 modulate alpha if the thickness is even smaller (e.g. thickness == 0.5
903 should modulate the alpha by 1/2)
904 */
905
906 SkAutoPaintRestoreColorStrokeWidth aprc(paint);
reed@google.coma76de3d2011-01-13 18:30:42 +0000907
reed@android.comebdeeb82009-09-03 21:45:49 +0000908 // can we approximate a thin (but not hairline) stroke with an alpha-modulated
909 // hairline? Only if the matrix scales evenly in X and Y, and the device-width is
910 // less than a pixel
reed@android.comf2b98d62010-12-20 18:26:13 +0000911 if (paint.isAntiAlias() &&
912 paint.getStyle() == SkPaint::kStroke_Style && paint.getXfermode() == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 SkScalar width = paint.getStrokeWidth();
reed@android.comebdeeb82009-09-03 21:45:49 +0000914 if (width > 0 && map_radius(*matrix, &width)) {
915 int scale = (int)SkScalarMul(width, 256);
916 int alpha = paint.getAlpha() * scale >> 8;
reed@google.coma76de3d2011-01-13 18:30:42 +0000917
reed@android.comebdeeb82009-09-03 21:45:49 +0000918 // pretend to be a hairline, with a modulated alpha
919 ((SkPaint*)&paint)->setAlpha(alpha);
920 ((SkPaint*)&paint)->setStrokeWidth(0);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 }
922 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000923
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
925 doFill = paint.getFillPath(*pathPtr, &tmpPath);
926 pathPtr = &tmpPath;
927 }
reed@google.coma76de3d2011-01-13 18:30:42 +0000928
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 if (paint.getRasterizer()) {
930 SkMask mask;
931 if (paint.getRasterizer()->rasterize(*pathPtr, *matrix,
932 &fClip->getBounds(), paint.getMaskFilter(), &mask,
933 SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
934 this->drawDevMask(mask, paint);
935 SkMask::FreeImage(mask.fImage);
936 }
937 return;
938 }
939
940 // avoid possibly allocating a new path in transform if we can
941 SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
942
943 // transform the path into device space
944 pathPtr->transform(*matrix, devPathPtr);
945
946 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
947
948 // how does filterPath() know to fill or hairline the path??? <mrr>
949 if (paint.getMaskFilter() &&
950 paint.getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fClip,
951 fBounder, blitter.get())) {
952 return; // filterPath() called the blitter, so we're done
953 }
954
955 if (fBounder && !fBounder->doPath(*devPathPtr, paint, doFill)) {
956 return;
957 }
958
959 if (doFill) {
960 if (paint.isAntiAlias()) {
961 SkScan::AntiFillPath(*devPathPtr, *fClip, blitter.get());
962 } else {
963 SkScan::FillPath(*devPathPtr, *fClip, blitter.get());
964 }
965 } else { // hairline
966 if (paint.isAntiAlias()) {
967 SkScan::AntiHairPath(*devPathPtr, fClip, blitter.get());
968 } else {
969 SkScan::HairPath(*devPathPtr, fClip, blitter.get());
970 }
971 }
972}
973
reed@android.com0baf1932009-06-24 12:41:42 +0000974/** For the purposes of drawing bitmaps, if a matrix is "almost" translate
975 go ahead and treat it as if it were, so that subsequent code can go fast.
976 */
977static bool just_translate(const SkMatrix& matrix, const SkBitmap& bitmap) {
978 SkMatrix::TypeMask mask = matrix.getType();
979
980 if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
981 return false;
982 }
983 if (mask & SkMatrix::kScale_Mask) {
984 SkScalar sx = matrix[SkMatrix::kMScaleX];
985 SkScalar sy = matrix[SkMatrix::kMScaleY];
986 int w = bitmap.width();
987 int h = bitmap.height();
988 int sw = SkScalarRound(SkScalarMul(sx, SkIntToScalar(w)));
989 int sh = SkScalarRound(SkScalarMul(sy, SkIntToScalar(h)));
990 return sw == w && sh == h;
991 }
992 // if we got here, we're either kTranslate_Mask or identity
993 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994}
995
996void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap,
997 const SkPaint& paint) const {
998 SkASSERT(bitmap.getConfig() == SkBitmap::kA8_Config);
999
reed@google.coma76de3d2011-01-13 18:30:42 +00001000 if (just_translate(*fMatrix, bitmap)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001 int ix = SkScalarRound(fMatrix->getTranslateX());
1002 int iy = SkScalarRound(fMatrix->getTranslateY());
1003
1004 SkMask mask;
1005 mask.fBounds.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
1006 mask.fFormat = SkMask::kA8_Format;
1007 mask.fRowBytes = bitmap.rowBytes();
1008 mask.fImage = bitmap.getAddr8(0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001009
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 this->drawDevMask(mask, paint);
1011 } else { // need to xform the bitmap first
1012 SkRect r;
1013 SkMask mask;
reed@google.coma76de3d2011-01-13 18:30:42 +00001014
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 r.set(0, 0,
1016 SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
1017 fMatrix->mapRect(&r);
1018 r.round(&mask.fBounds);
reed@google.coma76de3d2011-01-13 18:30:42 +00001019
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020 // set the mask's bounds to the transformed bitmap-bounds,
1021 // clipped to the actual device
1022 {
1023 SkIRect devBounds;
1024 devBounds.set(0, 0, fBitmap->width(), fBitmap->height());
1025 // need intersect(l, t, r, b) on irect
1026 if (!mask.fBounds.intersect(devBounds)) {
1027 return;
1028 }
1029 }
reed@android.com543ed932009-04-24 12:43:40 +00001030
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031 mask.fFormat = SkMask::kA8_Format;
1032 mask.fRowBytes = SkAlign4(mask.fBounds.width());
reed@android.com543ed932009-04-24 12:43:40 +00001033 size_t size = mask.computeImageSize();
1034 if (0 == size) {
1035 // the mask is too big to allocated, draw nothing
1036 return;
1037 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038
1039 // allocate (and clear) our temp buffer to hold the transformed bitmap
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 SkAutoMalloc storage(size);
1041 mask.fImage = (uint8_t*)storage.get();
1042 memset(mask.fImage, 0, size);
reed@google.coma76de3d2011-01-13 18:30:42 +00001043
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 // now draw our bitmap(src) into mask(dst), transformed by the matrix
1045 {
1046 SkBitmap device;
1047 device.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
1048 mask.fBounds.height(), mask.fRowBytes);
1049 device.setPixels(mask.fImage);
reed@google.coma76de3d2011-01-13 18:30:42 +00001050
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 SkCanvas c(device);
1052 // need the unclipped top/left for the translate
1053 c.translate(-SkIntToScalar(mask.fBounds.fLeft),
1054 -SkIntToScalar(mask.fBounds.fTop));
1055 c.concat(*fMatrix);
reed@android.com3469c762009-02-24 19:03:20 +00001056
1057 // We can't call drawBitmap, or we'll infinitely recurse. Instead
reed@android.comfb12c3e2009-03-05 20:43:42 +00001058 // we manually build a shader and draw that into our new mask
reed@android.com3469c762009-02-24 19:03:20 +00001059 SkPaint tmpPaint;
1060 tmpPaint.setFlags(paint.getFlags());
reed@google.com40c2ba22011-07-25 19:41:22 +00001061 SkAutoBitmapShaderInstall install(bitmap, tmpPaint);
reed@android.com3469c762009-02-24 19:03:20 +00001062 SkRect rr;
1063 rr.set(0, 0, SkIntToScalar(bitmap.width()),
1064 SkIntToScalar(bitmap.height()));
reed@google.com40c2ba22011-07-25 19:41:22 +00001065 c.drawRect(rr, install.paintWithShader());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 }
1067 this->drawDevMask(mask, paint);
1068 }
1069}
1070
1071static bool clipped_out(const SkMatrix& m, const SkRegion& c,
1072 const SkRect& srcR) {
1073 SkRect dstR;
1074 SkIRect devIR;
reed@google.coma76de3d2011-01-13 18:30:42 +00001075
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 m.mapRect(&dstR, srcR);
reed@google.coma76de3d2011-01-13 18:30:42 +00001077 dstR.roundOut(&devIR);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078 return c.quickReject(devIR);
1079}
1080
1081static bool clipped_out(const SkMatrix& matrix, const SkRegion& clip,
1082 int width, int height) {
1083 SkRect r;
1084 r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
1085 return clipped_out(matrix, clip, r);
1086}
1087
1088void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
reed@google.com40c2ba22011-07-25 19:41:22 +00001089 const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001090 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091
1092 // nothing to draw
1093 if (fClip->isEmpty() ||
1094 bitmap.width() == 0 || bitmap.height() == 0 ||
1095 bitmap.getConfig() == SkBitmap::kNo_Config ||
reed@google.com40c2ba22011-07-25 19:41:22 +00001096 (origPaint.getAlpha() == 0 && origPaint.getXfermode() == NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 return;
1098 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001099
1100#ifndef SK_ALLOW_OVER_32K_BITMAPS
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 // run away on too-big bitmaps for now (exceed 16.16)
1102 if (bitmap.width() > 32767 || bitmap.height() > 32767) {
1103 return;
1104 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001105#endif
1106
reed@google.com40c2ba22011-07-25 19:41:22 +00001107 SkPaint paint(origPaint);
1108 paint.setStyle(SkPaint::kFill_Style);
reed@google.coma76de3d2011-01-13 18:30:42 +00001109
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 SkMatrix matrix;
1111 if (!matrix.setConcat(*fMatrix, prematrix)) {
1112 return;
1113 }
1114
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 if (clipped_out(matrix, *fClip, bitmap.width(), bitmap.height())) {
1116 return;
1117 }
1118
reed@android.com218521e2010-02-09 13:06:29 +00001119 if (fBounder && just_translate(matrix, bitmap)) {
1120 SkIRect ir;
1121 int32_t ix = SkScalarRound(matrix.getTranslateX());
1122 int32_t iy = SkScalarRound(matrix.getTranslateY());
1123 ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
1124 if (!fBounder->doIRect(ir)) {
1125 return;
1126 }
1127 }
1128
1129 // only lock the pixels if we passed the clip and bounder tests
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 SkAutoLockPixels alp(bitmap);
1131 // after the lock, check if we are valid
1132 if (!bitmap.readyToDraw()) {
1133 return;
1134 }
1135
reed@android.com0baf1932009-06-24 12:41:42 +00001136 if (bitmap.getConfig() != SkBitmap::kA8_Config &&
1137 just_translate(matrix, bitmap)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 int ix = SkScalarRound(matrix.getTranslateX());
1139 int iy = SkScalarRound(matrix.getTranslateY());
1140 uint32_t storage[kBlitterStorageLongCount];
1141 SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
1142 ix, iy, storage, sizeof(storage));
1143 if (blitter) {
1144 SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage);
1145
1146 SkIRect ir;
1147 ir.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148
1149 SkRegion::Cliperator iter(*fClip, ir);
1150 const SkIRect& cr = iter.rect();
1151
1152 for (; !iter.done(); iter.next()) {
1153 SkASSERT(!cr.isEmpty());
1154 blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
1155 }
1156 return;
1157 }
1158#if 0
1159 SkDebugf("---- MISSING sprite case: config=%d [%d %d], device=%d, xfer=%p, alpha=0x%X colorFilter=%p\n",
1160 bitmap.config(), bitmap.width(), bitmap.height(), fBitmap->config(),
1161 paint.getXfermode(), paint.getAlpha(), paint.getColorFilter());
1162#endif
1163 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001164
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 // now make a temp draw on the stack, and use it
1166 //
1167 SkDraw draw(*this);
1168 draw.fMatrix = &matrix;
reed@google.coma76de3d2011-01-13 18:30:42 +00001169
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170 if (bitmap.getConfig() == SkBitmap::kA8_Config) {
1171 draw.drawBitmapAsMask(bitmap, paint);
1172 } else {
reed@google.com40c2ba22011-07-25 19:41:22 +00001173 SkAutoBitmapShaderInstall install(bitmap, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174
1175 SkRect r;
1176 r.set(0, 0, SkIntToScalar(bitmap.width()),
1177 SkIntToScalar(bitmap.height()));
reed@google.coma76de3d2011-01-13 18:30:42 +00001178 // is this ok if paint has a rasterizer?
reed@google.com40c2ba22011-07-25 19:41:22 +00001179 draw.drawRect(r, install.paintWithShader());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 }
1181}
1182
1183void SkDraw::drawSprite(const SkBitmap& bitmap, int x, int y,
reed@google.com40c2ba22011-07-25 19:41:22 +00001184 const SkPaint& origPaint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001185 SkDEBUGCODE(this->validate();)
reed@google.coma76de3d2011-01-13 18:30:42 +00001186
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 // nothing to draw
1188 if (fClip->isEmpty() ||
1189 bitmap.width() == 0 || bitmap.height() == 0 ||
1190 bitmap.getConfig() == SkBitmap::kNo_Config ||
reed@google.com40c2ba22011-07-25 19:41:22 +00001191 (origPaint.getAlpha() == 0 && origPaint.getXfermode() == NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192 return;
1193 }
1194
1195 SkIRect bounds;
1196 bounds.set(x, y, x + bitmap.width(), y + bitmap.height());
1197
1198 if (fClip->quickReject(bounds)) {
1199 return; // nothing to draw
1200 }
1201
reed@google.com40c2ba22011-07-25 19:41:22 +00001202 SkPaint paint(origPaint);
1203 paint.setStyle(SkPaint::kFill_Style);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204
1205 if (NULL == paint.getColorFilter()) {
1206 uint32_t storage[kBlitterStorageLongCount];
1207 SkBlitter* blitter = SkBlitter::ChooseSprite(*fBitmap, paint, bitmap,
1208 x, y, storage, sizeof(storage));
1209
1210 if (blitter) {
1211 SkAutoTPlacementDelete<SkBlitter> ad(blitter, storage);
1212
1213 if (fBounder && !fBounder->doIRect(bounds)) {
1214 return;
1215 }
1216
1217 SkRegion::Cliperator iter(*fClip, bounds);
1218 const SkIRect& cr = iter.rect();
1219
1220 for (; !iter.done(); iter.next()) {
1221 SkASSERT(!cr.isEmpty());
1222 blitter->blitRect(cr.fLeft, cr.fTop, cr.width(), cr.height());
1223 }
1224 return;
1225 }
1226 }
1227
reed@google.com40c2ba22011-07-25 19:41:22 +00001228 SkAutoBitmapShaderInstall install(bitmap, paint);
1229 const SkPaint& shaderPaint = install.paintWithShader();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230
1231 SkMatrix matrix;
1232 SkRect r;
1233
1234 // get a scalar version of our rect
1235 r.set(bounds);
1236
1237 // tell the shader our offset
1238 matrix.setTranslate(r.fLeft, r.fTop);
reed@google.com40c2ba22011-07-25 19:41:22 +00001239 shaderPaint.getShader()->setLocalMatrix(matrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00001240
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 SkDraw draw(*this);
1242 matrix.reset();
1243 draw.fMatrix = &matrix;
1244 // call ourself with a rect
reed@google.coma76de3d2011-01-13 18:30:42 +00001245 // is this OK if paint has a rasterizer?
reed@google.com40c2ba22011-07-25 19:41:22 +00001246 draw.drawRect(r, shaderPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247}
1248
1249///////////////////////////////////////////////////////////////////////////////
1250
1251#include "SkScalerContext.h"
1252#include "SkGlyphCache.h"
1253#include "SkUtils.h"
1254
1255static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
1256 const char text[], size_t byteLength, SkVector* stopVector) {
1257 SkFixed x = 0, y = 0;
1258 const char* stop = text + byteLength;
1259
1260 SkAutoKern autokern;
reed@google.coma76de3d2011-01-13 18:30:42 +00001261
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 while (text < stop) {
1263 // don't need x, y here, since all subpixel variants will have the
1264 // same advance
1265 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
1266
1267 x += autokern.adjust(glyph) + glyph.fAdvanceX;
1268 y += glyph.fAdvanceY;
1269 }
1270 stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
1271
1272 SkASSERT(text == stop);
1273}
1274
1275void SkDraw::drawText_asPaths(const char text[], size_t byteLength,
1276 SkScalar x, SkScalar y,
1277 const SkPaint& paint) const {
reed@android.comf2b98d62010-12-20 18:26:13 +00001278 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279
1280 SkTextToPathIter iter(text, byteLength, paint, true, true);
1281
1282 SkMatrix matrix;
1283 matrix.setScale(iter.getPathScale(), iter.getPathScale());
1284 matrix.postTranslate(x, y);
1285
1286 const SkPath* iterPath;
1287 SkScalar xpos, prevXPos = 0;
1288
1289 while ((iterPath = iter.next(&xpos)) != NULL) {
1290 matrix.postTranslate(xpos - prevXPos, 0);
reed@android.comf2b98d62010-12-20 18:26:13 +00001291 const SkPaint& pnt = iter.getPaint();
1292 if (fDevice) {
1293 fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
1294 } else {
1295 this->drawPath(*iterPath, pnt, &matrix, false);
1296 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 prevXPos = xpos;
1298 }
1299}
1300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301static void draw_paint_rect(const SkDraw* draw, const SkPaint& paint,
1302 const SkRect& r, SkScalar textSize) {
1303 if (paint.getStyle() == SkPaint::kFill_Style) {
1304 draw->drawRect(r, paint);
1305 } else {
1306 SkPaint p(paint);
1307 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
1308 draw->drawRect(r, p);
1309 }
1310}
1311
1312static void handle_aftertext(const SkDraw* draw, const SkPaint& paint,
1313 SkScalar width, const SkPoint& start) {
1314 uint32_t flags = paint.getFlags();
1315
1316 if (flags & (SkPaint::kUnderlineText_Flag |
1317 SkPaint::kStrikeThruText_Flag)) {
1318 SkScalar textSize = paint.getTextSize();
1319 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1320 SkRect r;
1321
1322 r.fLeft = start.fX;
1323 r.fRight = start.fX + width;
1324
1325 if (flags & SkPaint::kUnderlineText_Flag) {
1326 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1327 start.fY);
1328 r.fTop = offset;
1329 r.fBottom = offset + height;
1330 draw_paint_rect(draw, paint, r, textSize);
1331 }
1332 if (flags & SkPaint::kStrikeThruText_Flag) {
1333 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1334 start.fY);
1335 r.fTop = offset;
1336 r.fBottom = offset + height;
1337 draw_paint_rect(draw, paint, r, textSize);
1338 }
1339 }
1340}
1341
1342// disable warning : local variable used without having been initialized
reed@google.coma76de3d2011-01-13 18:30:42 +00001343#if defined _WIN32 && _MSC_VER >= 1300
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344#pragma warning ( push )
1345#pragma warning ( disable : 4701 )
1346#endif
1347
1348//////////////////////////////////////////////////////////////////////////////
1349
1350static void D1G_NoBounder_RectClip(const SkDraw1Glyph& state,
reed@android.comf2b98d62010-12-20 18:26:13 +00001351 SkFixed fx, SkFixed fy,
1352 const SkGlyph& glyph) {
1353 int left = SkFixedFloor(fx);
1354 int top = SkFixedFloor(fy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1356 SkASSERT(state.fClip->isRect());
1357 SkASSERT(NULL == state.fBounder);
1358 SkASSERT(state.fClipBounds == state.fClip->getBounds());
1359
1360 left += glyph.fLeft;
1361 top += glyph.fTop;
1362
1363 int right = left + glyph.fWidth;
1364 int bottom = top + glyph.fHeight;
1365
1366 SkMask mask;
1367 SkIRect storage;
1368 SkIRect* bounds = &mask.fBounds;
1369
1370 mask.fBounds.set(left, top, right, bottom);
1371
1372 // this extra test is worth it, assuming that most of the time it succeeds
1373 // since we can avoid writing to storage
1374 if (!state.fClipBounds.containsNoEmptyCheck(left, top, right, bottom)) {
1375 if (!storage.intersectNoEmptyCheck(mask.fBounds, state.fClipBounds))
1376 return;
1377 bounds = &storage;
1378 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001379
1380 uint8_t* aa = (uint8_t*)glyph.fImage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381 if (NULL == aa) {
1382 aa = (uint8_t*)state.fCache->findImage(glyph);
1383 if (NULL == aa) {
1384 return; // can't rasterize glyph
1385 }
1386 }
1387
1388 mask.fRowBytes = glyph.rowBytes();
reed@android.com6c14b432009-03-23 20:11:11 +00001389 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 mask.fImage = aa;
1391 state.fBlitter->blitMask(mask, *bounds);
1392}
1393
1394static void D1G_NoBounder_RgnClip(const SkDraw1Glyph& state,
reed@android.comf2b98d62010-12-20 18:26:13 +00001395 SkFixed fx, SkFixed fy,
1396 const SkGlyph& glyph) {
1397 int left = SkFixedFloor(fx);
1398 int top = SkFixedFloor(fy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1400 SkASSERT(!state.fClip->isRect());
1401 SkASSERT(NULL == state.fBounder);
1402
1403 SkMask mask;
1404
1405 left += glyph.fLeft;
1406 top += glyph.fTop;
1407
1408 mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
1409 SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
1410
1411 if (!clipper.done()) {
1412 const SkIRect& cr = clipper.rect();
1413 const uint8_t* aa = (const uint8_t*)glyph.fImage;
1414 if (NULL == aa) {
1415 aa = (uint8_t*)state.fCache->findImage(glyph);
1416 if (NULL == aa) {
1417 return;
1418 }
1419 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001420
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 mask.fRowBytes = glyph.rowBytes();
reed@android.com6c14b432009-03-23 20:11:11 +00001422 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 mask.fImage = (uint8_t*)aa;
1424 do {
1425 state.fBlitter->blitMask(mask, cr);
1426 clipper.next();
1427 } while (!clipper.done());
1428 }
1429}
1430
1431static void D1G_Bounder(const SkDraw1Glyph& state,
reed@android.comf2b98d62010-12-20 18:26:13 +00001432 SkFixed fx, SkFixed fy,
1433 const SkGlyph& glyph) {
1434 int left = SkFixedFloor(fx);
1435 int top = SkFixedFloor(fy);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
1437
1438 SkMask mask;
1439
1440 left += glyph.fLeft;
1441 top += glyph.fTop;
1442
1443 mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
1444 SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
1445
1446 if (!clipper.done()) {
1447 const SkIRect& cr = clipper.rect();
1448 const uint8_t* aa = (const uint8_t*)glyph.fImage;
1449 if (NULL == aa) {
1450 aa = (uint8_t*)state.fCache->findImage(glyph);
1451 if (NULL == aa) {
1452 return;
1453 }
1454 }
reed@android.comd055c1f2010-03-01 14:54:05 +00001455
1456 // we need to pass the origin, which we approximate with our
1457 // (unadjusted) left,top coordinates (the caller called fixedfloor)
1458 if (state.fBounder->doIRectGlyph(cr,
1459 left - glyph.fLeft,
1460 top - glyph.fTop, glyph)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461 mask.fRowBytes = glyph.rowBytes();
reed@android.com6c14b432009-03-23 20:11:11 +00001462 mask.fFormat = static_cast<SkMask::Format>(glyph.fMaskFormat);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 mask.fImage = (uint8_t*)aa;
1464 do {
1465 state.fBlitter->blitMask(mask, cr);
1466 clipper.next();
1467 } while (!clipper.done());
1468 }
1469 }
1470}
1471
reed@google.comfd4236e2011-07-25 21:16:22 +00001472static bool hasCustomD1GProc(const SkDraw& draw) {
1473 return draw.fProcs && draw.fProcs->fD1GProc;
1474}
1475
1476static bool needsRasterTextBlit(const SkDraw& draw) {
1477 return !hasCustomD1GProc(draw);
1478}
1479
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480SkDraw1Glyph::Proc SkDraw1Glyph::init(const SkDraw* draw, SkBlitter* blitter,
1481 SkGlyphCache* cache) {
1482 fDraw = draw;
1483 fBounder = draw->fBounder;
1484 fClip = draw->fClip;
1485 fClipBounds = fClip->getBounds();
1486 fBlitter = blitter;
1487 fCache = cache;
1488
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001489 if (hasCustomD1GProc(*draw)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490 return draw->fProcs->fD1GProc;
1491 }
1492
1493 if (NULL == fBounder) {
1494 if (fClip->isRect()) {
1495 return D1G_NoBounder_RectClip;
1496 } else {
1497 return D1G_NoBounder_RgnClip;
1498 }
1499 } else {
1500 return D1G_Bounder;
1501 }
1502}
1503
1504enum RoundBaseline {
1505 kDont_Round_Baseline,
1506 kRound_X_Baseline,
1507 kRound_Y_Baseline
1508};
1509
1510static RoundBaseline computeRoundBaseline(const SkMatrix& mat) {
1511 if (mat[1] == 0 && mat[3] == 0) {
1512 // we're 0 or 180 degrees, round the y coordinate of the baseline
1513 return kRound_Y_Baseline;
1514 } else if (mat[0] == 0 && mat[4] == 0) {
1515 // we're 90 or 270 degrees, round the x coordinate of the baseline
1516 return kRound_X_Baseline;
1517 } else {
1518 return kDont_Round_Baseline;
1519 }
1520}
1521
1522///////////////////////////////////////////////////////////////////////////////
1523
1524void SkDraw::drawText(const char text[], size_t byteLength,
1525 SkScalar x, SkScalar y, const SkPaint& paint) const {
1526 SkASSERT(byteLength == 0 || text != NULL);
1527
reed@android.comf2b98d62010-12-20 18:26:13 +00001528 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529
1530 // nothing to draw
1531 if (text == NULL || byteLength == 0 ||
1532 fClip->isEmpty() ||
1533 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1534 return;
1535 }
1536
1537 SkScalar underlineWidth = 0;
1538 SkPoint underlineStart;
1539
1540 underlineStart.set(0, 0); // to avoid warning
1541 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1542 SkPaint::kStrikeThruText_Flag)) {
1543 underlineWidth = paint.measureText(text, byteLength);
1544
1545 SkScalar offsetX = 0;
1546 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1547 offsetX = SkScalarHalf(underlineWidth);
1548 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1549 offsetX = underlineWidth;
1550 }
1551 underlineStart.set(x - offsetX, y);
1552 }
1553
1554 if (/*paint.isLinearText() ||*/
tomhudson@google.com8d430182011-06-06 19:11:19 +00001555 (fMatrix->hasPerspective())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556 this->drawText_asPaths(text, byteLength, x, y, paint);
1557 handle_aftertext(this, paint, underlineWidth, underlineStart);
1558 return;
1559 }
1560
1561 SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
1562
reed@android.comf2b98d62010-12-20 18:26:13 +00001563 const SkMatrix* matrix = fMatrix;
1564 SkFixed finalFYMask = ~0xFFFF; // trunc fy;
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001565 if (hasCustomD1GProc(*this)) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001566 // only support the fMVMatrix (for now) for the GPU case, which also
1567 // sets the fD1GProc
1568 if (fMVMatrix) {
1569 matrix = fMVMatrix;
1570 finalFYMask = ~0; // don't truncate
1571 }
1572 }
1573
1574 SkAutoGlyphCache autoCache(paint, matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001575 SkGlyphCache* cache = autoCache.getCache();
reed@google.coma76de3d2011-01-13 18:30:42 +00001576
reed@android.com8a1c16f2008-12-17 15:59:43 +00001577 // transform our starting point
1578 {
1579 SkPoint loc;
reed@android.comf2b98d62010-12-20 18:26:13 +00001580 matrix->mapXY(x, y, &loc);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581 x = loc.fX;
1582 y = loc.fY;
1583 }
1584
1585 // need to measure first
1586 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
1587 SkVector stop;
1588
1589 measure_text(cache, glyphCacheProc, text, byteLength, &stop);
1590
1591 SkScalar stopX = stop.fX;
1592 SkScalar stopY = stop.fY;
1593
1594 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1595 stopX = SkScalarHalf(stopX);
1596 stopY = SkScalarHalf(stopY);
1597 }
1598 x -= stopX;
1599 y -= stopY;
1600 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001601
reed@android.com8a1c16f2008-12-17 15:59:43 +00001602 SkFixed fx = SkScalarToFixed(x);
1603 SkFixed fy = SkScalarToFixed(y);
1604 const char* stop = text + byteLength;
1605
reed@android.comf2b98d62010-12-20 18:26:13 +00001606 SkFixed fxMask = ~0;
1607 SkFixed fyMask = ~0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 if (paint.isSubpixelText()) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001609 RoundBaseline roundBaseline = computeRoundBaseline(*matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610 if (kRound_Y_Baseline == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001611 fyMask = 0;
1612// fy = (fy + 0x8000) & ~0xFFFF;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 } else if (kRound_X_Baseline == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001614 fxMask = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001617 // apply the bias here, so we don't have to add 1/2 in the loop
1618 fx += SK_FixedHalf;
1619 fy += SK_FixedHalf;
reed@google.com39ce0ac2011-04-08 15:42:19 +00001620 fyMask &= finalFYMask;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001622 SkAutoBlitterChoose blitter;
1623 if (needsRasterTextBlit(*this)) {
1624 blitter.choose(*fBitmap, *matrix, paint);
1625 }
1626
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627 SkAutoKern autokern;
1628 SkDraw1Glyph d1g;
1629 SkDraw1Glyph::Proc proc = d1g.init(this, blitter.get(), cache);
1630
1631 while (text < stop) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001632 const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633
1634 fx += autokern.adjust(glyph);
1635
1636 if (glyph.fWidth) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001637 proc(d1g, fx, fy, glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638 }
1639 fx += glyph.fAdvanceX;
1640 fy += glyph.fAdvanceY;
1641 }
1642
1643 if (underlineWidth) {
1644 autoCache.release(); // release this now to free up the RAM
1645 handle_aftertext(this, paint, underlineWidth, underlineStart);
1646 }
1647}
1648
1649// last parameter is interpreted as SkFixed [x, y]
1650// return the fixed position, which may be rounded or not by the caller
1651// e.g. subpixel doesn't round
1652typedef void (*AlignProc)(const SkPoint&, const SkGlyph&, SkIPoint*);
1653
1654static void leftAlignProc(const SkPoint& loc, const SkGlyph& glyph,
1655 SkIPoint* dst) {
1656 dst->set(SkScalarToFixed(loc.fX), SkScalarToFixed(loc.fY));
1657}
1658
1659static void centerAlignProc(const SkPoint& loc, const SkGlyph& glyph,
1660 SkIPoint* dst) {
1661 dst->set(SkScalarToFixed(loc.fX) - (glyph.fAdvanceX >> 1),
1662 SkScalarToFixed(loc.fY) - (glyph.fAdvanceY >> 1));
1663}
1664
1665static void rightAlignProc(const SkPoint& loc, const SkGlyph& glyph,
1666 SkIPoint* dst) {
1667 dst->set(SkScalarToFixed(loc.fX) - glyph.fAdvanceX,
1668 SkScalarToFixed(loc.fY) - glyph.fAdvanceY);
1669}
1670
1671static AlignProc pick_align_proc(SkPaint::Align align) {
1672 static const AlignProc gProcs[] = {
1673 leftAlignProc, centerAlignProc, rightAlignProc
1674 };
reed@google.coma76de3d2011-01-13 18:30:42 +00001675
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676 SkASSERT((unsigned)align < SK_ARRAY_COUNT(gProcs));
1677
1678 return gProcs[align];
1679}
1680
1681class TextMapState {
1682public:
1683 mutable SkPoint fLoc;
reed@google.coma76de3d2011-01-13 18:30:42 +00001684
reed@android.com8a1c16f2008-12-17 15:59:43 +00001685 TextMapState(const SkMatrix& matrix, SkScalar y)
1686 : fMatrix(matrix), fProc(matrix.getMapXYProc()), fY(y) {}
1687
1688 typedef void (*Proc)(const TextMapState&, const SkScalar pos[]);
reed@google.coma76de3d2011-01-13 18:30:42 +00001689
reed@android.com8a1c16f2008-12-17 15:59:43 +00001690 Proc pickProc(int scalarsPerPosition);
reed@google.coma76de3d2011-01-13 18:30:42 +00001691
reed@android.com8a1c16f2008-12-17 15:59:43 +00001692private:
1693 const SkMatrix& fMatrix;
1694 SkMatrix::MapXYProc fProc;
1695 SkScalar fY; // ignored by MapXYProc
1696 // these are only used by Only... procs
1697 SkScalar fScaleX, fTransX, fTransformedY;
1698
1699 static void MapXProc(const TextMapState& state, const SkScalar pos[]) {
1700 state.fProc(state.fMatrix, *pos, state.fY, &state.fLoc);
1701 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001702
reed@android.com8a1c16f2008-12-17 15:59:43 +00001703 static void MapXYProc(const TextMapState& state, const SkScalar pos[]) {
1704 state.fProc(state.fMatrix, pos[0], pos[1], &state.fLoc);
1705 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001706
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707 static void MapOnlyScaleXProc(const TextMapState& state,
1708 const SkScalar pos[]) {
1709 state.fLoc.set(SkScalarMul(state.fScaleX, *pos) + state.fTransX,
1710 state.fTransformedY);
1711 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001712
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713 static void MapOnlyTransXProc(const TextMapState& state,
1714 const SkScalar pos[]) {
1715 state.fLoc.set(*pos + state.fTransX, state.fTransformedY);
1716 }
1717};
1718
1719TextMapState::Proc TextMapState::pickProc(int scalarsPerPosition) {
1720 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
reed@google.coma76de3d2011-01-13 18:30:42 +00001721
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722 if (1 == scalarsPerPosition) {
1723 unsigned mtype = fMatrix.getType();
1724 if (mtype & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
1725 return MapXProc;
1726 } else {
1727 fScaleX = fMatrix.getScaleX();
1728 fTransX = fMatrix.getTranslateX();
1729 fTransformedY = SkScalarMul(fY, fMatrix.getScaleY()) +
1730 fMatrix.getTranslateY();
1731 return (mtype & SkMatrix::kScale_Mask) ?
1732 MapOnlyScaleXProc : MapOnlyTransXProc;
1733 }
1734 } else {
1735 return MapXYProc;
1736 }
1737}
1738
1739//////////////////////////////////////////////////////////////////////////////
1740
1741void SkDraw::drawPosText(const char text[], size_t byteLength,
1742 const SkScalar pos[], SkScalar constY,
1743 int scalarsPerPosition, const SkPaint& paint) const {
1744 SkASSERT(byteLength == 0 || text != NULL);
1745 SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
1746
reed@android.comf2b98d62010-12-20 18:26:13 +00001747 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748
1749 // nothing to draw
1750 if (text == NULL || byteLength == 0 ||
1751 fClip->isEmpty() ||
1752 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1753 return;
1754 }
1755
1756 if (/*paint.isLinearText() ||*/
tomhudson@google.com8d430182011-06-06 19:11:19 +00001757 (fMatrix->hasPerspective())) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758 // TODO !!!!
1759// this->drawText_asPaths(text, byteLength, x, y, paint);
1760 return;
1761 }
1762
reed@android.comf2b98d62010-12-20 18:26:13 +00001763 const SkMatrix* matrix = fMatrix;
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001764 if (hasCustomD1GProc(*this)) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001765 // only support the fMVMatrix (for now) for the GPU case, which also
1766 // sets the fD1GProc
1767 if (fMVMatrix) {
1768 matrix = fMVMatrix;
1769 }
1770 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001771
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
reed@android.comf2b98d62010-12-20 18:26:13 +00001773 SkAutoGlyphCache autoCache(paint, matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 SkGlyphCache* cache = autoCache.getCache();
reed@google.coma76de3d2011-01-13 18:30:42 +00001775
reed@google.com1d6ee0b2011-07-05 17:44:56 +00001776 SkAutoBlitterChoose blitter;
1777 if (needsRasterTextBlit(*this)) {
1778 blitter.choose(*fBitmap, *matrix, paint);
1779 }
1780
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 const char* stop = text + byteLength;
1782 AlignProc alignProc = pick_align_proc(paint.getTextAlign());
1783 SkDraw1Glyph d1g;
1784 SkDraw1Glyph::Proc proc = d1g.init(this, blitter.get(), cache);
reed@android.comf2b98d62010-12-20 18:26:13 +00001785 TextMapState tms(*matrix, constY);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 TextMapState::Proc tmsProc = tms.pickProc(scalarsPerPosition);
1787
1788 if (paint.isSubpixelText()) {
1789 // maybe we should skip the rounding if linearText is set
reed@android.comf2b98d62010-12-20 18:26:13 +00001790 RoundBaseline roundBaseline = computeRoundBaseline(*matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791
1792 if (SkPaint::kLeft_Align == paint.getTextAlign()) {
1793 while (text < stop) {
reed@google.coma76de3d2011-01-13 18:30:42 +00001794
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795 tmsProc(tms, pos);
reed@google.coma76de3d2011-01-13 18:30:42 +00001796
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797 SkFixed fx = SkScalarToFixed(tms.fLoc.fX);
1798 SkFixed fy = SkScalarToFixed(tms.fLoc.fY);
reed@android.comf2b98d62010-12-20 18:26:13 +00001799 SkFixed fxMask = ~0;
1800 SkFixed fyMask = ~0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801
1802 if (kRound_Y_Baseline == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001803 fyMask = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001804 } else if (kRound_X_Baseline == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001805 fxMask = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001807
reed@android.comf2b98d62010-12-20 18:26:13 +00001808 const SkGlyph& glyph = glyphCacheProc(cache, &text,
1809 fx & fxMask, fy & fyMask);
reed@google.coma76de3d2011-01-13 18:30:42 +00001810
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 if (glyph.fWidth) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001812 proc(d1g, fx, fy, glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001813 }
1814 pos += scalarsPerPosition;
1815 }
1816 } else {
1817 while (text < stop) {
1818 const SkGlyph* glyph = &glyphCacheProc(cache, &text, 0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001819
reed@android.com8a1c16f2008-12-17 15:59:43 +00001820 if (glyph->fWidth) {
1821 SkDEBUGCODE(SkFixed prevAdvX = glyph->fAdvanceX;)
1822 SkDEBUGCODE(SkFixed prevAdvY = glyph->fAdvanceY;)
1823
1824 SkFixed fx, fy;
reed@android.comf2b98d62010-12-20 18:26:13 +00001825 SkFixed fxMask = ~0;
1826 SkFixed fyMask = ~0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 tmsProc(tms, pos);
reed@google.coma76de3d2011-01-13 18:30:42 +00001828
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 {
1830 SkIPoint fixedLoc;
1831 alignProc(tms.fLoc, *glyph, &fixedLoc);
1832 fx = fixedLoc.fX;
1833 fy = fixedLoc.fY;
1834
1835 if (kRound_Y_Baseline == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001836 fyMask = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837 } else if (kRound_X_Baseline == roundBaseline) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001838 fxMask = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839 }
1840 }
reed@google.coma76de3d2011-01-13 18:30:42 +00001841
reed@android.com8a1c16f2008-12-17 15:59:43 +00001842 // have to call again, now that we've been "aligned"
reed@android.comf2b98d62010-12-20 18:26:13 +00001843 glyph = &glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001844 // the assumption is that the advance hasn't changed
1845 SkASSERT(prevAdvX == glyph->fAdvanceX);
1846 SkASSERT(prevAdvY == glyph->fAdvanceY);
reed@google.coma76de3d2011-01-13 18:30:42 +00001847
reed@android.comf2b98d62010-12-20 18:26:13 +00001848 proc(d1g, fx, fy, *glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849 }
1850 pos += scalarsPerPosition;
1851 }
1852 }
1853 } else { // not subpixel
1854 while (text < stop) {
1855 // the last 2 parameters are ignored
1856 const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
reed@google.coma76de3d2011-01-13 18:30:42 +00001857
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858 if (glyph.fWidth) {
1859 tmsProc(tms, pos);
reed@google.coma76de3d2011-01-13 18:30:42 +00001860
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861 SkIPoint fixedLoc;
1862 alignProc(tms.fLoc, glyph, &fixedLoc);
reed@google.coma76de3d2011-01-13 18:30:42 +00001863
reed@android.comf2b98d62010-12-20 18:26:13 +00001864 proc(d1g, fixedLoc.fX + SK_FixedHalf,
1865 fixedLoc.fY + SK_FixedHalf, glyph);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001866 }
1867 pos += scalarsPerPosition;
1868 }
1869 }
1870}
1871
1872#if defined _WIN32 && _MSC_VER >= 1300
1873#pragma warning ( pop )
1874#endif
1875
1876///////////////////////////////////////////////////////////////////////////////
1877
1878#include "SkPathMeasure.h"
1879
1880static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
1881 SkPathMeasure& meas, const SkMatrix& matrix) {
1882 SkMatrix::MapXYProc proc = matrix.getMapXYProc();
1883
1884 for (int i = 0; i < count; i++) {
1885 SkPoint pos;
1886 SkVector tangent;
1887
1888 proc(matrix, src[i].fX, src[i].fY, &pos);
1889 SkScalar sx = pos.fX;
1890 SkScalar sy = pos.fY;
1891
1892 meas.getPosTan(sx, &pos, &tangent);
1893
1894 /* This is the old way (that explains our approach but is way too slow
1895 SkMatrix matrix;
1896 SkPoint pt;
1897
1898 pt.set(sx, sy);
1899 matrix.setSinCos(tangent.fY, tangent.fX);
1900 matrix.preTranslate(-sx, 0);
1901 matrix.postTranslate(pos.fX, pos.fY);
1902 matrix.mapPoints(&dst[i], &pt, 1);
1903 */
1904 dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
1905 pos.fY + SkScalarMul(tangent.fX, sy));
1906 }
1907}
1908
1909/* TODO
1910
1911 Need differentially more subdivisions when the follow-path is curvy. Not sure how to
1912 determine that, but we need it. I guess a cheap answer is let the caller tell us,
1913 but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
1914*/
1915static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
1916 const SkMatrix& matrix) {
1917 SkPath::Iter iter(src, false);
1918 SkPoint srcP[4], dstP[3];
1919 SkPath::Verb verb;
1920
1921 while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
1922 switch (verb) {
1923 case SkPath::kMove_Verb:
1924 morphpoints(dstP, srcP, 1, meas, matrix);
1925 dst->moveTo(dstP[0]);
1926 break;
1927 case SkPath::kLine_Verb:
1928 // turn lines into quads to look bendy
1929 srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
1930 srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
1931 morphpoints(dstP, srcP, 2, meas, matrix);
1932 dst->quadTo(dstP[0], dstP[1]);
1933 break;
1934 case SkPath::kQuad_Verb:
1935 morphpoints(dstP, &srcP[1], 2, meas, matrix);
1936 dst->quadTo(dstP[0], dstP[1]);
1937 break;
1938 case SkPath::kCubic_Verb:
1939 morphpoints(dstP, &srcP[1], 3, meas, matrix);
1940 dst->cubicTo(dstP[0], dstP[1], dstP[2]);
1941 break;
1942 case SkPath::kClose_Verb:
1943 dst->close();
1944 break;
1945 default:
1946 SkASSERT(!"unknown verb");
1947 break;
1948 }
1949 }
1950}
1951
1952void SkDraw::drawTextOnPath(const char text[], size_t byteLength,
1953 const SkPath& follow, const SkMatrix* matrix,
1954 const SkPaint& paint) const {
1955 SkASSERT(byteLength == 0 || text != NULL);
1956
1957 // nothing to draw
1958 if (text == NULL || byteLength == 0 ||
1959 fClip->isEmpty() ||
1960 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1961 return;
1962 }
1963
1964 SkTextToPathIter iter(text, byteLength, paint, true, true);
1965 SkPathMeasure meas(follow, false);
1966 SkScalar hOffset = 0;
1967
1968 // need to measure first
1969 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
1970 SkScalar pathLen = meas.getLength();
1971 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1972 pathLen = SkScalarHalf(pathLen);
1973 }
1974 hOffset += pathLen;
1975 }
1976
1977 const SkPath* iterPath;
1978 SkScalar xpos;
1979 SkMatrix scaledMatrix;
1980 SkScalar scale = iter.getPathScale();
1981
1982 scaledMatrix.setScale(scale, scale);
reed@google.coma76de3d2011-01-13 18:30:42 +00001983
reed@android.com8a1c16f2008-12-17 15:59:43 +00001984 while ((iterPath = iter.next(&xpos)) != NULL) {
1985 SkPath tmp;
1986 SkMatrix m(scaledMatrix);
1987
1988 m.postTranslate(xpos + hOffset, 0);
1989 if (matrix) {
1990 m.postConcat(*matrix);
1991 }
1992 morphpath(&tmp, *iterPath, meas, m);
reed@android.comf2b98d62010-12-20 18:26:13 +00001993 if (fDevice) {
1994 fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true);
1995 } else {
1996 this->drawPath(tmp, iter.getPaint(), NULL, true);
1997 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001998 }
1999}
2000
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00002001#ifdef ANDROID
2002void SkDraw::drawPosTextOnPath(const char text[], size_t byteLength,
2003 const SkPoint pos[], const SkPaint& paint,
2004 const SkPath& path, const SkMatrix* matrix) const {
2005 // nothing to draw
2006 if (text == NULL || byteLength == 0 || fClip->isEmpty() ||
2007 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2008 return;
2009 }
2010
2011 SkMatrix scaledMatrix;
2012 SkPathMeasure meas(path, false);
2013
2014 SkMeasureCacheProc glyphCacheProc = paint.getMeasureCacheProc(
2015 SkPaint::kForward_TextBufferDirection, true);
2016
2017 // Copied (modified) from SkTextToPathIter constructor to setup paint
2018 SkPaint tempPaint(paint);
2019
2020 tempPaint.setLinearText(true);
2021 tempPaint.setMaskFilter(NULL); // don't want this affecting our path-cache lookup
2022
2023 if (tempPaint.getPathEffect() == NULL && !(tempPaint.getStrokeWidth() > 0
2024 && tempPaint.getStyle() != SkPaint::kFill_Style)) {
2025 tempPaint.setStyle(SkPaint::kFill_Style);
2026 tempPaint.setPathEffect(NULL);
2027 }
2028 // End copied from SkTextToPathIter constructor
2029
2030 // detach cache
2031 SkGlyphCache* cache = tempPaint.detachCache(NULL);
2032
2033 // Must set scale, even if 1
2034 SkScalar scale = SK_Scalar1;
2035 scaledMatrix.setScale(scale, scale);
2036
2037 // Loop over all glyph ids
2038 for (const char* stop = text + byteLength; text < stop; pos++) {
2039
2040 const SkGlyph& glyph = glyphCacheProc(cache, &text);
2041 SkPath tmp;
2042
2043 const SkPath* glyphPath = cache->findPath(glyph);
2044 if (glyphPath == NULL) {
2045 continue;
2046 }
2047
2048 SkMatrix m(scaledMatrix);
2049 m.postTranslate(pos->fX, 0);
2050
2051 if (matrix) {
2052 m.postConcat(*matrix);
2053 }
2054
2055 morphpath(&tmp, *glyphPath, meas, m);
2056 this->drawPath(tmp, tempPaint);
2057
2058 }
2059
2060 // re-attach cache
2061 SkGlyphCache::AttachCache(cache);
2062}
2063#endif
2064
reed@android.com8a1c16f2008-12-17 15:59:43 +00002065///////////////////////////////////////////////////////////////////////////////
2066
2067struct VertState {
2068 int f0, f1, f2;
2069
2070 VertState(int vCount, const uint16_t indices[], int indexCount)
2071 : fIndices(indices) {
2072 fCurrIndex = 0;
2073 if (indices) {
2074 fCount = indexCount;
2075 } else {
2076 fCount = vCount;
2077 }
2078 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002079
2080 typedef bool (*Proc)(VertState*);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081 Proc chooseProc(SkCanvas::VertexMode mode);
2082
2083private:
2084 int fCount;
2085 int fCurrIndex;
2086 const uint16_t* fIndices;
reed@google.coma76de3d2011-01-13 18:30:42 +00002087
reed@android.com8a1c16f2008-12-17 15:59:43 +00002088 static bool Triangles(VertState*);
2089 static bool TrianglesX(VertState*);
2090 static bool TriangleStrip(VertState*);
2091 static bool TriangleStripX(VertState*);
2092 static bool TriangleFan(VertState*);
2093 static bool TriangleFanX(VertState*);
2094};
2095
2096bool VertState::Triangles(VertState* state) {
2097 int index = state->fCurrIndex;
2098 if (index + 3 > state->fCount) {
2099 return false;
2100 }
2101 state->f0 = index + 0;
2102 state->f1 = index + 1;
2103 state->f2 = index + 2;
2104 state->fCurrIndex = index + 3;
2105 return true;
2106}
2107
2108bool VertState::TrianglesX(VertState* state) {
2109 const uint16_t* indices = state->fIndices;
2110 int index = state->fCurrIndex;
2111 if (index + 3 > state->fCount) {
2112 return false;
2113 }
2114 state->f0 = indices[index + 0];
2115 state->f1 = indices[index + 1];
2116 state->f2 = indices[index + 2];
2117 state->fCurrIndex = index + 3;
2118 return true;
2119}
2120
2121bool VertState::TriangleStrip(VertState* state) {
2122 int index = state->fCurrIndex;
2123 if (index + 3 > state->fCount) {
2124 return false;
2125 }
2126 state->f2 = index + 2;
2127 if (index & 1) {
2128 state->f0 = index + 1;
2129 state->f1 = index + 0;
2130 } else {
2131 state->f0 = index + 0;
2132 state->f1 = index + 1;
2133 }
2134 state->fCurrIndex = index + 1;
2135 return true;
2136}
2137
2138bool VertState::TriangleStripX(VertState* state) {
2139 const uint16_t* indices = state->fIndices;
2140 int index = state->fCurrIndex;
2141 if (index + 3 > state->fCount) {
2142 return false;
2143 }
2144 state->f2 = indices[index + 2];
2145 if (index & 1) {
2146 state->f0 = indices[index + 1];
2147 state->f1 = indices[index + 0];
2148 } else {
2149 state->f0 = indices[index + 0];
2150 state->f1 = indices[index + 1];
2151 }
2152 state->fCurrIndex = index + 1;
2153 return true;
2154}
2155
2156bool VertState::TriangleFan(VertState* state) {
2157 int index = state->fCurrIndex;
2158 if (index + 3 > state->fCount) {
2159 return false;
2160 }
2161 state->f0 = 0;
2162 state->f1 = index + 1;
2163 state->f2 = index + 2;
2164 state->fCurrIndex = index + 1;
2165 return true;
2166}
2167
2168bool VertState::TriangleFanX(VertState* state) {
2169 const uint16_t* indices = state->fIndices;
2170 int index = state->fCurrIndex;
2171 if (index + 3 > state->fCount) {
2172 return false;
2173 }
2174 state->f0 = indices[0];
2175 state->f1 = indices[index + 1];
2176 state->f2 = indices[index + 2];
2177 state->fCurrIndex = index + 1;
2178 return true;
2179}
2180
2181VertState::Proc VertState::chooseProc(SkCanvas::VertexMode mode) {
2182 switch (mode) {
2183 case SkCanvas::kTriangles_VertexMode:
2184 return fIndices ? TrianglesX : Triangles;
2185 case SkCanvas::kTriangleStrip_VertexMode:
2186 return fIndices ? TriangleStripX : TriangleStrip;
2187 case SkCanvas::kTriangleFan_VertexMode:
2188 return fIndices ? TriangleFanX : TriangleFan;
2189 default:
2190 return NULL;
2191 }
2192}
2193
2194typedef void (*HairProc)(const SkPoint&, const SkPoint&, const SkRegion*,
2195 SkBlitter*);
2196
2197static HairProc ChooseHairProc(bool doAntiAlias) {
2198 return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
2199}
2200
2201static bool texture_to_matrix(const VertState& state, const SkPoint verts[],
2202 const SkPoint texs[], SkMatrix* matrix) {
2203 SkPoint src[3], dst[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00002204
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 src[0] = texs[state.f0];
2206 src[1] = texs[state.f1];
2207 src[2] = texs[state.f2];
2208 dst[0] = verts[state.f0];
2209 dst[1] = verts[state.f1];
2210 dst[2] = verts[state.f2];
2211 return matrix->setPolyToPoly(src, dst, 3);
2212}
2213
2214class SkTriColorShader : public SkShader {
2215public:
2216 SkTriColorShader() {}
2217
2218 bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
reed@google.coma76de3d2011-01-13 18:30:42 +00002219
reed@android.com8a1c16f2008-12-17 15:59:43 +00002220 virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
reed@google.coma76de3d2011-01-13 18:30:42 +00002221
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222protected:
2223 SkTriColorShader(SkFlattenableReadBuffer& buffer) : SkShader(buffer) {}
reed@google.coma76de3d2011-01-13 18:30:42 +00002224
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225 virtual Factory getFactory() { return CreateProc; }
reed@google.coma76de3d2011-01-13 18:30:42 +00002226
reed@android.com8a1c16f2008-12-17 15:59:43 +00002227private:
2228 SkMatrix fDstToUnit;
2229 SkPMColor fColors[3];
reed@google.coma76de3d2011-01-13 18:30:42 +00002230
reed@android.com8a1c16f2008-12-17 15:59:43 +00002231 static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
2232 return SkNEW_ARGS(SkTriColorShader, (buffer));
2233 }
2234 typedef SkShader INHERITED;
2235};
2236
2237bool SkTriColorShader::setup(const SkPoint pts[], const SkColor colors[],
2238 int index0, int index1, int index2) {
reed@google.coma76de3d2011-01-13 18:30:42 +00002239
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240 fColors[0] = SkPreMultiplyColor(colors[index0]);
2241 fColors[1] = SkPreMultiplyColor(colors[index1]);
2242 fColors[2] = SkPreMultiplyColor(colors[index2]);
reed@google.coma76de3d2011-01-13 18:30:42 +00002243
reed@android.com8a1c16f2008-12-17 15:59:43 +00002244 SkMatrix m, im;
2245 m.reset();
2246 m.set(0, pts[index1].fX - pts[index0].fX);
2247 m.set(1, pts[index2].fX - pts[index0].fX);
2248 m.set(2, pts[index0].fX);
2249 m.set(3, pts[index1].fY - pts[index0].fY);
2250 m.set(4, pts[index2].fY - pts[index0].fY);
2251 m.set(5, pts[index0].fY);
2252 if (!m.invert(&im)) {
2253 return false;
2254 }
2255 return fDstToUnit.setConcat(im, this->getTotalInverse());
2256}
2257
2258#include "SkColorPriv.h"
reed@android.com689411a2008-12-18 02:52:32 +00002259#include "SkComposeShader.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00002260
2261static int ScalarTo256(SkScalar v) {
2262 int scale = SkScalarToFixed(v) >> 8;
2263 if (scale < 0) {
2264 scale = 0;
2265 }
2266 if (scale > 255) {
2267 scale = 255;
2268 }
2269 return SkAlpha255To256(scale);
2270}
2271
2272void SkTriColorShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
2273 SkPoint src;
reed@google.coma76de3d2011-01-13 18:30:42 +00002274
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 for (int i = 0; i < count; i++) {
2276 fDstToUnit.mapXY(SkIntToScalar(x), SkIntToScalar(y), &src);
2277 x += 1;
reed@google.coma76de3d2011-01-13 18:30:42 +00002278
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 int scale1 = ScalarTo256(src.fX);
2280 int scale2 = ScalarTo256(src.fY);
2281 int scale0 = 256 - scale1 - scale2;
2282 if (scale0 < 0) {
2283 if (scale1 > scale2) {
2284 scale2 = 256 - scale1;
2285 } else {
2286 scale1 = 256 - scale2;
2287 }
2288 scale0 = 0;
2289 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002290
reed@android.com8a1c16f2008-12-17 15:59:43 +00002291 dstC[i] = SkAlphaMulQ(fColors[0], scale0) +
2292 SkAlphaMulQ(fColors[1], scale1) +
2293 SkAlphaMulQ(fColors[2], scale2);
2294 }
2295}
2296
2297void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
2298 const SkPoint vertices[], const SkPoint textures[],
2299 const SkColor colors[], SkXfermode* xmode,
2300 const uint16_t indices[], int indexCount,
2301 const SkPaint& paint) const {
2302 SkASSERT(0 == count || NULL != vertices);
reed@google.coma76de3d2011-01-13 18:30:42 +00002303
reed@android.com8a1c16f2008-12-17 15:59:43 +00002304 // abort early if there is nothing to draw
2305 if (count < 3 || (indices && indexCount < 3) || fClip->isEmpty() ||
2306 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2307 return;
2308 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002309
reed@android.com8a1c16f2008-12-17 15:59:43 +00002310 // transform out vertices into device coordinates
2311 SkAutoSTMalloc<16, SkPoint> storage(count);
2312 SkPoint* devVerts = storage.get();
2313 fMatrix->mapPoints(devVerts, vertices, count);
reed@google.coma76de3d2011-01-13 18:30:42 +00002314
reed@android.com8a1c16f2008-12-17 15:59:43 +00002315 if (fBounder) {
2316 SkRect bounds;
2317 bounds.set(devVerts, count);
2318 if (!fBounder->doRect(bounds, paint)) {
2319 return;
2320 }
2321 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002322
reed@android.com8a1c16f2008-12-17 15:59:43 +00002323 /*
2324 We can draw the vertices in 1 of 4 ways:
2325
2326 - solid color (no shader/texture[], no colors[])
2327 - just colors (no shader/texture[], has colors[])
2328 - just texture (has shader/texture[], no colors[])
2329 - colors * texture (has shader/texture[], has colors[])
reed@google.coma76de3d2011-01-13 18:30:42 +00002330
reed@android.com8a1c16f2008-12-17 15:59:43 +00002331 Thus for texture drawing, we need both texture[] and a shader.
2332 */
2333
2334 SkTriColorShader triShader; // must be above declaration of p
2335 SkPaint p(paint);
2336
2337 SkShader* shader = p.getShader();
2338 if (NULL == shader) {
2339 // if we have no shader, we ignore the texture coordinates
2340 textures = NULL;
2341 } else if (NULL == textures) {
2342 // if we don't have texture coordinates, ignore the shader
2343 p.setShader(NULL);
2344 shader = NULL;
2345 }
2346
2347 // setup the custom shader (if needed)
2348 if (NULL != colors) {
2349 if (NULL == textures) {
2350 // just colors (no texture)
2351 p.setShader(&triShader);
2352 } else {
2353 // colors * texture
2354 SkASSERT(shader);
2355 bool releaseMode = false;
2356 if (NULL == xmode) {
reed@android.com845fdac2009-06-23 03:01:32 +00002357 xmode = SkXfermode::Create(SkXfermode::kMultiply_Mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002358 releaseMode = true;
2359 }
2360 SkShader* compose = SkNEW_ARGS(SkComposeShader,
2361 (&triShader, shader, xmode));
2362 p.setShader(compose)->unref();
2363 if (releaseMode) {
2364 xmode->unref();
2365 }
2366 }
2367 }
2368
2369 SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, p);
2370 // setup our state and function pointer for iterating triangles
2371 VertState state(count, indices, indexCount);
2372 VertState::Proc vertProc = state.chooseProc(vmode);
reed@google.coma76de3d2011-01-13 18:30:42 +00002373
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 if (NULL != textures || NULL != colors) {
2375 SkMatrix localM, tempM;
2376 bool hasLocalM = shader && shader->getLocalMatrix(&localM);
reed@google.coma76de3d2011-01-13 18:30:42 +00002377
reed@android.com8a1c16f2008-12-17 15:59:43 +00002378 if (NULL != colors) {
2379 if (!triShader.setContext(*fBitmap, p, *fMatrix)) {
2380 colors = NULL;
2381 }
2382 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002383
reed@android.com8a1c16f2008-12-17 15:59:43 +00002384 while (vertProc(&state)) {
2385 if (NULL != textures) {
2386 if (texture_to_matrix(state, vertices, textures, &tempM)) {
2387 if (hasLocalM) {
2388 tempM.postConcat(localM);
2389 }
2390 shader->setLocalMatrix(tempM);
2391 // need to recal setContext since we changed the local matrix
2392 if (!shader->setContext(*fBitmap, p, *fMatrix)) {
2393 continue;
2394 }
2395 }
2396 }
2397 if (NULL != colors) {
2398 if (!triShader.setup(vertices, colors,
2399 state.f0, state.f1, state.f2)) {
2400 continue;
2401 }
2402 }
2403 SkScan::FillTriangle(devVerts[state.f0], devVerts[state.f1],
2404 devVerts[state.f2], fClip, blitter.get());
2405 }
2406 // now restore the shader's original local matrix
2407 if (NULL != shader) {
2408 if (hasLocalM) {
2409 shader->setLocalMatrix(localM);
2410 } else {
2411 shader->resetLocalMatrix();
2412 }
2413 }
2414 } else {
2415 // no colors[] and no texture
2416 HairProc hairProc = ChooseHairProc(paint.isAntiAlias());
2417 while (vertProc(&state)) {
2418 hairProc(devVerts[state.f0], devVerts[state.f1], fClip, blitter.get());
2419 hairProc(devVerts[state.f1], devVerts[state.f2], fClip, blitter.get());
2420 hairProc(devVerts[state.f2], devVerts[state.f0], fClip, blitter.get());
2421 }
2422 }
2423}
2424
reed@google.com0a0a2362011-03-23 13:51:55 +00002425///////////////////////////////////////////////////////////////////////////////
2426///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00002427
2428#ifdef SK_DEBUG
2429
reed@android.comf2b98d62010-12-20 18:26:13 +00002430void SkDraw::validate() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002431 SkASSERT(fBitmap != NULL);
2432 SkASSERT(fMatrix != NULL);
2433 SkASSERT(fClip != NULL);
2434
2435 const SkIRect& cr = fClip->getBounds();
2436 SkIRect br;
2437
reed@android.comf2b98d62010-12-20 18:26:13 +00002438 br.set(0, 0, fBitmap->width(), fBitmap->height());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002439 SkASSERT(cr.isEmpty() || br.contains(cr));
reed@android.comf2b98d62010-12-20 18:26:13 +00002440
2441 // assert that both are null, or both are not-null
2442 SkASSERT(!fMVMatrix == !fExtMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002443}
2444
2445#endif
2446
reed@google.com0a0a2362011-03-23 13:51:55 +00002447///////////////////////////////////////////////////////////////////////////////
2448
2449SkBounder::SkBounder() {
2450 // initialize up front. This gets reset by SkCanvas before each draw call.
2451 fClip = &SkRegion::GetEmptyRegion();
2452}
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453
2454bool SkBounder::doIRect(const SkIRect& r) {
2455 SkIRect rr;
2456 return rr.intersect(fClip->getBounds(), r) && this->onIRect(rr);
2457}
2458
reed@android.comd055c1f2010-03-01 14:54:05 +00002459// TODO: change the prototype to take fixed, and update the callers
2460bool SkBounder::doIRectGlyph(const SkIRect& r, int x, int y,
2461 const SkGlyph& glyph) {
reed@android.com474a12c2010-01-04 19:35:33 +00002462 SkIRect rr;
reed@android.comd055c1f2010-03-01 14:54:05 +00002463 if (!rr.intersect(fClip->getBounds(), r)) {
2464 return false;
2465 }
2466 GlyphRec rec;
2467 rec.fLSB.set(SkIntToFixed(x), SkIntToFixed(y));
2468 rec.fRSB.set(rec.fLSB.fX + glyph.fAdvanceX,
2469 rec.fLSB.fY + glyph.fAdvanceY);
2470 rec.fGlyphID = glyph.getGlyphID();
2471 rec.fFlags = 0;
2472 return this->onIRectGlyph(rr, rec);
reed@android.com474a12c2010-01-04 19:35:33 +00002473}
2474
reed@android.com8a1c16f2008-12-17 15:59:43 +00002475bool SkBounder::doHairline(const SkPoint& pt0, const SkPoint& pt1,
2476 const SkPaint& paint) {
2477 SkIRect r;
2478 SkScalar v0, v1;
2479
2480 v0 = pt0.fX;
2481 v1 = pt1.fX;
2482 if (v0 > v1) {
2483 SkTSwap<SkScalar>(v0, v1);
2484 }
2485 r.fLeft = SkScalarFloor(v0);
2486 r.fRight = SkScalarCeil(v1);
2487
2488 v0 = pt0.fY;
2489 v1 = pt1.fY;
2490 if (v0 > v1) {
2491 SkTSwap<SkScalar>(v0, v1);
2492 }
2493 r.fTop = SkScalarFloor(v0);
2494 r.fBottom = SkScalarCeil(v1);
2495
2496 if (paint.isAntiAlias()) {
2497 r.inset(-1, -1);
2498 }
2499 return this->doIRect(r);
2500}
2501
2502bool SkBounder::doRect(const SkRect& rect, const SkPaint& paint) {
2503 SkIRect r;
2504
2505 if (paint.getStyle() == SkPaint::kFill_Style) {
2506 rect.round(&r);
2507 } else {
2508 int rad = -1;
2509 rect.roundOut(&r);
2510 if (paint.isAntiAlias()) {
2511 rad = -2;
2512 }
2513 r.inset(rad, rad);
2514 }
2515 return this->doIRect(r);
2516}
2517
2518bool SkBounder::doPath(const SkPath& path, const SkPaint& paint, bool doFill) {
reed@android.comd252db02009-04-01 18:31:44 +00002519 SkIRect r;
2520 const SkRect& bounds = path.getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002521
2522 if (doFill) {
2523 bounds.round(&r);
2524 } else { // hairline
2525 bounds.roundOut(&r);
2526 }
2527
2528 if (paint.isAntiAlias()) {
2529 r.inset(-1, -1);
2530 }
2531 return this->doIRect(r);
2532}
2533
2534void SkBounder::commit() {
2535 // override in subclass
2536}
2537
2538////////////////////////////////////////////////////////////////////////////////////////////////
2539
2540#include "SkPath.h"
2541#include "SkDraw.h"
2542#include "SkRegion.h"
2543#include "SkBlitter.h"
2544
2545static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
2546 SkMaskFilter* filter, const SkMatrix* filterMatrix,
2547 SkIRect* bounds) {
2548 if (devPath.isEmpty()) {
2549 return false;
2550 }
2551
2552 SkIPoint margin;
2553 margin.set(0, 0);
2554
2555 // init our bounds from the path
2556 {
reed@android.comd252db02009-04-01 18:31:44 +00002557 SkRect pathBounds = devPath.getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002558 pathBounds.inset(-SK_ScalarHalf, -SK_ScalarHalf);
2559 pathBounds.roundOut(bounds);
2560 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002561
reed@android.com8a1c16f2008-12-17 15:59:43 +00002562 if (filter) {
2563 SkASSERT(filterMatrix);
reed@google.coma76de3d2011-01-13 18:30:42 +00002564
reed@android.com8a1c16f2008-12-17 15:59:43 +00002565 SkMask srcM, dstM;
reed@google.coma76de3d2011-01-13 18:30:42 +00002566
reed@android.com8a1c16f2008-12-17 15:59:43 +00002567 srcM.fBounds = *bounds;
2568 srcM.fFormat = SkMask::kA8_Format;
2569 srcM.fImage = NULL;
2570 if (!filter->filterMask(&dstM, srcM, *filterMatrix, &margin)) {
2571 return false;
2572 }
2573 *bounds = dstM.fBounds;
2574 }
2575
2576 if (clipBounds && !SkIRect::Intersects(*clipBounds, *bounds)) {
2577 return false;
2578 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002579
reed@android.com8a1c16f2008-12-17 15:59:43 +00002580 // (possibly) trim the srcM bounds to reflect the clip
2581 // (plus whatever slop the filter needs)
2582 if (clipBounds && !clipBounds->contains(*bounds)) {
2583 SkIRect tmp = *bounds;
2584 (void)tmp.intersect(*clipBounds);
reed@android.com35555912009-03-16 18:46:55 +00002585 // Ugh. Guard against gigantic margins from wacky filters. Without this
2586 // check we can request arbitrary amounts of slop beyond our visible
2587 // clip, and bring down the renderer (at least on finite RAM machines
2588 // like handsets, etc.). Need to balance this invented value between
2589 // quality of large filters like blurs, and the corresponding memory
2590 // requests.
2591 static const int MAX_MARGIN = 128;
2592 tmp.inset(-SkMin32(margin.fX, MAX_MARGIN),
2593 -SkMin32(margin.fY, MAX_MARGIN));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002594 (void)bounds->intersect(tmp);
2595 }
2596
2597 return true;
2598}
2599
2600static void draw_into_mask(const SkMask& mask, const SkPath& devPath) {
2601 SkBitmap bm;
2602 SkDraw draw;
2603 SkRegion clipRgn;
2604 SkMatrix matrix;
2605 SkPaint paint;
2606
2607 bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height(), mask.fRowBytes);
2608 bm.setPixels(mask.fImage);
2609
2610 clipRgn.setRect(0, 0, mask.fBounds.width(), mask.fBounds.height());
2611 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
2612 -SkIntToScalar(mask.fBounds.fTop));
2613
2614 draw.fBitmap = &bm;
2615 draw.fClip = &clipRgn;
2616 draw.fMatrix = &matrix;
2617 draw.fBounder = NULL;
2618 paint.setAntiAlias(true);
2619 draw.drawPath(devPath, paint);
2620}
2621
2622bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
2623 SkMaskFilter* filter, const SkMatrix* filterMatrix,
2624 SkMask* mask, SkMask::CreateMode mode) {
2625 if (SkMask::kJustRenderImage_CreateMode != mode) {
2626 if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
2627 return false;
2628 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002629
reed@android.com8a1c16f2008-12-17 15:59:43 +00002630 if (SkMask::kComputeBoundsAndRenderImage_CreateMode == mode) {
2631 mask->fFormat = SkMask::kA8_Format;
2632 mask->fRowBytes = mask->fBounds.width();
reed@android.com543ed932009-04-24 12:43:40 +00002633 size_t size = mask->computeImageSize();
2634 if (0 == size) {
2635 // we're too big to allocate the mask, abort
2636 return false;
2637 }
2638 mask->fImage = SkMask::AllocImage(size);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002639 memset(mask->fImage, 0, mask->computeImageSize());
2640 }
2641
2642 if (SkMask::kJustComputeBounds_CreateMode != mode) {
2643 draw_into_mask(*mask, devPath);
2644 }
reed@google.coma76de3d2011-01-13 18:30:42 +00002645
reed@android.com8a1c16f2008-12-17 15:59:43 +00002646 return true;
2647}