blob: aa6fe72c58458c1643ca79ecc61d3be8d34b194e [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"
17#include "SkScalarCompare.h"
18#include "SkTemplates.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000019#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021
22//#define SK_TRACE_SAVERESTORE
23
24#ifdef SK_TRACE_SAVERESTORE
25 static int gLayerCounter;
26 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
27 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
28
29 static int gRecCounter;
30 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
31 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
32
33 static int gCanvasCounter;
34 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
35 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
36#else
37 #define inc_layer()
38 #define dec_layer()
39 #define inc_rec()
40 #define dec_rec()
41 #define inc_canvas()
42 #define dec_canvas()
43#endif
44
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000045typedef SkTLazy<SkPaint> SkLazyPaint;
46
reed@android.com8a1c16f2008-12-17 15:59:43 +000047///////////////////////////////////////////////////////////////////////////////
48// Helpers for computing fast bounds for quickReject tests
49
50static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
51 return paint != NULL && paint->isAntiAlias() ?
52 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
53}
54
55///////////////////////////////////////////////////////////////////////////////
56
57/* This is the record we keep for each SkDevice that the user installs.
58 The clip/matrix/proc are fields that reflect the top of the save/restore
59 stack. Whenever the canvas changes, it marks a dirty flag, and then before
60 these are used (assuming we're not on a layer) we rebuild these cache
61 values: they reflect the top of the save stack, but translated and clipped
62 by the device's XY offset and bitmap-bounds.
63*/
64struct DeviceCM {
65 DeviceCM* fNext;
66 SkDevice* fDevice;
67 SkRegion fClip;
68 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000069 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000070 // optional, related to canvas' external matrix
71 const SkMatrix* fMVMatrix;
72 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000073
bungeman@google.com88edf1e2011-08-08 19:41:56 +000074 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 : fNext(NULL) {
76 if (NULL != device) {
77 device->ref();
78 device->lockPixels();
79 }
reed@google.com4b226022011-01-11 18:32:13 +000080 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000082 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000083
bungeman@google.com88edf1e2011-08-08 19:41:56 +000084 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 if (NULL != fDevice) {
86 fDevice->unlockPixels();
87 fDevice->unref();
88 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000089 SkDELETE(fPaint);
90 }
reed@google.com4b226022011-01-11 18:32:13 +000091
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +000093 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +000094 int x = fDevice->getOrigin().x();
95 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 int width = fDevice->width();
97 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +000098
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 if ((x | y) == 0) {
100 fMatrix = &totalMatrix;
101 fClip = totalClip;
102 } else {
103 fMatrixStorage = totalMatrix;
104 fMatrixStorage.postTranslate(SkIntToScalar(-x),
105 SkIntToScalar(-y));
106 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 totalClip.translate(-x, -y, &fClip);
109 }
110
111 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
112
113 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 if (updateClip) {
116 updateClip->op(x, y, x + width, y + height,
117 SkRegion::kDifference_Op);
118 }
reed@google.com4b226022011-01-11 18:32:13 +0000119
reed@google.com46799cd2011-02-22 20:56:26 +0000120 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121
122#ifdef SK_DEBUG
123 if (!fClip.isEmpty()) {
124 SkIRect deviceR;
125 deviceR.set(0, 0, width, height);
126 SkASSERT(deviceR.contains(fClip.getBounds()));
127 }
128#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000129 // default is to assume no external matrix
130 fMVMatrix = NULL;
131 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000133
134 // can only be called after calling updateMC()
135 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
136 fMVMatrixStorage.setConcat(extI, *fMatrix);
137 fMVMatrix = &fMVMatrixStorage;
138 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
139 }
140
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000142 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143};
144
145/* This is the record we keep for each save/restore level in the stack.
146 Since a level optionally copies the matrix and/or stack, we have pointers
147 for these fields. If the value is copied for this level, the copy is
148 stored in the ...Storage field, and the pointer points to that. If the
149 value is not copied for this level, we ignore ...Storage, and just point
150 at the corresponding value in the previous level in the stack.
151*/
152class SkCanvas::MCRec {
153public:
154 MCRec* fNext;
155 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
156 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
157 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000158
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159 DeviceCM* fLayer;
160 /* If there are any layers in the stack, this points to the top-most
161 one that is at or below this level in the stack (so we know what
162 bitmap/device to draw into from this level. This value is NOT
163 reference counted, since the real owner is either our fLayer field,
164 or a previous one in a lower level.)
165 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000166 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167
168 MCRec(const MCRec* prev, int flags) {
169 if (NULL != prev) {
170 if (flags & SkCanvas::kMatrix_SaveFlag) {
171 fMatrixStorage = *prev->fMatrix;
172 fMatrix = &fMatrixStorage;
173 } else {
174 fMatrix = prev->fMatrix;
175 }
reed@google.com4b226022011-01-11 18:32:13 +0000176
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 if (flags & SkCanvas::kClip_SaveFlag) {
178 fRegionStorage = *prev->fRegion;
179 fRegion = &fRegionStorage;
180 } else {
181 fRegion = prev->fRegion;
182 }
183
184 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000185 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186
187 fTopLayer = prev->fTopLayer;
188 } else { // no prev
189 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000190
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 fMatrix = &fMatrixStorage;
192 fRegion = &fRegionStorage;
193 fFilter = NULL;
194 fTopLayer = NULL;
195 }
196 fLayer = NULL;
197
198 // don't bother initializing fNext
199 inc_rec();
200 }
201 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000202 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 SkDELETE(fLayer);
204 dec_rec();
205 }
reed@google.com4b226022011-01-11 18:32:13 +0000206
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207private:
208 SkMatrix fMatrixStorage;
209 SkRegion fRegionStorage;
210};
211
212class SkDrawIter : public SkDraw {
213public:
214 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
215 fCanvas = canvas;
216 canvas->updateDeviceCMCache();
217
reed@google.com7d7ca792011-02-23 22:39:18 +0000218 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 fBounder = canvas->getBounder();
220 fCurrLayer = canvas->fMCRec->fTopLayer;
221 fSkipEmptyClips = skipEmptyClips;
222 }
reed@google.com4b226022011-01-11 18:32:13 +0000223
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 bool next() {
225 // skip over recs with empty clips
226 if (fSkipEmptyClips) {
227 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
228 fCurrLayer = fCurrLayer->fNext;
229 }
230 }
231
232 if (NULL != fCurrLayer) {
233 const DeviceCM* rec = fCurrLayer;
234
235 fMatrix = rec->fMatrix;
236 fClip = &rec->fClip;
237 fDevice = rec->fDevice;
238 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000240 fMVMatrix = rec->fMVMatrix;
241 fExtMatrix = rec->fExtMatrix;
242 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243
244 fCurrLayer = rec->fNext;
245 if (fBounder) {
246 fBounder->setClip(fClip);
247 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000249
bsalomon@google.comd302f142011-03-03 13:54:13 +0000250 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 return true;
252 }
253 return false;
254 }
reed@google.com4b226022011-01-11 18:32:13 +0000255
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000257 int getX() const { return fDevice->getOrigin().x(); }
258 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 const SkMatrix& getMatrix() const { return *fMatrix; }
260 const SkRegion& getClip() const { return *fClip; }
261 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000262
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263private:
264 SkCanvas* fCanvas;
265 const DeviceCM* fCurrLayer;
266 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 SkBool8 fSkipEmptyClips;
268
269 typedef SkDraw INHERITED;
270};
271
272/////////////////////////////////////////////////////////////////////////////
273
274class AutoDrawLooper {
275public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000276 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
277 fCanvas = canvas;
278 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000280 fPaint = NULL;
281 fSaveCount = canvas->getSaveCount();
282 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283
reed@google.com4e2b3d32011-04-07 14:18:59 +0000284 if (fLooper) {
285 fLooper->init(canvas);
286 }
287 }
288
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000290 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000292
293 const SkPaint& paint() const {
294 SkASSERT(fPaint);
295 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000297
298 bool next(SkDrawFilter::Type drawType);
299
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000301 SkLazyPaint fLazyPaint;
302 SkCanvas* fCanvas;
303 const SkPaint& fOrigPaint;
304 SkDrawLooper* fLooper;
305 SkDrawFilter* fFilter;
306 const SkPaint* fPaint;
307 int fSaveCount;
308 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309};
310
reed@google.com4e2b3d32011-04-07 14:18:59 +0000311bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
312 if (fDone) {
313 fPaint = NULL;
314 return false;
315 }
316 if (!fLooper && !fFilter) {
317 fDone = true;
318 fPaint = &fOrigPaint;
319 return true;
320 }
321
322 SkPaint* paint = fLazyPaint.set(fOrigPaint);
323 if (fLooper && !fLooper->next(fCanvas, paint)) {
324 fDone = true;
325 fPaint = NULL;
326 return false;
327 }
328 if (fFilter) {
329 fFilter->filter(paint, drawType);
mike@reedtribe.org53e3bed2011-04-08 00:37:03 +0000330 if (NULL == fLooper) {
331 // no looper means we only draw once
332 fDone = true;
333 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000334 }
335 fPaint = paint;
336 return true;
337}
338
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339/* Stack helper for managing a SkBounder. In the destructor, if we were
340 given a bounder, we call its commit() method, signifying that we are
341 done accumulating bounds for that draw.
342*/
343class SkAutoBounderCommit {
344public:
345 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
346 ~SkAutoBounderCommit() {
347 if (NULL != fBounder) {
348 fBounder->commit();
349 }
350 }
351private:
352 SkBounder* fBounder;
353};
354
355#include "SkColorPriv.h"
356
357class AutoValidator {
358public:
359 AutoValidator(SkDevice* device) : fDevice(device) {}
360 ~AutoValidator() {
361#ifdef SK_DEBUG
362 const SkBitmap& bm = fDevice->accessBitmap(false);
363 if (bm.config() == SkBitmap::kARGB_4444_Config) {
364 for (int y = 0; y < bm.height(); y++) {
365 const SkPMColor16* p = bm.getAddr16(0, y);
366 for (int x = 0; x < bm.width(); x++) {
367 SkPMColor16 c = p[x];
368 SkPMColor16Assert(c);
369 }
370 }
371 }
372#endif
373 }
374private:
375 SkDevice* fDevice;
376};
377
378////////// macros to place around the internal draw calls //////////////////
379
reed@google.com4e2b3d32011-04-07 14:18:59 +0000380#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000382 AutoDrawLooper looper(this, paint); \
383 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384 SkAutoBounderCommit ac(fBounder); \
385 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000386
reed@google.com4e2b3d32011-04-07 14:18:59 +0000387#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388
389////////////////////////////////////////////////////////////////////////////
390
391SkDevice* SkCanvas::init(SkDevice* device) {
392 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000393 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000395 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000396 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000397 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000398 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399
400 fMCRec = (MCRec*)fMCStack.push_back();
401 new (fMCRec) MCRec(NULL, 0);
402
403 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
404 fMCRec->fTopLayer = fMCRec->fLayer;
405 fMCRec->fNext = NULL;
406
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000407 fExternalMatrix.reset();
408 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000409 fUseExternalMatrix = false;
410
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411 return this->setDevice(device);
412}
413
reed@google.comcde92112011-07-06 20:00:52 +0000414SkCanvas::SkCanvas()
415: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000416 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000417
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000418 this->init(NULL);
419}
420
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000422 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 inc_canvas();
424
425 this->init(device);
426}
427
428SkCanvas::SkCanvas(const SkBitmap& bitmap)
429 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
430 inc_canvas();
431
reed@google.comcde92112011-07-06 20:00:52 +0000432 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433}
434
435SkCanvas::~SkCanvas() {
436 // free up the contents of our deque
437 this->restoreToCount(1); // restore everything but the last
438 this->internalRestore(); // restore the last, since we're going away
439
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000440 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000441
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442 dec_canvas();
443}
444
445SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
446 SkRefCnt_SafeAssign(fBounder, bounder);
447 return bounder;
448}
449
450SkDrawFilter* SkCanvas::getDrawFilter() const {
451 return fMCRec->fFilter;
452}
453
454SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
455 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
456 return filter;
457}
458
459///////////////////////////////////////////////////////////////////////////////
460
461SkDevice* SkCanvas::getDevice() const {
462 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000463 SkDeque::F2BIter iter(fMCStack);
464 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 SkASSERT(rec && rec->fLayer);
466 return rec->fLayer->fDevice;
467}
468
reed@google.com9266fed2011-03-30 00:18:03 +0000469SkDevice* SkCanvas::getTopDevice() const {
470 return fMCRec->fTopLayer->fDevice;
471}
472
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473SkDevice* SkCanvas::setDevice(SkDevice* device) {
474 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000475 SkDeque::F2BIter iter(fMCStack);
476 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477 SkASSERT(rec && rec->fLayer);
478 SkDevice* rootDevice = rec->fLayer->fDevice;
479
480 if (rootDevice == device) {
481 return device;
482 }
reed@google.com4b226022011-01-11 18:32:13 +0000483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 /* Notify the devices that they are going in/out of scope, so they can do
485 things like lock/unlock their pixels, etc.
486 */
487 if (device) {
488 device->lockPixels();
489 }
490 if (rootDevice) {
491 rootDevice->unlockPixels();
492 }
493
494 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
495 rootDevice = device;
496
497 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000498
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 /* Now we update our initial region to have the bounds of the new device,
500 and then intersect all of the clips in our stack with these bounds,
501 to ensure that we can't draw outside of the device's bounds (and trash
502 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 NOTE: this is only a partial-fix, since if the new device is larger than
505 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000506 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
508 reconstruct the correct clips, so this approximation will have to do.
509 The caller really needs to restore() back to the base if they want to
510 accurately take advantage of the new device bounds.
511 */
512
513 if (NULL == device) {
514 rec->fRegion->setEmpty();
515 while ((rec = (MCRec*)iter.next()) != NULL) {
516 (void)rec->fRegion->setEmpty();
517 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000518 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 } else {
520 // compute our total bounds for all devices
521 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000522
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 bounds.set(0, 0, device->width(), device->height());
524
525 // now jam our 1st clip to be bounds, and intersect the rest with that
526 rec->fRegion->setRect(bounds);
527 while ((rec = (MCRec*)iter.next()) != NULL) {
528 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
529 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000530 fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 }
532 return device;
533}
534
reed@google.comaf951c92011-06-16 19:10:39 +0000535SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
536 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 device->unref();
538 return device;
539}
540
reed@google.com51df9e32010-12-23 19:29:18 +0000541bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
542 SkDevice* device = this->getDevice();
543 if (!device) {
544 return false;
545 }
546 return device->readPixels(srcRect, bitmap);
547}
548
reed@google.com4b226022011-01-11 18:32:13 +0000549//////////////////////////////////////////////////////////////////////////////
550
reed@google.com51df9e32010-12-23 19:29:18 +0000551bool SkCanvas::readPixels(SkBitmap* bitmap) {
552 SkDevice* device = this->getDevice();
553 if (!device) {
554 return false;
555 }
556 SkIRect bounds;
557 bounds.set(0, 0, device->width(), device->height());
558 return this->readPixels(bounds, bitmap);
559}
560
561void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
562 SkDevice* device = this->getDevice();
563 if (device) {
564 device->writePixels(bitmap, x, y);
565 }
566}
567
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568//////////////////////////////////////////////////////////////////////////////
569
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570void SkCanvas::updateDeviceCMCache() {
571 if (fDeviceCMDirty) {
572 const SkMatrix& totalMatrix = this->getTotalMatrix();
573 const SkRegion& totalClip = this->getTotalClip();
574 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000577 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000578 if (fUseExternalMatrix) {
579 layer->updateExternalMatrix(fExternalMatrix,
580 fExternalInverse);
581 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 } else {
583 SkRegion clip;
584 clip = totalClip; // make a copy
585 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000586 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000587 if (fUseExternalMatrix) {
588 layer->updateExternalMatrix(fExternalMatrix,
589 fExternalInverse);
590 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 } while ((layer = layer->fNext) != NULL);
592 }
593 fDeviceCMDirty = false;
594 }
595}
596
reed@android.comf2b98d62010-12-20 18:26:13 +0000597void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000598 const SkRegion& clip,
599 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000601 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000602 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000603 fLastDeviceToGainFocus = device;
604 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605}
606
607///////////////////////////////////////////////////////////////////////////////
608
609int SkCanvas::internalSave(SaveFlags flags) {
610 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 MCRec* newTop = (MCRec*)fMCStack.push_back();
613 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000614
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 newTop->fNext = fMCRec;
616 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000617
reed@google.com5c3d1472011-02-22 19:12:23 +0000618 fClipStack.save();
619 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
620
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 return saveCount;
622}
623
624int SkCanvas::save(SaveFlags flags) {
625 // call shared impl
626 return this->internalSave(flags);
627}
628
629#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
630#define C16MASK (1 << SkBitmap::kRGB_565_Config)
631#define C8MASK (1 << SkBitmap::kA8_Config)
632
633static SkBitmap::Config resolve_config(SkCanvas* canvas,
634 const SkIRect& bounds,
635 SkCanvas::SaveFlags flags,
636 bool* isOpaque) {
637 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
638
639#if 0
640 // loop through and union all the configs we may draw into
641 uint32_t configMask = 0;
642 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
643 {
644 SkDevice* device = canvas->getLayerDevice(i);
645 if (device->intersects(bounds))
646 configMask |= 1 << device->config();
647 }
648
649 // if the caller wants alpha or fullcolor, we can't return 565
650 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
651 SkCanvas::kHasAlphaLayer_SaveFlag))
652 configMask &= ~C16MASK;
653
654 switch (configMask) {
655 case C8MASK: // if we only have A8, return that
656 return SkBitmap::kA8_Config;
657
658 case C16MASK: // if we only have 565, return that
659 return SkBitmap::kRGB_565_Config;
660
661 default:
662 return SkBitmap::kARGB_8888_Config; // default answer
663 }
664#else
665 return SkBitmap::kARGB_8888_Config; // default answer
666#endif
667}
668
669static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
670 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
671}
672
673int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
674 SaveFlags flags) {
675 // do this before we create the layer. We don't call the public save() since
676 // that would invoke a possibly overridden virtual
677 int count = this->internalSave(flags);
678
679 fDeviceCMDirty = true;
680
681 SkIRect ir;
682 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000683 if (clipBounds.isEmpty()) {
684 return count;
685 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686
687 if (NULL != bounds) {
688 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000689
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 this->getTotalMatrix().mapRect(&r, *bounds);
691 r.roundOut(&ir);
692 // early exit if the layer's bounds are clipped out
693 if (!ir.intersect(clipBounds)) {
694 if (bounds_affects_clip(flags))
695 fMCRec->fRegion->setEmpty();
696 return count;
697 }
698 } else { // no user bounds, so just use the clip
699 ir = clipBounds;
700 }
701
reed@google.com5c3d1472011-02-22 19:12:23 +0000702 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 // early exit if the clip is now empty
704 if (bounds_affects_clip(flags) &&
705 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
706 return count;
707 }
708
709 bool isOpaque;
710 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
711
bsalomon@google.come97f0852011-06-17 13:10:25 +0000712 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
713 isOpaque);
bungeman@google.come25c6842011-08-17 14:53:54 +0000714 if (NULL == device) {
715 SkDebugf("Unable to create device for layer.");
716 return count;
717 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000718
reed@google.com6f8f2922011-03-04 22:27:10 +0000719 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
721 device->unref();
722
723 layer->fNext = fMCRec->fTopLayer;
724 fMCRec->fLayer = layer;
725 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
726
727 return count;
728}
729
730int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
731 SaveFlags flags) {
732 if (0xFF == alpha) {
733 return this->saveLayer(bounds, NULL, flags);
734 } else {
735 SkPaint tmpPaint;
736 tmpPaint.setAlpha(alpha);
737 return this->saveLayer(bounds, &tmpPaint, flags);
738 }
739}
740
741void SkCanvas::restore() {
742 // check for underflow
743 if (fMCStack.count() > 1) {
744 this->internalRestore();
745 }
746}
747
748void SkCanvas::internalRestore() {
749 SkASSERT(fMCStack.count() != 0);
750
751 fDeviceCMDirty = true;
752 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000753 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754
reed@google.com5c3d1472011-02-22 19:12:23 +0000755 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000756 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 DeviceCM* layer = fMCRec->fLayer; // may be null
758 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
759 fMCRec->fLayer = NULL;
760
761 // now do the normal restore()
762 fMCRec->~MCRec(); // balanced in save()
763 fMCStack.pop_back();
764 fMCRec = (MCRec*)fMCStack.back();
765
766 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
767 since if we're being recorded, we don't want to record this (the
768 recorder will have already recorded the restore).
769 */
770 if (NULL != layer) {
771 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000772 const SkIPoint& origin = layer->fDevice->getOrigin();
773 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 layer->fPaint);
775 // reset this, since drawDevice will have set it to true
776 fDeviceCMDirty = true;
777 }
778 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000779 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000780
781 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782}
783
784int SkCanvas::getSaveCount() const {
785 return fMCStack.count();
786}
787
788void SkCanvas::restoreToCount(int count) {
789 // sanity check
790 if (count < 1) {
791 count = 1;
792 }
793 while (fMCStack.count() > count) {
794 this->restore();
795 }
796}
797
798/////////////////////////////////////////////////////////////////////////////
799
800// can't draw it if its empty, or its too big for a fixed-point width or height
801static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000802 return bitmap.width() <= 0 || bitmap.height() <= 0
803#ifndef SK_ALLOW_OVER_32K_BITMAPS
804 || bitmap.width() > 32767 || bitmap.height() > 32767
805#endif
806 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807}
808
reed@android.comf2b98d62010-12-20 18:26:13 +0000809void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 const SkMatrix& matrix, const SkPaint* paint) {
811 if (reject_bitmap(bitmap)) {
812 return;
813 }
814
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000815 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000817 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000819 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820}
821
822void SkCanvas::drawDevice(SkDevice* device, int x, int y,
823 const SkPaint* paint) {
824 SkPaint tmp;
825 if (NULL == paint) {
826 tmp.setDither(true);
827 paint = &tmp;
828 }
reed@google.com4b226022011-01-11 18:32:13 +0000829
reed@google.com4e2b3d32011-04-07 14:18:59 +0000830 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 while (iter.next()) {
832 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000833 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000835 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836}
837
838/////////////////////////////////////////////////////////////////////////////
839
840bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
841 fDeviceCMDirty = true;
842 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000843 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 return fMCRec->fMatrix->preTranslate(dx, dy);
845}
846
847bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
848 fDeviceCMDirty = true;
849 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000850 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 return fMCRec->fMatrix->preScale(sx, sy);
852}
853
854bool SkCanvas::rotate(SkScalar degrees) {
855 fDeviceCMDirty = true;
856 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000857 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 return fMCRec->fMatrix->preRotate(degrees);
859}
860
861bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
862 fDeviceCMDirty = true;
863 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000864 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 return fMCRec->fMatrix->preSkew(sx, sy);
866}
867
868bool SkCanvas::concat(const SkMatrix& matrix) {
869 fDeviceCMDirty = true;
870 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000871 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 return fMCRec->fMatrix->preConcat(matrix);
873}
874
875void SkCanvas::setMatrix(const SkMatrix& matrix) {
876 fDeviceCMDirty = true;
877 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000878 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 *fMCRec->fMatrix = matrix;
880}
881
882// this is not virtual, so it must call a virtual method so that subclasses
883// will see its action
884void SkCanvas::resetMatrix() {
885 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000886
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 matrix.reset();
888 this->setMatrix(matrix);
889}
890
891//////////////////////////////////////////////////////////////////////////////
892
893bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000894 AutoValidateClip avc(this);
895
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 fDeviceCMDirty = true;
897 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000898 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899
900 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000901 // for these simpler matrices, we can stay a rect ever after applying
902 // the matrix. This means we don't have to a) make a path, and b) tell
903 // the region code to scan-convert the path, only to discover that it
904 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 SkRect r;
906 SkIRect ir;
907
908 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000909 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 r.round(&ir);
911 return fMCRec->fRegion->op(ir, op);
912 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000913 // since we're rotate or some such thing, we convert the rect to a path
914 // and clip against that, since it can handle any matrix. However, to
915 // avoid recursion in the case where we are subclassed (e.g. Pictures)
916 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 SkPath path;
918
919 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000920 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 }
922}
923
reed@google.com819c9212011-02-23 18:56:55 +0000924static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
925 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000926 // base is used to limit the size (and therefore memory allocation) of the
927 // region that results from scan converting devPath.
928 SkRegion base;
929
reed@google.com819c9212011-02-23 18:56:55 +0000930 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000931 // since we are intersect, we can do better (tighter) with currRgn's
932 // bounds, than just using the device. However, if currRgn is complex,
933 // our region blitter may hork, so we do that case in two steps.
934 if (currRgn->isRect()) {
935 return currRgn->setPath(devPath, *currRgn);
936 } else {
937 base.setRect(currRgn->getBounds());
938 SkRegion rgn;
939 rgn.setPath(devPath, base);
940 return currRgn->op(rgn, op);
941 }
reed@google.com819c9212011-02-23 18:56:55 +0000942 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000943 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
944 base.setRect(0, 0, bm.width(), bm.height());
945
946 if (SkRegion::kReplace_Op == op) {
947 return currRgn->setPath(devPath, base);
948 } else {
949 SkRegion rgn;
950 rgn.setPath(devPath, base);
951 return currRgn->op(rgn, op);
952 }
953 }
954}
955
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000957 AutoValidateClip avc(this);
958
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 fDeviceCMDirty = true;
960 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000961 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962
963 SkPath devPath;
964 path.transform(*fMCRec->fMatrix, &devPath);
965
reed@google.com5c3d1472011-02-22 19:12:23 +0000966 // if we called path.swap() we could avoid a deep copy of this path
967 fClipStack.clipDevPath(devPath, op);
968
reed@google.com819c9212011-02-23 18:56:55 +0000969 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970}
971
972bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000973 AutoValidateClip avc(this);
974
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 fDeviceCMDirty = true;
976 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000977 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978
reed@google.com5c3d1472011-02-22 19:12:23 +0000979 // todo: signal fClipStack that we have a region, and therefore (I guess)
980 // we have to ignore it, and use the region directly?
981 fClipStack.clipDevRect(rgn.getBounds());
982
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 return fMCRec->fRegion->op(rgn, op);
984}
985
reed@google.com819c9212011-02-23 18:56:55 +0000986#ifdef SK_DEBUG
987void SkCanvas::validateClip() const {
988 // construct clipRgn from the clipstack
989 const SkDevice* device = this->getDevice();
990 SkIRect ir;
991 ir.set(0, 0, device->width(), device->height());
992 SkRegion clipRgn(ir);
993
994 SkClipStack::B2FIter iter(fClipStack);
995 const SkClipStack::B2FIter::Clip* clip;
996 while ((clip = iter.next()) != NULL) {
997 if (clip->fPath) {
998 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
999 } else if (clip->fRect) {
1000 clip->fRect->round(&ir);
1001 clipRgn.op(ir, clip->fOp);
1002 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001003 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001004 }
1005 }
1006
reed@google.com6f8f2922011-03-04 22:27:10 +00001007#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001008 // now compare against the current rgn
1009 const SkRegion& rgn = this->getTotalClip();
1010 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001011#endif
reed@google.com819c9212011-02-23 18:56:55 +00001012}
1013#endif
1014
reed@google.com5c3d1472011-02-22 19:12:23 +00001015///////////////////////////////////////////////////////////////////////////////
1016
reed@android.comba09de42010-02-05 20:46:05 +00001017void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001019 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1020 fLocalBoundsCompareTypeBW;
1021
1022 if (!this->getClipBounds(&r, et)) {
1023 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001025 rCompare.set(SkScalarToCompareType(r.fLeft),
1026 SkScalarToCompareType(r.fTop),
1027 SkScalarToCompareType(r.fRight),
1028 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 }
1030}
1031
reed@android.comd252db02009-04-01 18:31:44 +00001032/* current impl ignores edgetype, and relies on
1033 getLocalClipBoundsCompareType(), which always returns a value assuming
1034 antialiasing (worst case)
1035 */
reed@android.comba09de42010-02-05 20:46:05 +00001036bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001037
1038 if (!rect.hasValidCoordinates())
1039 return true;
1040
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 if (fMCRec->fRegion->isEmpty()) {
1042 return true;
1043 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044
tomhudson@google.com8d430182011-06-06 19:11:19 +00001045 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001046 SkRect dst;
1047 fMCRec->fMatrix->mapRect(&dst, rect);
1048 SkIRect idst;
1049 dst.roundOut(&idst);
1050 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1051 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001052 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001053
reed@android.coma380ae42009-07-21 01:17:02 +00001054 // for speed, do the most likely reject compares first
1055 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1056 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1057 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1058 return true;
1059 }
1060 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1061 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1062 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1063 return true;
1064 }
1065 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067}
1068
1069bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001070 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071}
1072
1073bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1074 /* current impl ignores edgetype, and relies on
1075 getLocalClipBoundsCompareType(), which always returns a value assuming
1076 antialiasing (worst case)
1077 */
1078
1079 if (fMCRec->fRegion->isEmpty()) {
1080 return true;
1081 }
reed@google.com4b226022011-01-11 18:32:13 +00001082
reed@android.comaefd2bc2009-03-30 21:02:14 +00001083 SkScalarCompareType userT = SkScalarToCompareType(top);
1084 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001085
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001087 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 if (userT >= userB) {
1089 return true;
1090 }
reed@google.com4b226022011-01-11 18:32:13 +00001091
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 // check if we are above or below the local clip bounds
1093 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1094 return userT >= clipR.fBottom || userB <= clipR.fTop;
1095}
1096
1097bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1098 const SkRegion& clip = *fMCRec->fRegion;
1099 if (clip.isEmpty()) {
1100 if (bounds) {
1101 bounds->setEmpty();
1102 }
1103 return false;
1104 }
1105
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001106 SkMatrix inverse;
1107 // if we can't invert the CTM, we can't return local clip bounds
1108 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001109 if (bounds) {
1110 bounds->setEmpty();
1111 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001112 return false;
1113 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001115 if (NULL != bounds) {
1116 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 // get the clip's bounds
1118 const SkIRect& ibounds = clip.getBounds();
1119 // adjust it outwards if we are antialiasing
1120 int inset = (kAA_EdgeType == et);
1121 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1122 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001123
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 // invert into local coordinates
1125 inverse.mapRect(bounds, r);
1126 }
1127 return true;
1128}
1129
1130const SkMatrix& SkCanvas::getTotalMatrix() const {
1131 return *fMCRec->fMatrix;
1132}
1133
1134const SkRegion& SkCanvas::getTotalClip() const {
1135 return *fMCRec->fRegion;
1136}
1137
reed@google.com7d7ca792011-02-23 22:39:18 +00001138const SkClipStack& SkCanvas::getTotalClipStack() const {
1139 return fClipStack;
1140}
1141
reed@android.comf2b98d62010-12-20 18:26:13 +00001142void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1143 if (NULL == matrix || matrix->isIdentity()) {
1144 if (fUseExternalMatrix) {
1145 fDeviceCMDirty = true;
1146 }
1147 fUseExternalMatrix = false;
1148 } else {
1149 fUseExternalMatrix = true;
1150 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001151
reed@android.comf2b98d62010-12-20 18:26:13 +00001152 fExternalMatrix = *matrix;
1153 matrix->invert(&fExternalInverse);
1154 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001155}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156
bsalomon@google.come97f0852011-06-17 13:10:25 +00001157SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1158 int width, int height,
1159 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001160 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001161 if (device) {
1162 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1163 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001164 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001165 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001166 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167}
1168
bsalomon@google.come97f0852011-06-17 13:10:25 +00001169SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1170 int width, int height,
1171 bool isOpaque) {
1172 SkDevice* device = this->getDevice();
1173 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001174 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001175 } else {
1176 return NULL;
1177 }
1178}
1179
1180
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181//////////////////////////////////////////////////////////////////////////////
1182// These are the virtual drawing methods
1183//////////////////////////////////////////////////////////////////////////////
1184
reed@google.com2a981812011-04-14 18:59:28 +00001185void SkCanvas::clear(SkColor color) {
1186 SkDrawIter iter(this);
1187
1188 while (iter.next()) {
1189 iter.fDevice->clear(color);
1190 }
1191}
1192
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193void SkCanvas::drawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001194 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195
1196 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001197 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 }
1199
reed@google.com4e2b3d32011-04-07 14:18:59 +00001200 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201}
1202
1203void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1204 const SkPaint& paint) {
1205 if ((long)count <= 0) {
1206 return;
1207 }
1208
1209 SkASSERT(pts != NULL);
1210
reed@google.com4e2b3d32011-04-07 14:18:59 +00001211 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001212
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001214 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 }
reed@google.com4b226022011-01-11 18:32:13 +00001216
reed@google.com4e2b3d32011-04-07 14:18:59 +00001217 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218}
1219
1220void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1221 if (paint.canComputeFastBounds()) {
1222 SkRect storage;
1223 if (this->quickReject(paint.computeFastBounds(r, &storage),
1224 paint2EdgeType(&paint))) {
1225 return;
1226 }
1227 }
reed@google.com4b226022011-01-11 18:32:13 +00001228
reed@google.com4e2b3d32011-04-07 14:18:59 +00001229 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230
1231 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001232 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233 }
1234
reed@google.com4e2b3d32011-04-07 14:18:59 +00001235 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236}
1237
1238void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1239 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001240 SkRect storage;
1241 const SkRect& bounds = path.getBounds();
1242 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 paint2EdgeType(&paint))) {
1244 return;
1245 }
1246 }
1247
reed@google.com4e2b3d32011-04-07 14:18:59 +00001248 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249
1250 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001251 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 }
1253
reed@google.com4e2b3d32011-04-07 14:18:59 +00001254 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255}
1256
1257void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1258 const SkPaint* paint) {
1259 SkDEBUGCODE(bitmap.validate();)
1260
1261 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1262 SkRect fastBounds;
1263 fastBounds.set(x, y,
1264 x + SkIntToScalar(bitmap.width()),
1265 y + SkIntToScalar(bitmap.height()));
1266 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1267 return;
1268 }
1269 }
reed@google.com4b226022011-01-11 18:32:13 +00001270
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 SkMatrix matrix;
1272 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001273 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274}
1275
1276void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1277 const SkRect& dst, const SkPaint* paint) {
1278 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1279 return;
1280 }
reed@google.com4b226022011-01-11 18:32:13 +00001281
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 // do this now, to avoid the cost of calling extract for RLE bitmaps
1283 if (this->quickReject(dst, paint2EdgeType(paint))) {
1284 return;
1285 }
reed@google.com4b226022011-01-11 18:32:13 +00001286
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001288
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001290 SkRect tmpSrc;
1291 if (src) {
1292 tmpSrc.set(*src);
1293 // if the extract process clipped off the top or left of the
1294 // original, we adjust for that here to get the position right.
1295 if (tmpSrc.fLeft > 0) {
1296 tmpSrc.fRight -= tmpSrc.fLeft;
1297 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001298 }
reed@android.com87899992009-10-16 14:48:38 +00001299 if (tmpSrc.fTop > 0) {
1300 tmpSrc.fBottom -= tmpSrc.fTop;
1301 tmpSrc.fTop = 0;
1302 }
1303 } else {
1304 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1305 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306 }
reed@android.com87899992009-10-16 14:48:38 +00001307 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001308
1309 // ensure that src is "valid" before we pass it to our internal routines
1310 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1311 SkIRect tmpISrc;
1312 if (src) {
1313 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001314 if (!tmpISrc.intersect(*src)) {
1315 return;
1316 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001317 src = &tmpISrc;
1318 }
1319 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320}
1321
1322void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1323 const SkPaint* paint) {
1324 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001325 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326}
1327
reed@android.comf2b98d62010-12-20 18:26:13 +00001328void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1329 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001331
reed@google.com4e2b3d32011-04-07 14:18:59 +00001332 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001333
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001335 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336 }
reed@android.com9b039062009-02-11 15:09:58 +00001337
reed@google.com4e2b3d32011-04-07 14:18:59 +00001338 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339}
1340
1341void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1342 const SkPaint* paint) {
1343 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001344
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345 if (reject_bitmap(bitmap)) {
1346 return;
1347 }
reed@google.com4b226022011-01-11 18:32:13 +00001348
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 SkPaint tmp;
1350 if (NULL == paint) {
1351 paint = &tmp;
1352 }
reed@google.com4b226022011-01-11 18:32:13 +00001353
reed@google.com4e2b3d32011-04-07 14:18:59 +00001354 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001355
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 while (iter.next()) {
1357 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001358 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001360 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361}
1362
reed@google.comf67e4cf2011-03-15 20:56:58 +00001363class SkDeviceFilteredPaint {
1364public:
1365 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1366 SkDevice::TextFlags flags;
1367 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001368 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001369 newPaint->setFlags(flags.fFlags);
1370 newPaint->setHinting(flags.fHinting);
1371 fPaint = newPaint;
1372 } else {
1373 fPaint = &paint;
1374 }
1375 }
1376
reed@google.comf67e4cf2011-03-15 20:56:58 +00001377 const SkPaint& paint() const { return *fPaint; }
1378
1379private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001380 const SkPaint* fPaint;
1381 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001382};
1383
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384void SkCanvas::drawText(const void* text, size_t byteLength,
1385 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001386 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387
1388 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001389 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001390 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391 }
1392
reed@google.com4e2b3d32011-04-07 14:18:59 +00001393 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394}
1395
1396void SkCanvas::drawPosText(const void* text, size_t byteLength,
1397 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001398 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001399
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001401 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001403 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 }
reed@google.com4b226022011-01-11 18:32:13 +00001405
reed@google.com4e2b3d32011-04-07 14:18:59 +00001406 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407}
1408
1409void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1410 const SkScalar xpos[], SkScalar constY,
1411 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001412 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001413
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001415 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001417 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 }
reed@google.com4b226022011-01-11 18:32:13 +00001419
reed@google.com4e2b3d32011-04-07 14:18:59 +00001420 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421}
1422
1423void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1424 const SkPath& path, const SkMatrix* matrix,
1425 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001426 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427
1428 while (iter.next()) {
1429 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001430 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431 }
1432
reed@google.com4e2b3d32011-04-07 14:18:59 +00001433 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434}
1435
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001436#ifdef ANDROID
1437void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1438 const SkPoint pos[], const SkPaint& paint,
1439 const SkPath& path, const SkMatrix* matrix) {
1440
reed@google.com4e2b3d32011-04-07 14:18:59 +00001441 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001442
1443 while (iter.next()) {
1444 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001445 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001446 }
1447
reed@google.com4e2b3d32011-04-07 14:18:59 +00001448 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001449}
1450#endif
1451
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1453 const SkPoint verts[], const SkPoint texs[],
1454 const SkColor colors[], SkXfermode* xmode,
1455 const uint16_t indices[], int indexCount,
1456 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001457 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001458
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459 while (iter.next()) {
1460 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001461 colors, xmode, indices, indexCount,
1462 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 }
reed@google.com4b226022011-01-11 18:32:13 +00001464
reed@google.com4e2b3d32011-04-07 14:18:59 +00001465 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466}
1467
reed@android.comcb608442009-12-04 21:32:27 +00001468void SkCanvas::drawData(const void* data, size_t length) {
1469 // do nothing. Subclasses may do something with the data
1470}
1471
reed@android.com8a1c16f2008-12-17 15:59:43 +00001472//////////////////////////////////////////////////////////////////////////////
1473// These methods are NOT virtual, and therefore must call back into virtual
1474// methods, rather than actually drawing themselves.
1475//////////////////////////////////////////////////////////////////////////////
1476
1477void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001478 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479 SkPaint paint;
1480
1481 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001482 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001483 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 }
1485 this->drawPaint(paint);
1486}
1487
reed@android.com845fdac2009-06-23 03:01:32 +00001488void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 SkPaint paint;
1490
1491 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001492 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001493 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 }
1495 this->drawPaint(paint);
1496}
1497
1498void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1499 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001500
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 pt.set(x, y);
1502 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1503}
1504
1505void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1506 SkPoint pt;
1507 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001508
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509 pt.set(x, y);
1510 paint.setColor(color);
1511 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1512}
1513
1514void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1515 const SkPaint& paint) {
1516 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001517
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518 pts[0].set(x0, y0);
1519 pts[1].set(x1, y1);
1520 this->drawPoints(kLines_PointMode, 2, pts, paint);
1521}
1522
1523void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1524 SkScalar right, SkScalar bottom,
1525 const SkPaint& paint) {
1526 SkRect r;
1527
1528 r.set(left, top, right, bottom);
1529 this->drawRect(r, paint);
1530}
1531
1532void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1533 const SkPaint& paint) {
1534 if (radius < 0) {
1535 radius = 0;
1536 }
1537
1538 SkRect r;
1539 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001540
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541 if (paint.canComputeFastBounds()) {
1542 SkRect storage;
1543 if (this->quickReject(paint.computeFastBounds(r, &storage),
1544 paint2EdgeType(&paint))) {
1545 return;
1546 }
1547 }
reed@google.com4b226022011-01-11 18:32:13 +00001548
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549 SkPath path;
1550 path.addOval(r);
1551 this->drawPath(path, paint);
1552}
1553
1554void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1555 const SkPaint& paint) {
1556 if (rx > 0 && ry > 0) {
1557 if (paint.canComputeFastBounds()) {
1558 SkRect storage;
1559 if (this->quickReject(paint.computeFastBounds(r, &storage),
1560 paint2EdgeType(&paint))) {
1561 return;
1562 }
1563 }
1564
1565 SkPath path;
1566 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1567 this->drawPath(path, paint);
1568 } else {
1569 this->drawRect(r, paint);
1570 }
1571}
1572
1573void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1574 if (paint.canComputeFastBounds()) {
1575 SkRect storage;
1576 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1577 paint2EdgeType(&paint))) {
1578 return;
1579 }
1580 }
1581
1582 SkPath path;
1583 path.addOval(oval);
1584 this->drawPath(path, paint);
1585}
1586
1587void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1588 SkScalar sweepAngle, bool useCenter,
1589 const SkPaint& paint) {
1590 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1591 this->drawOval(oval, paint);
1592 } else {
1593 SkPath path;
1594 if (useCenter) {
1595 path.moveTo(oval.centerX(), oval.centerY());
1596 }
1597 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1598 if (useCenter) {
1599 path.close();
1600 }
1601 this->drawPath(path, paint);
1602 }
1603}
1604
1605void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1606 const SkPath& path, SkScalar hOffset,
1607 SkScalar vOffset, const SkPaint& paint) {
1608 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001609
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610 matrix.setTranslate(hOffset, vOffset);
1611 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1612}
1613
reed@android.comf76bacf2009-05-13 14:00:33 +00001614///////////////////////////////////////////////////////////////////////////////
1615
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616void SkCanvas::drawPicture(SkPicture& picture) {
1617 int saveCount = save();
1618 picture.draw(this);
1619 restoreToCount(saveCount);
1620}
1621
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622///////////////////////////////////////////////////////////////////////////////
1623///////////////////////////////////////////////////////////////////////////////
1624
1625SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001626 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627
1628 SkASSERT(canvas);
1629
1630 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1631 fDone = !fImpl->next();
1632}
1633
1634SkCanvas::LayerIter::~LayerIter() {
1635 fImpl->~SkDrawIter();
1636}
1637
1638void SkCanvas::LayerIter::next() {
1639 fDone = !fImpl->next();
1640}
1641
1642SkDevice* SkCanvas::LayerIter::device() const {
1643 return fImpl->getDevice();
1644}
1645
1646const SkMatrix& SkCanvas::LayerIter::matrix() const {
1647 return fImpl->getMatrix();
1648}
1649
1650const SkPaint& SkCanvas::LayerIter::paint() const {
1651 const SkPaint* paint = fImpl->getPaint();
1652 if (NULL == paint) {
1653 paint = &fDefaultPaint;
1654 }
1655 return *paint;
1656}
1657
1658const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1659int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1660int SkCanvas::LayerIter::y() const { return fImpl->getY(); }