blob: 3687d1e98441330798b7234ad5aa3b08e181404f [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
11#include "SkBounder.h"
12#include "SkDevice.h"
13#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
16#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000017#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkScalarCompare.h"
19#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000020#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000021#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023
24//#define SK_TRACE_SAVERESTORE
25
26#ifdef SK_TRACE_SAVERESTORE
27 static int gLayerCounter;
28 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
29 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
30
31 static int gRecCounter;
32 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
33 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
34
35 static int gCanvasCounter;
36 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
37 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
38#else
39 #define inc_layer()
40 #define dec_layer()
41 #define inc_rec()
42 #define dec_rec()
43 #define inc_canvas()
44 #define dec_canvas()
45#endif
46
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000047typedef SkTLazy<SkPaint> SkLazyPaint;
48
reed@android.com8a1c16f2008-12-17 15:59:43 +000049///////////////////////////////////////////////////////////////////////////////
50// Helpers for computing fast bounds for quickReject tests
51
52static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
53 return paint != NULL && paint->isAntiAlias() ?
54 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
55}
56
57///////////////////////////////////////////////////////////////////////////////
58
59/* This is the record we keep for each SkDevice that the user installs.
60 The clip/matrix/proc are fields that reflect the top of the save/restore
61 stack. Whenever the canvas changes, it marks a dirty flag, and then before
62 these are used (assuming we're not on a layer) we rebuild these cache
63 values: they reflect the top of the save stack, but translated and clipped
64 by the device's XY offset and bitmap-bounds.
65*/
66struct DeviceCM {
67 DeviceCM* fNext;
68 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000069 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000071 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000072 // optional, related to canvas' external matrix
73 const SkMatrix* fMVMatrix;
74 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000075
bungeman@google.com88edf1e2011-08-08 19:41:56 +000076 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 : fNext(NULL) {
78 if (NULL != device) {
79 device->ref();
80 device->lockPixels();
81 }
reed@google.com4b226022011-01-11 18:32:13 +000082 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000084 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000085
bungeman@google.com88edf1e2011-08-08 19:41:56 +000086 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 if (NULL != fDevice) {
88 fDevice->unlockPixels();
89 fDevice->unref();
90 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 SkDELETE(fPaint);
92 }
reed@google.com4b226022011-01-11 18:32:13 +000093
reed@google.com045e62d2011-10-24 12:19:46 +000094 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
95 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +000096 int x = fDevice->getOrigin().x();
97 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 int width = fDevice->width();
99 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000100
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 if ((x | y) == 0) {
102 fMatrix = &totalMatrix;
103 fClip = totalClip;
104 } else {
105 fMatrixStorage = totalMatrix;
106 fMatrixStorage.postTranslate(SkIntToScalar(-x),
107 SkIntToScalar(-y));
108 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000109
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 totalClip.translate(-x, -y, &fClip);
111 }
112
reed@google.com045e62d2011-10-24 12:19:46 +0000113 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114
115 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000118 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 SkRegion::kDifference_Op);
120 }
reed@google.com4b226022011-01-11 18:32:13 +0000121
reed@google.com045e62d2011-10-24 12:19:46 +0000122 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
124#ifdef SK_DEBUG
125 if (!fClip.isEmpty()) {
126 SkIRect deviceR;
127 deviceR.set(0, 0, width, height);
128 SkASSERT(deviceR.contains(fClip.getBounds()));
129 }
130#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000131 // default is to assume no external matrix
132 fMVMatrix = NULL;
133 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000135
136 // can only be called after calling updateMC()
137 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
138 fMVMatrixStorage.setConcat(extI, *fMatrix);
139 fMVMatrix = &fMVMatrixStorage;
140 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
141 }
142
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000144 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145};
146
147/* This is the record we keep for each save/restore level in the stack.
148 Since a level optionally copies the matrix and/or stack, we have pointers
149 for these fields. If the value is copied for this level, the copy is
150 stored in the ...Storage field, and the pointer points to that. If the
151 value is not copied for this level, we ignore ...Storage, and just point
152 at the corresponding value in the previous level in the stack.
153*/
154class SkCanvas::MCRec {
155public:
156 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000157 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
158 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
159 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000160
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 DeviceCM* fLayer;
162 /* If there are any layers in the stack, this points to the top-most
163 one that is at or below this level in the stack (so we know what
164 bitmap/device to draw into from this level. This value is NOT
165 reference counted, since the real owner is either our fLayer field,
166 or a previous one in a lower level.)
167 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000168 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169
170 MCRec(const MCRec* prev, int flags) {
171 if (NULL != prev) {
172 if (flags & SkCanvas::kMatrix_SaveFlag) {
173 fMatrixStorage = *prev->fMatrix;
174 fMatrix = &fMatrixStorage;
175 } else {
176 fMatrix = prev->fMatrix;
177 }
reed@google.com4b226022011-01-11 18:32:13 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000180 fRasterClipStorage = *prev->fRasterClip;
181 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 } else {
reed@google.com00177082011-10-12 14:34:30 +0000183 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 }
185
186 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000187 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188
189 fTopLayer = prev->fTopLayer;
190 } else { // no prev
191 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000192
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000194 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 fFilter = NULL;
196 fTopLayer = NULL;
197 }
198 fLayer = NULL;
199
200 // don't bother initializing fNext
201 inc_rec();
202 }
203 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000204 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 SkDELETE(fLayer);
206 dec_rec();
207 }
reed@google.com4b226022011-01-11 18:32:13 +0000208
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209private:
reed@google.com00177082011-10-12 14:34:30 +0000210 SkMatrix fMatrixStorage;
211 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212};
213
214class SkDrawIter : public SkDraw {
215public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000216 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000217 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000218 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 canvas->updateDeviceCMCache();
220
reed@google.com90c07ea2012-04-13 13:50:27 +0000221 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 fBounder = canvas->getBounder();
223 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000224 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 }
reed@google.com4b226022011-01-11 18:32:13 +0000226
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 bool next() {
228 // skip over recs with empty clips
229 if (fSkipEmptyClips) {
230 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
231 fCurrLayer = fCurrLayer->fNext;
232 }
233 }
234
reed@google.comf68c5e22012-02-24 16:38:58 +0000235 const DeviceCM* rec = fCurrLayer;
236 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
238 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000239 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
240 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 fDevice = rec->fDevice;
242 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000244 fMVMatrix = rec->fMVMatrix;
245 fExtMatrix = rec->fExtMatrix;
246 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247
248 fCurrLayer = rec->fNext;
249 if (fBounder) {
250 fBounder->setClip(fClip);
251 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000253
bsalomon@google.comd302f142011-03-03 13:54:13 +0000254 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 return true;
256 }
257 return false;
258 }
reed@google.com4b226022011-01-11 18:32:13 +0000259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000261 int getX() const { return fDevice->getOrigin().x(); }
262 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 const SkMatrix& getMatrix() const { return *fMatrix; }
264 const SkRegion& getClip() const { return *fClip; }
265 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267private:
268 SkCanvas* fCanvas;
269 const DeviceCM* fCurrLayer;
270 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 SkBool8 fSkipEmptyClips;
272
273 typedef SkDraw INHERITED;
274};
275
276/////////////////////////////////////////////////////////////////////////////
277
278class AutoDrawLooper {
279public:
reed@google.com8926b162012-03-23 15:36:36 +0000280 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
281 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000282 fCanvas = canvas;
283 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000285 fPaint = NULL;
286 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000287 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289
reed@google.com8926b162012-03-23 15:36:36 +0000290 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
291 SkPaint tmp;
292 tmp.setImageFilter(fOrigPaint.getImageFilter());
293 // it would be nice if we had a guess at the bounds, instead of null
294 (void)canvas->internalSaveLayer(NULL, &tmp,
295 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
296 // we'll clear the imageFilter for the actual draws in next(), so
297 // it will only be applied during the restore().
298 fDoClearImageFilter = true;
299 }
300
reed@google.com4e2b3d32011-04-07 14:18:59 +0000301 if (fLooper) {
302 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000303 fIsSimple = false;
304 } else {
305 // can we be marked as simple?
306 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000307 }
308 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000309
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000311 if (fDoClearImageFilter) {
312 fCanvas->internalRestore();
313 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000314 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000316
reed@google.com4e2b3d32011-04-07 14:18:59 +0000317 const SkPaint& paint() const {
318 SkASSERT(fPaint);
319 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000321
reed@google.com129ec222012-05-15 13:24:09 +0000322 bool next(SkDrawFilter::Type drawType) {
323 if (fDone) {
324 return false;
325 } else if (fIsSimple) {
326 fDone = true;
327 fPaint = &fOrigPaint;
328 return !fPaint->nothingToDraw();
329 } else {
330 return this->doNext(drawType);
331 }
332 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000333
reed@android.com8a1c16f2008-12-17 15:59:43 +0000334private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000335 SkLazyPaint fLazyPaint;
336 SkCanvas* fCanvas;
337 const SkPaint& fOrigPaint;
338 SkDrawLooper* fLooper;
339 SkDrawFilter* fFilter;
340 const SkPaint* fPaint;
341 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000342 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000343 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000344 bool fIsSimple;
345
346 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347};
348
reed@google.com129ec222012-05-15 13:24:09 +0000349bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000350 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000351 SkASSERT(!fIsSimple);
352 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
353
354 SkPaint* paint = fLazyPaint.set(fOrigPaint);
355
356 if (fDoClearImageFilter) {
357 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000359
reed@google.com129ec222012-05-15 13:24:09 +0000360 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000361 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000362 return false;
363 }
364 if (fFilter) {
365 fFilter->filter(paint, drawType);
366 if (NULL == fLooper) {
367 // no looper means we only draw once
368 fDone = true;
369 }
370 }
371 fPaint = paint;
372
373 // if we only came in here for the imagefilter, mark us as done
374 if (!fLooper && !fFilter) {
375 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000376 }
377
378 // call this after any possible paint modifiers
379 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000380 fPaint = NULL;
381 return false;
382 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000383 return true;
384}
385
reed@android.com8a1c16f2008-12-17 15:59:43 +0000386/* Stack helper for managing a SkBounder. In the destructor, if we were
387 given a bounder, we call its commit() method, signifying that we are
388 done accumulating bounds for that draw.
389*/
390class SkAutoBounderCommit {
391public:
392 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
393 ~SkAutoBounderCommit() {
394 if (NULL != fBounder) {
395 fBounder->commit();
396 }
397 }
398private:
399 SkBounder* fBounder;
400};
401
402#include "SkColorPriv.h"
403
404class AutoValidator {
405public:
406 AutoValidator(SkDevice* device) : fDevice(device) {}
407 ~AutoValidator() {
408#ifdef SK_DEBUG
409 const SkBitmap& bm = fDevice->accessBitmap(false);
410 if (bm.config() == SkBitmap::kARGB_4444_Config) {
411 for (int y = 0; y < bm.height(); y++) {
412 const SkPMColor16* p = bm.getAddr16(0, y);
413 for (int x = 0; x < bm.width(); x++) {
414 SkPMColor16 c = p[x];
415 SkPMColor16Assert(c);
416 }
417 }
418 }
419#endif
420 }
421private:
422 SkDevice* fDevice;
423};
424
425////////// macros to place around the internal draw calls //////////////////
426
reed@google.com8926b162012-03-23 15:36:36 +0000427#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
428/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
429 AutoDrawLooper looper(this, paint, true); \
430 while (looper.next(type)) { \
431 SkAutoBounderCommit ac(fBounder); \
432 SkDrawIter iter(this);
433
reed@google.com4e2b3d32011-04-07 14:18:59 +0000434#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000436 AutoDrawLooper looper(this, paint); \
437 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 SkAutoBounderCommit ac(fBounder); \
439 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000440
reed@google.com4e2b3d32011-04-07 14:18:59 +0000441#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442
443////////////////////////////////////////////////////////////////////////////
444
445SkDevice* SkCanvas::init(SkDevice* device) {
446 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000447 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000449 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000450 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000451 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000452 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000453 fSaveLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454
455 fMCRec = (MCRec*)fMCStack.push_back();
456 new (fMCRec) MCRec(NULL, 0);
457
458 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
459 fMCRec->fTopLayer = fMCRec->fLayer;
460 fMCRec->fNext = NULL;
461
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000462 fExternalMatrix.reset();
463 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000464 fUseExternalMatrix = false;
465
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 return this->setDevice(device);
467}
468
reed@google.comcde92112011-07-06 20:00:52 +0000469SkCanvas::SkCanvas()
470: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000471 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000472
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000473 this->init(NULL);
474}
475
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000477 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 inc_canvas();
479
480 this->init(device);
481}
482
483SkCanvas::SkCanvas(const SkBitmap& bitmap)
484 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
485 inc_canvas();
486
reed@google.comcde92112011-07-06 20:00:52 +0000487 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488}
489
490SkCanvas::~SkCanvas() {
491 // free up the contents of our deque
492 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000493 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000494
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 this->internalRestore(); // restore the last, since we're going away
496
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000497 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000498
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 dec_canvas();
500}
501
502SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
503 SkRefCnt_SafeAssign(fBounder, bounder);
504 return bounder;
505}
506
507SkDrawFilter* SkCanvas::getDrawFilter() const {
508 return fMCRec->fFilter;
509}
510
511SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
512 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
513 return filter;
514}
515
516///////////////////////////////////////////////////////////////////////////////
517
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000518void SkCanvas::flush() {
519 SkDevice* device = this->getDevice();
520 if (device) {
521 device->flush();
522 }
523}
524
reed@google.com210ce002011-11-01 14:24:23 +0000525SkISize SkCanvas::getDeviceSize() const {
526 SkDevice* d = this->getDevice();
527 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
528}
529
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530SkDevice* SkCanvas::getDevice() const {
531 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000532 SkDeque::F2BIter iter(fMCStack);
533 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 SkASSERT(rec && rec->fLayer);
535 return rec->fLayer->fDevice;
536}
537
reed@google.com0b53d592012-03-19 18:26:34 +0000538SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
539 if (updateMatrixClip) {
540 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
541 }
reed@google.com9266fed2011-03-30 00:18:03 +0000542 return fMCRec->fTopLayer->fDevice;
543}
544
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545SkDevice* SkCanvas::setDevice(SkDevice* device) {
546 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000547 SkDeque::F2BIter iter(fMCStack);
548 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 SkASSERT(rec && rec->fLayer);
550 SkDevice* rootDevice = rec->fLayer->fDevice;
551
552 if (rootDevice == device) {
553 return device;
554 }
reed@google.com4b226022011-01-11 18:32:13 +0000555
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 /* Notify the devices that they are going in/out of scope, so they can do
557 things like lock/unlock their pixels, etc.
558 */
559 if (device) {
560 device->lockPixels();
561 }
562 if (rootDevice) {
563 rootDevice->unlockPixels();
564 }
565
566 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
567 rootDevice = device;
568
569 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000570
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 /* Now we update our initial region to have the bounds of the new device,
572 and then intersect all of the clips in our stack with these bounds,
573 to ensure that we can't draw outside of the device's bounds (and trash
574 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 NOTE: this is only a partial-fix, since if the new device is larger than
577 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000578 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
580 reconstruct the correct clips, so this approximation will have to do.
581 The caller really needs to restore() back to the base if they want to
582 accurately take advantage of the new device bounds.
583 */
584
reed@google.com42aea282012-03-28 16:19:15 +0000585 SkIRect bounds;
586 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000588 } else {
589 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 }
reed@google.com42aea282012-03-28 16:19:15 +0000591 // now jam our 1st clip to be bounds, and intersect the rest with that
592 rec->fRasterClip->setRect(bounds);
593 while ((rec = (MCRec*)iter.next()) != NULL) {
594 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
595 }
596
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 return device;
598}
599
reed@google.comaf951c92011-06-16 19:10:39 +0000600SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
601 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 device->unref();
603 return device;
604}
605
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000606bool SkCanvas::readPixels(SkBitmap* bitmap,
607 int x, int y,
608 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000609 SkDevice* device = this->getDevice();
610 if (!device) {
611 return false;
612 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000613 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000614}
615
bsalomon@google.comc6980972011-11-02 19:57:21 +0000616bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000617 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000618 if (!device) {
619 return false;
620 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000621
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000622 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000623 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000624 if (!bounds.intersect(srcRect)) {
625 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000626 }
627
628 SkBitmap tmp;
629 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
630 bounds.height());
631 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
632 bitmap->swap(tmp);
633 return true;
634 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000635 return false;
636 }
reed@google.com51df9e32010-12-23 19:29:18 +0000637}
638
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000639void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
640 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000641 SkDevice* device = this->getDevice();
642 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000643 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000644 }
645}
646
junov@google.com4370aed2012-01-18 16:21:08 +0000647SkCanvas* SkCanvas::canvasForDrawIter() {
648 return this;
649}
650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651//////////////////////////////////////////////////////////////////////////////
652
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653void SkCanvas::updateDeviceCMCache() {
654 if (fDeviceCMDirty) {
655 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000656 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000658
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000660 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000661 if (fUseExternalMatrix) {
662 layer->updateExternalMatrix(fExternalMatrix,
663 fExternalInverse);
664 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000666 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000668 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000669 if (fUseExternalMatrix) {
670 layer->updateExternalMatrix(fExternalMatrix,
671 fExternalInverse);
672 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673 } while ((layer = layer->fNext) != NULL);
674 }
675 fDeviceCMDirty = false;
676 }
677}
678
reed@android.comf2b98d62010-12-20 18:26:13 +0000679void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000680 const SkRegion& clip,
681 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000682 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000683 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000684 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000685 fLastDeviceToGainFocus = device;
686 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
689///////////////////////////////////////////////////////////////////////////////
690
691int SkCanvas::internalSave(SaveFlags flags) {
692 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000693
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694 MCRec* newTop = (MCRec*)fMCStack.push_back();
695 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697 newTop->fNext = fMCRec;
698 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000699
reed@google.com5c3d1472011-02-22 19:12:23 +0000700 fClipStack.save();
701 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
702
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 return saveCount;
704}
705
706int SkCanvas::save(SaveFlags flags) {
707 // call shared impl
708 return this->internalSave(flags);
709}
710
711#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
712#define C16MASK (1 << SkBitmap::kRGB_565_Config)
713#define C8MASK (1 << SkBitmap::kA8_Config)
714
715static SkBitmap::Config resolve_config(SkCanvas* canvas,
716 const SkIRect& bounds,
717 SkCanvas::SaveFlags flags,
718 bool* isOpaque) {
719 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
720
721#if 0
722 // loop through and union all the configs we may draw into
723 uint32_t configMask = 0;
724 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
725 {
726 SkDevice* device = canvas->getLayerDevice(i);
727 if (device->intersects(bounds))
728 configMask |= 1 << device->config();
729 }
730
731 // if the caller wants alpha or fullcolor, we can't return 565
732 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
733 SkCanvas::kHasAlphaLayer_SaveFlag))
734 configMask &= ~C16MASK;
735
736 switch (configMask) {
737 case C8MASK: // if we only have A8, return that
738 return SkBitmap::kA8_Config;
739
740 case C16MASK: // if we only have 565, return that
741 return SkBitmap::kRGB_565_Config;
742
743 default:
744 return SkBitmap::kARGB_8888_Config; // default answer
745 }
746#else
747 return SkBitmap::kARGB_8888_Config; // default answer
748#endif
749}
750
751static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
752 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
753}
754
junov@chromium.orga907ac32012-02-24 21:54:07 +0000755bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
756 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000757 SkIRect clipBounds;
758 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000759 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000760 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000761 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 if (NULL != bounds) {
763 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000764
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 this->getTotalMatrix().mapRect(&r, *bounds);
766 r.roundOut(&ir);
767 // early exit if the layer's bounds are clipped out
768 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000769 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000770 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000771 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000772 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 }
774 } else { // no user bounds, so just use the clip
775 ir = clipBounds;
776 }
777
reed@google.com5c3d1472011-02-22 19:12:23 +0000778 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000779
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 // early exit if the clip is now empty
781 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000782 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000783 return false;
784 }
785
786 if (intersection) {
787 *intersection = ir;
788 }
789 return true;
790}
791
792int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
793 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000794 return this->internalSaveLayer(bounds, paint, flags, false);
795}
796
797int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
798 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000799 // do this before we create the layer. We don't call the public save() since
800 // that would invoke a possibly overridden virtual
801 int count = this->internalSave(flags);
802
803 fDeviceCMDirty = true;
804
805 SkIRect ir;
806 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 return count;
808 }
809
reed@google.comb55deeb2012-01-06 14:43:09 +0000810 // Kill the imagefilter if our device doesn't allow it
811 SkLazyPaint lazyP;
812 if (paint && paint->getImageFilter()) {
813 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000814 if (justForImageFilter) {
815 // early exit if the layer was just for the imageFilter
816 return count;
817 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000818 SkPaint* p = lazyP.set(*paint);
819 p->setImageFilter(NULL);
820 paint = p;
821 }
822 }
823
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 bool isOpaque;
825 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
826
reed@google.com76dd2772012-01-05 21:15:07 +0000827 SkDevice* device;
828 if (paint && paint->getImageFilter()) {
829 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
830 isOpaque);
831 } else {
832 device = this->createLayerDevice(config, ir.width(), ir.height(),
833 isOpaque);
834 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000835 if (NULL == device) {
836 SkDebugf("Unable to create device for layer.");
837 return count;
838 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000839
reed@google.com6f8f2922011-03-04 22:27:10 +0000840 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
842 device->unref();
843
844 layer->fNext = fMCRec->fTopLayer;
845 fMCRec->fLayer = layer;
846 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
847
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000848 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 return count;
850}
851
852int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
853 SaveFlags flags) {
854 if (0xFF == alpha) {
855 return this->saveLayer(bounds, NULL, flags);
856 } else {
857 SkPaint tmpPaint;
858 tmpPaint.setAlpha(alpha);
859 return this->saveLayer(bounds, &tmpPaint, flags);
860 }
861}
862
863void SkCanvas::restore() {
864 // check for underflow
865 if (fMCStack.count() > 1) {
866 this->internalRestore();
867 }
868}
869
870void SkCanvas::internalRestore() {
871 SkASSERT(fMCStack.count() != 0);
872
873 fDeviceCMDirty = true;
874 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000875 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000876
reed@google.com5c3d1472011-02-22 19:12:23 +0000877 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000878 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 DeviceCM* layer = fMCRec->fLayer; // may be null
880 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
881 fMCRec->fLayer = NULL;
882
883 // now do the normal restore()
884 fMCRec->~MCRec(); // balanced in save()
885 fMCStack.pop_back();
886 fMCRec = (MCRec*)fMCStack.back();
887
888 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
889 since if we're being recorded, we don't want to record this (the
890 recorder will have already recorded the restore).
891 */
892 if (NULL != layer) {
893 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000894 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000895 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
896 layer->fPaint);
897 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000899
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000900 SkASSERT(fSaveLayerCount > 0);
901 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 }
903 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000904 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000905
906 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907}
908
909int SkCanvas::getSaveCount() const {
910 return fMCStack.count();
911}
912
913void SkCanvas::restoreToCount(int count) {
914 // sanity check
915 if (count < 1) {
916 count = 1;
917 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000918
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000919 int n = this->getSaveCount() - count;
920 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 this->restore();
922 }
923}
924
reed@google.com7c202932011-12-14 18:48:05 +0000925bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000926 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000927}
928
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929/////////////////////////////////////////////////////////////////////////////
930
931// can't draw it if its empty, or its too big for a fixed-point width or height
932static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000933 return bitmap.width() <= 0 || bitmap.height() <= 0
934#ifndef SK_ALLOW_OVER_32K_BITMAPS
935 || bitmap.width() > 32767 || bitmap.height() > 32767
936#endif
937 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938}
939
reed@android.comf2b98d62010-12-20 18:26:13 +0000940void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 const SkMatrix& matrix, const SkPaint* paint) {
942 if (reject_bitmap(bitmap)) {
943 return;
944 }
945
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000946 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000948 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000950 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951}
952
reed@google.com76dd2772012-01-05 21:15:07 +0000953#include "SkImageFilter.h"
954
955class DeviceImageFilterProxy : public SkImageFilter::Proxy {
956public:
957 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000958
reed@google.com8926b162012-03-23 15:36:36 +0000959 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
960 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
961 w, h, false);
962 }
963 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
964 return fDevice->canHandleImageFilter(filter);
965 }
966 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000967 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000968 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
969 return fDevice->filterImage(filter, src, ctm, result, offset);
970 }
971
reed@google.com76dd2772012-01-05 21:15:07 +0000972private:
973 SkDevice* fDevice;
974};
975
reed@google.com8926b162012-03-23 15:36:36 +0000976void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
977 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 SkPaint tmp;
979 if (NULL == paint) {
980 tmp.setDither(true);
981 paint = &tmp;
982 }
reed@google.com4b226022011-01-11 18:32:13 +0000983
reed@google.com8926b162012-03-23 15:36:36 +0000984 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000986 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000987 paint = &looper.paint();
988 SkImageFilter* filter = paint->getImageFilter();
989 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000990 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000991 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000992 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000993 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000994 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000995 SkPaint tmpUnfiltered(*paint);
996 tmpUnfiltered.setImageFilter(NULL);
997 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +0000998 }
999 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001000 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001001 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001003 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004}
1005
reed@google.com8926b162012-03-23 15:36:36 +00001006void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1007 const SkPaint* paint) {
1008 SkDEBUGCODE(bitmap.validate();)
1009
1010 if (reject_bitmap(bitmap)) {
1011 return;
1012 }
1013
1014 SkPaint tmp;
1015 if (NULL == paint) {
1016 paint = &tmp;
1017 }
1018
1019 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
1020
1021 while (iter.next()) {
1022 paint = &looper.paint();
1023 SkImageFilter* filter = paint->getImageFilter();
1024 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1025 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1026 DeviceImageFilterProxy proxy(iter.fDevice);
1027 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001028 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1029 &dst, &pos)) {
1030 SkPaint tmpUnfiltered(*paint);
1031 tmpUnfiltered.setImageFilter(NULL);
1032 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1033 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001034 }
1035 } else {
1036 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1037 }
1038 }
1039 LOOPER_END
1040}
1041
reed@android.com8a1c16f2008-12-17 15:59:43 +00001042/////////////////////////////////////////////////////////////////////////////
1043
1044bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1045 fDeviceCMDirty = true;
1046 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001047 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048 return fMCRec->fMatrix->preTranslate(dx, dy);
1049}
1050
1051bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1052 fDeviceCMDirty = true;
1053 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001054 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 return fMCRec->fMatrix->preScale(sx, sy);
1056}
1057
1058bool SkCanvas::rotate(SkScalar degrees) {
1059 fDeviceCMDirty = true;
1060 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001061 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 return fMCRec->fMatrix->preRotate(degrees);
1063}
1064
1065bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1066 fDeviceCMDirty = true;
1067 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001068 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069 return fMCRec->fMatrix->preSkew(sx, sy);
1070}
1071
1072bool SkCanvas::concat(const SkMatrix& matrix) {
1073 fDeviceCMDirty = true;
1074 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001075 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 return fMCRec->fMatrix->preConcat(matrix);
1077}
1078
1079void SkCanvas::setMatrix(const SkMatrix& matrix) {
1080 fDeviceCMDirty = true;
1081 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001082 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 *fMCRec->fMatrix = matrix;
1084}
1085
1086// this is not virtual, so it must call a virtual method so that subclasses
1087// will see its action
1088void SkCanvas::resetMatrix() {
1089 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001090
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 matrix.reset();
1092 this->setMatrix(matrix);
1093}
1094
1095//////////////////////////////////////////////////////////////////////////////
1096
reed@google.comc42d35d2011-10-12 11:57:42 +00001097bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001098 AutoValidateClip avc(this);
1099
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 fDeviceCMDirty = true;
1101 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001102 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103
1104 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001105 // for these simpler matrices, we can stay a rect ever after applying
1106 // the matrix. This means we don't have to a) make a path, and b) tell
1107 // the region code to scan-convert the path, only to discover that it
1108 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110
1111 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001112 fClipStack.clipDevRect(r, op, doAA);
1113 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001115 // since we're rotate or some such thing, we convert the rect to a path
1116 // and clip against that, since it can handle any matrix. However, to
1117 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1118 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 SkPath path;
1120
1121 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001122 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 }
1124}
1125
reed@google.com00177082011-10-12 14:34:30 +00001126static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001127 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001128 // base is used to limit the size (and therefore memory allocation) of the
1129 // region that results from scan converting devPath.
1130 SkRegion base;
1131
reed@google.com819c9212011-02-23 18:56:55 +00001132 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001133 // since we are intersect, we can do better (tighter) with currRgn's
1134 // bounds, than just using the device. However, if currRgn is complex,
1135 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001136 if (currClip->isRect()) {
1137 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001138 } else {
reed@google.com00177082011-10-12 14:34:30 +00001139 base.setRect(currClip->getBounds());
1140 SkRasterClip clip;
1141 clip.setPath(devPath, base, doAA);
1142 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001143 }
reed@google.com819c9212011-02-23 18:56:55 +00001144 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001145 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001146 if (!device) {
1147 return currClip->setEmpty();
1148 }
1149
junov@chromium.orga907ac32012-02-24 21:54:07 +00001150 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001151
1152 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001153 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001154 } else {
reed@google.com00177082011-10-12 14:34:30 +00001155 SkRasterClip clip;
1156 clip.setPath(devPath, base, doAA);
1157 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001158 }
1159 }
1160}
1161
reed@google.comc42d35d2011-10-12 11:57:42 +00001162bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001163 AutoValidateClip avc(this);
1164
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 fDeviceCMDirty = true;
1166 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001167 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168
1169 SkPath devPath;
1170 path.transform(*fMCRec->fMatrix, &devPath);
1171
reed@google.comfe701122011-11-08 19:41:23 +00001172 // Check if the transfomation, or the original path itself
1173 // made us empty. Note this can also happen if we contained NaN
1174 // values. computing the bounds detects this, and will set our
1175 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1176 if (devPath.getBounds().isEmpty()) {
1177 // resetting the path will remove any NaN or other wanky values
1178 // that might upset our scan converter.
1179 devPath.reset();
1180 }
1181
reed@google.com5c3d1472011-02-22 19:12:23 +00001182 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001183 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001184
reed@google.com00177082011-10-12 14:34:30 +00001185 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186}
1187
1188bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001189 AutoValidateClip avc(this);
1190
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 fDeviceCMDirty = true;
1192 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001193 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194
reed@google.com5c3d1472011-02-22 19:12:23 +00001195 // todo: signal fClipStack that we have a region, and therefore (I guess)
1196 // we have to ignore it, and use the region directly?
1197 fClipStack.clipDevRect(rgn.getBounds());
1198
reed@google.com00177082011-10-12 14:34:30 +00001199 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200}
1201
reed@google.com819c9212011-02-23 18:56:55 +00001202#ifdef SK_DEBUG
1203void SkCanvas::validateClip() const {
1204 // construct clipRgn from the clipstack
1205 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001206 if (!device) {
1207 SkASSERT(this->getTotalClip().isEmpty());
1208 return;
1209 }
1210
reed@google.com819c9212011-02-23 18:56:55 +00001211 SkIRect ir;
1212 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001213 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001214
1215 SkClipStack::B2FIter iter(fClipStack);
1216 const SkClipStack::B2FIter::Clip* clip;
1217 while ((clip = iter.next()) != NULL) {
1218 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001219 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001220 } else if (clip->fRect) {
1221 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001222 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001223 } else {
reed@google.com00177082011-10-12 14:34:30 +00001224 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001225 }
1226 }
1227
reed@google.com6f8f2922011-03-04 22:27:10 +00001228#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001229 // now compare against the current rgn
1230 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001231 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001232#endif
reed@google.com819c9212011-02-23 18:56:55 +00001233}
1234#endif
1235
reed@google.com90c07ea2012-04-13 13:50:27 +00001236void SkCanvas::replayClips(ClipVisitor* visitor) const {
1237 SkClipStack::B2FIter iter(fClipStack);
1238 const SkClipStack::B2FIter::Clip* clip;
1239
robertphillips@google.com7460b372012-04-25 16:54:51 +00001240 SkRect empty = { 0, 0, 0, 0 };
reed@google.com90c07ea2012-04-13 13:50:27 +00001241 while ((clip = iter.next()) != NULL) {
1242 if (clip->fPath) {
1243 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1244 } else if (clip->fRect) {
1245 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1246 } else {
1247 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1248 }
1249 }
1250}
1251
reed@google.com5c3d1472011-02-22 19:12:23 +00001252///////////////////////////////////////////////////////////////////////////////
1253
reed@android.comba09de42010-02-05 20:46:05 +00001254void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001256 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1257 fLocalBoundsCompareTypeBW;
1258
1259 if (!this->getClipBounds(&r, et)) {
1260 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001262 rCompare.set(SkScalarToCompareType(r.fLeft),
1263 SkScalarToCompareType(r.fTop),
1264 SkScalarToCompareType(r.fRight),
1265 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 }
1267}
1268
reed@android.comd252db02009-04-01 18:31:44 +00001269/* current impl ignores edgetype, and relies on
1270 getLocalClipBoundsCompareType(), which always returns a value assuming
1271 antialiasing (worst case)
1272 */
reed@android.comba09de42010-02-05 20:46:05 +00001273bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001274
reed@google.com16078632011-12-06 18:56:37 +00001275 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001276 return true;
1277
reed@google.com00177082011-10-12 14:34:30 +00001278 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 return true;
1280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281
tomhudson@google.com8d430182011-06-06 19:11:19 +00001282 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001283 SkRect dst;
1284 fMCRec->fMatrix->mapRect(&dst, rect);
1285 SkIRect idst;
1286 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001287 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001288 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001289 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001290
reed@android.coma380ae42009-07-21 01:17:02 +00001291 // for speed, do the most likely reject compares first
1292 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1293 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1294 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1295 return true;
1296 }
1297 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1298 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1299 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1300 return true;
1301 }
1302 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304}
1305
1306bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001307 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308}
1309
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001310static inline int pinIntForScalar(int x) {
1311#ifdef SK_SCALAR_IS_FIXED
1312 if (x < SK_MinS16) {
1313 x = SK_MinS16;
1314 } else if (x > SK_MaxS16) {
1315 x = SK_MaxS16;
1316 }
1317#endif
1318 return x;
1319}
1320
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001322 SkIRect ibounds;
1323 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 return false;
1325 }
1326
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001327 SkMatrix inverse;
1328 // if we can't invert the CTM, we can't return local clip bounds
1329 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001330 if (bounds) {
1331 bounds->setEmpty();
1332 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001333 return false;
1334 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001336 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001337 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 // adjust it outwards if we are antialiasing
1339 int inset = (kAA_EdgeType == et);
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001340
1341 // SkRect::iset() will correctly assert if we pass a value out of range
1342 // (when SkScalar==fixed), so we pin to legal values. This does not
1343 // really returnt the correct answer, but its the best we can do given
1344 // that we've promised to return SkRect (even though we support devices
1345 // that can be larger than 32K in width or height).
1346 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1347 pinIntForScalar(ibounds.fTop - inset),
1348 pinIntForScalar(ibounds.fRight + inset),
1349 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350 inverse.mapRect(bounds, r);
1351 }
1352 return true;
1353}
1354
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001355bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001356 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001357 if (clip.isEmpty()) {
1358 if (bounds) {
1359 bounds->setEmpty();
1360 }
1361 return false;
1362 }
1363
1364 if (NULL != bounds) {
1365 *bounds = clip.getBounds();
1366 }
1367 return true;
1368}
1369
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370const SkMatrix& SkCanvas::getTotalMatrix() const {
1371 return *fMCRec->fMatrix;
1372}
1373
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001374SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001375 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1376 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001377 return kComplex_ClipType;
1378}
1379
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001381 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382}
1383
reed@android.comf2b98d62010-12-20 18:26:13 +00001384void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1385 if (NULL == matrix || matrix->isIdentity()) {
1386 if (fUseExternalMatrix) {
1387 fDeviceCMDirty = true;
1388 }
1389 fUseExternalMatrix = false;
1390 } else {
mike@reedtribe.org90bf4272012-04-14 19:06:16 +00001391 if (matrix->invert(&fExternalInverse)) {
1392 fExternalMatrix = *matrix;
1393 fUseExternalMatrix = true;
1394 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.comfc9a3be2012-04-12 13:52:14 +00001395 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001396 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001397}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398
bsalomon@google.come97f0852011-06-17 13:10:25 +00001399SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1400 int width, int height,
1401 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001402 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001403 if (device) {
1404 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1405 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001406 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001407 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001408 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409}
1410
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001411SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001412 int width, int height,
1413 bool isOpaque) {
1414 SkDevice* device = this->getDevice();
1415 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001416 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001417 } else {
1418 return NULL;
1419 }
1420}
1421
1422
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423//////////////////////////////////////////////////////////////////////////////
1424// These are the virtual drawing methods
1425//////////////////////////////////////////////////////////////////////////////
1426
reed@google.com2a981812011-04-14 18:59:28 +00001427void SkCanvas::clear(SkColor color) {
1428 SkDrawIter iter(this);
1429
1430 while (iter.next()) {
1431 iter.fDevice->clear(color);
1432 }
1433}
1434
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001436 this->internalDrawPaint(paint);
1437}
1438
1439void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001440 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441
1442 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001443 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 }
1445
reed@google.com4e2b3d32011-04-07 14:18:59 +00001446 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447}
1448
1449void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1450 const SkPaint& paint) {
1451 if ((long)count <= 0) {
1452 return;
1453 }
1454
reed@google.coma584aed2012-05-16 14:06:02 +00001455 if (paint.canComputeFastBounds()) {
1456 SkRect r;
1457 // special-case 2 points (common for drawing a single line)
1458 if (2 == count) {
1459 r.set(pts[0], pts[1]);
1460 } else {
1461 r.set(pts, count);
1462 }
1463 SkRect storage;
1464 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage),
1465 paint2EdgeType(&paint))) {
1466 return;
1467 }
1468 }
1469
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470 SkASSERT(pts != NULL);
1471
reed@google.com4e2b3d32011-04-07 14:18:59 +00001472 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001473
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001475 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 }
reed@google.com4b226022011-01-11 18:32:13 +00001477
reed@google.com4e2b3d32011-04-07 14:18:59 +00001478 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479}
1480
1481void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1482 if (paint.canComputeFastBounds()) {
1483 SkRect storage;
1484 if (this->quickReject(paint.computeFastBounds(r, &storage),
1485 paint2EdgeType(&paint))) {
1486 return;
1487 }
1488 }
reed@google.com4b226022011-01-11 18:32:13 +00001489
reed@google.com4e2b3d32011-04-07 14:18:59 +00001490 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491
1492 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001493 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 }
1495
reed@google.com4e2b3d32011-04-07 14:18:59 +00001496 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497}
1498
1499void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001500 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001501 SkRect storage;
1502 const SkRect& bounds = path.getBounds();
1503 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 paint2EdgeType(&paint))) {
1505 return;
1506 }
1507 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001508 if (path.isEmpty()) {
1509 if (path.isInverseFillType()) {
1510 this->internalDrawPaint(paint);
1511 }
1512 return;
1513 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514
reed@google.com4e2b3d32011-04-07 14:18:59 +00001515 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516
1517 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001518 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519 }
1520
reed@google.com4e2b3d32011-04-07 14:18:59 +00001521 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522}
1523
1524void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1525 const SkPaint* paint) {
1526 SkDEBUGCODE(bitmap.validate();)
1527
reed@google.com3d608122011-11-21 15:16:16 +00001528 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001529 SkRect bounds = {
1530 x, y,
1531 x + SkIntToScalar(bitmap.width()),
1532 y + SkIntToScalar(bitmap.height())
1533 };
1534 if (paint) {
1535 (void)paint->computeFastBounds(bounds, &bounds);
1536 }
1537 if (this->quickReject(bounds, paint2EdgeType(paint))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 return;
1539 }
1540 }
reed@google.com4b226022011-01-11 18:32:13 +00001541
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542 SkMatrix matrix;
1543 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001544 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545}
1546
reed@google.com9987ec32011-09-07 11:57:52 +00001547// this one is non-virtual, so it can be called safely by other canvas apis
1548void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1549 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1551 return;
1552 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001553
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001555 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001556 SkRect storage;
1557 const SkRect* bounds = &dst;
1558 if (paint) {
1559 bounds = &paint->computeFastBounds(dst, &storage);
1560 }
1561 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001562 return;
1563 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564 }
reed@google.com3d608122011-11-21 15:16:16 +00001565
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001567
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001569 SkRect tmpSrc;
1570 if (src) {
1571 tmpSrc.set(*src);
1572 // if the extract process clipped off the top or left of the
1573 // original, we adjust for that here to get the position right.
1574 if (tmpSrc.fLeft > 0) {
1575 tmpSrc.fRight -= tmpSrc.fLeft;
1576 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001577 }
reed@android.com87899992009-10-16 14:48:38 +00001578 if (tmpSrc.fTop > 0) {
1579 tmpSrc.fBottom -= tmpSrc.fTop;
1580 tmpSrc.fTop = 0;
1581 }
1582 } else {
1583 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1584 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 }
reed@android.com87899992009-10-16 14:48:38 +00001586 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001587
reed@android.comf2b98d62010-12-20 18:26:13 +00001588 // ensure that src is "valid" before we pass it to our internal routines
1589 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1590 SkIRect tmpISrc;
1591 if (src) {
1592 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001593 if (!tmpISrc.intersect(*src)) {
1594 return;
1595 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001596 src = &tmpISrc;
1597 }
1598 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001599}
1600
reed@google.com9987ec32011-09-07 11:57:52 +00001601void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1602 const SkRect& dst, const SkPaint* paint) {
1603 SkDEBUGCODE(bitmap.validate();)
1604 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1605}
1606
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1608 const SkPaint* paint) {
1609 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001610 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611}
1612
reed@android.comf2b98d62010-12-20 18:26:13 +00001613void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1614 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001616
reed@google.com4e2b3d32011-04-07 14:18:59 +00001617 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001618
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001620 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 }
reed@android.com9b039062009-02-11 15:09:58 +00001622
reed@google.com4e2b3d32011-04-07 14:18:59 +00001623 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624}
1625
reed@google.com9987ec32011-09-07 11:57:52 +00001626void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1627 const SkIRect& center, const SkRect& dst,
1628 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001629 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001630 SkRect storage;
1631 const SkRect* bounds = &dst;
1632 if (paint) {
1633 bounds = &paint->computeFastBounds(dst, &storage);
1634 }
1635 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001636 return;
1637 }
1638 }
1639
reed@google.com9987ec32011-09-07 11:57:52 +00001640 const int32_t w = bitmap.width();
1641 const int32_t h = bitmap.height();
1642
1643 SkIRect c = center;
1644 // pin center to the bounds of the bitmap
1645 c.fLeft = SkMax32(0, center.fLeft);
1646 c.fTop = SkMax32(0, center.fTop);
1647 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1648 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1649
1650 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1651 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1652 SkScalar dstX[4] = {
1653 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1654 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1655 };
1656 SkScalar dstY[4] = {
1657 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1658 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1659 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001660
reed@google.com9987ec32011-09-07 11:57:52 +00001661 if (dstX[1] > dstX[2]) {
1662 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1663 dstX[2] = dstX[1];
1664 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001665
reed@google.com9987ec32011-09-07 11:57:52 +00001666 if (dstY[1] > dstY[2]) {
1667 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1668 dstY[2] = dstY[1];
1669 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001670
reed@google.com9987ec32011-09-07 11:57:52 +00001671 SkIRect s;
1672 SkRect d;
1673 for (int y = 0; y < 3; y++) {
1674 s.fTop = srcY[y];
1675 s.fBottom = srcY[y+1];
1676 d.fTop = dstY[y];
1677 d.fBottom = dstY[y+1];
1678 for (int x = 0; x < 3; x++) {
1679 s.fLeft = srcX[x];
1680 s.fRight = srcX[x+1];
1681 d.fLeft = dstX[x];
1682 d.fRight = dstX[x+1];
1683 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1684 }
1685 }
1686}
1687
1688void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1689 const SkRect& dst, const SkPaint* paint) {
1690 SkDEBUGCODE(bitmap.validate();)
1691
1692 // Need a device entry-point, so gpu can use a mesh
1693 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1694}
1695
reed@google.comf67e4cf2011-03-15 20:56:58 +00001696class SkDeviceFilteredPaint {
1697public:
1698 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1699 SkDevice::TextFlags flags;
1700 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001701 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001702 newPaint->setFlags(flags.fFlags);
1703 newPaint->setHinting(flags.fHinting);
1704 fPaint = newPaint;
1705 } else {
1706 fPaint = &paint;
1707 }
1708 }
1709
reed@google.comf67e4cf2011-03-15 20:56:58 +00001710 const SkPaint& paint() const { return *fPaint; }
1711
1712private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001713 const SkPaint* fPaint;
1714 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001715};
1716
bungeman@google.com52c748b2011-08-22 21:30:43 +00001717void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1718 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001719 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001720 draw.fDevice->drawRect(draw, r, paint);
1721 } else {
1722 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001723 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001724 draw.fDevice->drawRect(draw, r, p);
1725 }
1726}
1727
1728void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1729 const char text[], size_t byteLength,
1730 SkScalar x, SkScalar y) {
1731 SkASSERT(byteLength == 0 || text != NULL);
1732
1733 // nothing to draw
1734 if (text == NULL || byteLength == 0 ||
1735 draw.fClip->isEmpty() ||
1736 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1737 return;
1738 }
1739
1740 SkScalar width = 0;
1741 SkPoint start;
1742
1743 start.set(0, 0); // to avoid warning
1744 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1745 SkPaint::kStrikeThruText_Flag)) {
1746 width = paint.measureText(text, byteLength);
1747
1748 SkScalar offsetX = 0;
1749 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1750 offsetX = SkScalarHalf(width);
1751 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1752 offsetX = width;
1753 }
1754 start.set(x - offsetX, y);
1755 }
1756
1757 if (0 == width) {
1758 return;
1759 }
1760
1761 uint32_t flags = paint.getFlags();
1762
1763 if (flags & (SkPaint::kUnderlineText_Flag |
1764 SkPaint::kStrikeThruText_Flag)) {
1765 SkScalar textSize = paint.getTextSize();
1766 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1767 SkRect r;
1768
1769 r.fLeft = start.fX;
1770 r.fRight = start.fX + width;
1771
1772 if (flags & SkPaint::kUnderlineText_Flag) {
1773 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1774 start.fY);
1775 r.fTop = offset;
1776 r.fBottom = offset + height;
1777 DrawRect(draw, paint, r, textSize);
1778 }
1779 if (flags & SkPaint::kStrikeThruText_Flag) {
1780 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1781 start.fY);
1782 r.fTop = offset;
1783 r.fBottom = offset + height;
1784 DrawRect(draw, paint, r, textSize);
1785 }
1786 }
1787}
1788
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789void SkCanvas::drawText(const void* text, size_t byteLength,
1790 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001791 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792
1793 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001794 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001795 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001796 DrawTextDecorations(iter, dfp.paint(),
1797 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001798 }
1799
reed@google.com4e2b3d32011-04-07 14:18:59 +00001800 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801}
1802
1803void SkCanvas::drawPosText(const void* text, size_t byteLength,
1804 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001805 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001806
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001808 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001810 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 }
reed@google.com4b226022011-01-11 18:32:13 +00001812
reed@google.com4e2b3d32011-04-07 14:18:59 +00001813 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814}
1815
1816void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1817 const SkScalar xpos[], SkScalar constY,
1818 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001819 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001820
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001822 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001824 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825 }
reed@google.com4b226022011-01-11 18:32:13 +00001826
reed@google.com4e2b3d32011-04-07 14:18:59 +00001827 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828}
1829
1830void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1831 const SkPath& path, const SkMatrix* matrix,
1832 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001833 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834
1835 while (iter.next()) {
1836 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001837 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838 }
1839
reed@google.com4e2b3d32011-04-07 14:18:59 +00001840 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841}
1842
djsollen@google.com56c69772011-11-08 19:00:26 +00001843#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001844void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1845 const SkPoint pos[], const SkPaint& paint,
1846 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001847 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001848
1849 while (iter.next()) {
1850 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001851 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001852 }
1853
reed@google.com4e2b3d32011-04-07 14:18:59 +00001854 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001855}
1856#endif
1857
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1859 const SkPoint verts[], const SkPoint texs[],
1860 const SkColor colors[], SkXfermode* xmode,
1861 const uint16_t indices[], int indexCount,
1862 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001863 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001864
reed@android.com8a1c16f2008-12-17 15:59:43 +00001865 while (iter.next()) {
1866 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001867 colors, xmode, indices, indexCount,
1868 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001869 }
reed@google.com4b226022011-01-11 18:32:13 +00001870
reed@google.com4e2b3d32011-04-07 14:18:59 +00001871 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001872}
1873
reed@android.comcb608442009-12-04 21:32:27 +00001874void SkCanvas::drawData(const void* data, size_t length) {
1875 // do nothing. Subclasses may do something with the data
1876}
1877
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878//////////////////////////////////////////////////////////////////////////////
1879// These methods are NOT virtual, and therefore must call back into virtual
1880// methods, rather than actually drawing themselves.
1881//////////////////////////////////////////////////////////////////////////////
1882
1883void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001884 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885 SkPaint paint;
1886
1887 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001888 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001889 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 }
1891 this->drawPaint(paint);
1892}
1893
reed@android.com845fdac2009-06-23 03:01:32 +00001894void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895 SkPaint paint;
1896
1897 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001898 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001899 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900 }
1901 this->drawPaint(paint);
1902}
1903
1904void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1905 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001906
reed@android.com8a1c16f2008-12-17 15:59:43 +00001907 pt.set(x, y);
1908 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1909}
1910
1911void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1912 SkPoint pt;
1913 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001914
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915 pt.set(x, y);
1916 paint.setColor(color);
1917 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1918}
1919
1920void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1921 const SkPaint& paint) {
1922 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001923
reed@android.com8a1c16f2008-12-17 15:59:43 +00001924 pts[0].set(x0, y0);
1925 pts[1].set(x1, y1);
1926 this->drawPoints(kLines_PointMode, 2, pts, paint);
1927}
1928
1929void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1930 SkScalar right, SkScalar bottom,
1931 const SkPaint& paint) {
1932 SkRect r;
1933
1934 r.set(left, top, right, bottom);
1935 this->drawRect(r, paint);
1936}
1937
1938void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1939 const SkPaint& paint) {
1940 if (radius < 0) {
1941 radius = 0;
1942 }
1943
1944 SkRect r;
1945 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001946
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947 if (paint.canComputeFastBounds()) {
1948 SkRect storage;
1949 if (this->quickReject(paint.computeFastBounds(r, &storage),
1950 paint2EdgeType(&paint))) {
1951 return;
1952 }
1953 }
reed@google.com4b226022011-01-11 18:32:13 +00001954
reed@android.com8a1c16f2008-12-17 15:59:43 +00001955 SkPath path;
1956 path.addOval(r);
1957 this->drawPath(path, paint);
1958}
1959
1960void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1961 const SkPaint& paint) {
1962 if (rx > 0 && ry > 0) {
1963 if (paint.canComputeFastBounds()) {
1964 SkRect storage;
1965 if (this->quickReject(paint.computeFastBounds(r, &storage),
1966 paint2EdgeType(&paint))) {
1967 return;
1968 }
1969 }
1970
1971 SkPath path;
1972 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1973 this->drawPath(path, paint);
1974 } else {
1975 this->drawRect(r, paint);
1976 }
1977}
1978
1979void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1980 if (paint.canComputeFastBounds()) {
1981 SkRect storage;
1982 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1983 paint2EdgeType(&paint))) {
1984 return;
1985 }
1986 }
1987
1988 SkPath path;
1989 path.addOval(oval);
1990 this->drawPath(path, paint);
1991}
1992
1993void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1994 SkScalar sweepAngle, bool useCenter,
1995 const SkPaint& paint) {
1996 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1997 this->drawOval(oval, paint);
1998 } else {
1999 SkPath path;
2000 if (useCenter) {
2001 path.moveTo(oval.centerX(), oval.centerY());
2002 }
2003 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2004 if (useCenter) {
2005 path.close();
2006 }
2007 this->drawPath(path, paint);
2008 }
2009}
2010
2011void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2012 const SkPath& path, SkScalar hOffset,
2013 SkScalar vOffset, const SkPaint& paint) {
2014 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002015
reed@android.com8a1c16f2008-12-17 15:59:43 +00002016 matrix.setTranslate(hOffset, vOffset);
2017 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2018}
2019
reed@android.comf76bacf2009-05-13 14:00:33 +00002020///////////////////////////////////////////////////////////////////////////////
2021
reed@android.com8a1c16f2008-12-17 15:59:43 +00002022void SkCanvas::drawPicture(SkPicture& picture) {
2023 int saveCount = save();
2024 picture.draw(this);
2025 restoreToCount(saveCount);
2026}
2027
reed@android.com8a1c16f2008-12-17 15:59:43 +00002028///////////////////////////////////////////////////////////////////////////////
2029///////////////////////////////////////////////////////////////////////////////
2030
2031SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002032 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033
2034 SkASSERT(canvas);
2035
2036 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2037 fDone = !fImpl->next();
2038}
2039
2040SkCanvas::LayerIter::~LayerIter() {
2041 fImpl->~SkDrawIter();
2042}
2043
2044void SkCanvas::LayerIter::next() {
2045 fDone = !fImpl->next();
2046}
2047
2048SkDevice* SkCanvas::LayerIter::device() const {
2049 return fImpl->getDevice();
2050}
2051
2052const SkMatrix& SkCanvas::LayerIter::matrix() const {
2053 return fImpl->getMatrix();
2054}
2055
2056const SkPaint& SkCanvas::LayerIter::paint() const {
2057 const SkPaint* paint = fImpl->getPaint();
2058 if (NULL == paint) {
2059 paint = &fDefaultPaint;
2060 }
2061 return *paint;
2062}
2063
2064const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2065int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2066int SkCanvas::LayerIter::y() const { return fImpl->getY(); }