blob: d8349ab16a740aa02ba34a9832bcc1148bec7f1b [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"
21#include <new>
22
23//#define SK_TRACE_SAVERESTORE
24
25#ifdef SK_TRACE_SAVERESTORE
26 static int gLayerCounter;
27 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
28 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
29
30 static int gRecCounter;
31 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
32 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
33
34 static int gCanvasCounter;
35 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
36 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
37#else
38 #define inc_layer()
39 #define dec_layer()
40 #define inc_rec()
41 #define dec_rec()
42 #define inc_canvas()
43 #define dec_canvas()
44#endif
45
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000046typedef SkTLazy<SkPaint> SkLazyPaint;
47
reed@android.com8a1c16f2008-12-17 15:59:43 +000048///////////////////////////////////////////////////////////////////////////////
49// Helpers for computing fast bounds for quickReject tests
50
51static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
52 return paint != NULL && paint->isAntiAlias() ?
53 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
54}
55
56///////////////////////////////////////////////////////////////////////////////
57
58/* This is the record we keep for each SkDevice that the user installs.
59 The clip/matrix/proc are fields that reflect the top of the save/restore
60 stack. Whenever the canvas changes, it marks a dirty flag, and then before
61 these are used (assuming we're not on a layer) we rebuild these cache
62 values: they reflect the top of the save stack, but translated and clipped
63 by the device's XY offset and bitmap-bounds.
64*/
65struct DeviceCM {
66 DeviceCM* fNext;
67 SkDevice* fDevice;
68 SkRegion fClip;
69 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000070 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000071 // optional, related to canvas' external matrix
72 const SkMatrix* fMVMatrix;
73 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000074
75 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
76 : fNext(NULL) {
77 if (NULL != device) {
78 device->ref();
79 device->lockPixels();
80 }
reed@google.com4b226022011-01-11 18:32:13 +000081 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
83 }
84
85 ~DeviceCM() {
86 if (NULL != fDevice) {
87 fDevice->unlockPixels();
88 fDevice->unref();
89 }
90 SkDELETE(fPaint);
91 }
reed@google.com4b226022011-01-11 18:32:13 +000092
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +000094 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +000095 int x = fDevice->getOrigin().x();
96 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 int width = fDevice->width();
98 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 if ((x | y) == 0) {
101 fMatrix = &totalMatrix;
102 fClip = totalClip;
103 } else {
104 fMatrixStorage = totalMatrix;
105 fMatrixStorage.postTranslate(SkIntToScalar(-x),
106 SkIntToScalar(-y));
107 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 totalClip.translate(-x, -y, &fClip);
110 }
111
112 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
113
114 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 if (updateClip) {
117 updateClip->op(x, y, x + width, y + height,
118 SkRegion::kDifference_Op);
119 }
reed@google.com4b226022011-01-11 18:32:13 +0000120
reed@google.com46799cd2011-02-22 20:56:26 +0000121 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122
123#ifdef SK_DEBUG
124 if (!fClip.isEmpty()) {
125 SkIRect deviceR;
126 deviceR.set(0, 0, width, height);
127 SkASSERT(deviceR.contains(fClip.getBounds()));
128 }
129#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000130 // default is to assume no external matrix
131 fMVMatrix = NULL;
132 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000134
135 // can only be called after calling updateMC()
136 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
137 fMVMatrixStorage.setConcat(extI, *fMatrix);
138 fMVMatrix = &fMVMatrixStorage;
139 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
140 }
141
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000143 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144};
145
146/* This is the record we keep for each save/restore level in the stack.
147 Since a level optionally copies the matrix and/or stack, we have pointers
148 for these fields. If the value is copied for this level, the copy is
149 stored in the ...Storage field, and the pointer points to that. If the
150 value is not copied for this level, we ignore ...Storage, and just point
151 at the corresponding value in the previous level in the stack.
152*/
153class SkCanvas::MCRec {
154public:
155 MCRec* fNext;
156 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
157 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
158 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000159
reed@android.com8a1c16f2008-12-17 15:59:43 +0000160 DeviceCM* fLayer;
161 /* If there are any layers in the stack, this points to the top-most
162 one that is at or below this level in the stack (so we know what
163 bitmap/device to draw into from this level. This value is NOT
164 reference counted, since the real owner is either our fLayer field,
165 or a previous one in a lower level.)
166 */
167 DeviceCM* fTopLayer;
168
169 MCRec(const MCRec* prev, int flags) {
170 if (NULL != prev) {
171 if (flags & SkCanvas::kMatrix_SaveFlag) {
172 fMatrixStorage = *prev->fMatrix;
173 fMatrix = &fMatrixStorage;
174 } else {
175 fMatrix = prev->fMatrix;
176 }
reed@google.com4b226022011-01-11 18:32:13 +0000177
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 if (flags & SkCanvas::kClip_SaveFlag) {
179 fRegionStorage = *prev->fRegion;
180 fRegion = &fRegionStorage;
181 } else {
182 fRegion = prev->fRegion;
183 }
184
185 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000186 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187
188 fTopLayer = prev->fTopLayer;
189 } else { // no prev
190 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000191
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 fMatrix = &fMatrixStorage;
193 fRegion = &fRegionStorage;
194 fFilter = NULL;
195 fTopLayer = NULL;
196 }
197 fLayer = NULL;
198
199 // don't bother initializing fNext
200 inc_rec();
201 }
202 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000203 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 SkDELETE(fLayer);
205 dec_rec();
206 }
reed@google.com4b226022011-01-11 18:32:13 +0000207
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208private:
209 SkMatrix fMatrixStorage;
210 SkRegion fRegionStorage;
211};
212
213class SkDrawIter : public SkDraw {
214public:
215 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
216 fCanvas = canvas;
217 canvas->updateDeviceCMCache();
218
reed@google.com7d7ca792011-02-23 22:39:18 +0000219 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 fBounder = canvas->getBounder();
221 fCurrLayer = canvas->fMCRec->fTopLayer;
222 fSkipEmptyClips = skipEmptyClips;
223 }
reed@google.com4b226022011-01-11 18:32:13 +0000224
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 bool next() {
226 // skip over recs with empty clips
227 if (fSkipEmptyClips) {
228 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
229 fCurrLayer = fCurrLayer->fNext;
230 }
231 }
232
233 if (NULL != fCurrLayer) {
234 const DeviceCM* rec = fCurrLayer;
235
236 fMatrix = rec->fMatrix;
237 fClip = &rec->fClip;
238 fDevice = rec->fDevice;
239 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000241 fMVMatrix = rec->fMVMatrix;
242 fExtMatrix = rec->fExtMatrix;
243 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
245 fCurrLayer = rec->fNext;
246 if (fBounder) {
247 fBounder->setClip(fClip);
248 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000250
bsalomon@google.comd302f142011-03-03 13:54:13 +0000251 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 return true;
253 }
254 return false;
255 }
reed@google.com4b226022011-01-11 18:32:13 +0000256
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000258 int getX() const { return fDevice->getOrigin().x(); }
259 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 const SkMatrix& getMatrix() const { return *fMatrix; }
261 const SkRegion& getClip() const { return *fClip; }
262 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000263
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264private:
265 SkCanvas* fCanvas;
266 const DeviceCM* fCurrLayer;
267 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 SkBool8 fSkipEmptyClips;
269
270 typedef SkDraw INHERITED;
271};
272
273/////////////////////////////////////////////////////////////////////////////
274
275class AutoDrawLooper {
276public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000277 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
278 fCanvas = canvas;
279 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000281 fPaint = NULL;
282 fSaveCount = canvas->getSaveCount();
283 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284
reed@google.com4e2b3d32011-04-07 14:18:59 +0000285 if (fLooper) {
286 fLooper->init(canvas);
287 }
288 }
289
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000291 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293
294 const SkPaint& paint() const {
295 SkASSERT(fPaint);
296 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000298
299 bool next(SkDrawFilter::Type drawType);
300
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000302 SkLazyPaint fLazyPaint;
303 SkCanvas* fCanvas;
304 const SkPaint& fOrigPaint;
305 SkDrawLooper* fLooper;
306 SkDrawFilter* fFilter;
307 const SkPaint* fPaint;
308 int fSaveCount;
309 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000310};
311
reed@google.com4e2b3d32011-04-07 14:18:59 +0000312bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
313 if (fDone) {
314 fPaint = NULL;
315 return false;
316 }
317 if (!fLooper && !fFilter) {
318 fDone = true;
319 fPaint = &fOrigPaint;
320 return true;
321 }
322
323 SkPaint* paint = fLazyPaint.set(fOrigPaint);
324 if (fLooper && !fLooper->next(fCanvas, paint)) {
325 fDone = true;
326 fPaint = NULL;
327 return false;
328 }
329 if (fFilter) {
330 fFilter->filter(paint, drawType);
mike@reedtribe.org53e3bed2011-04-08 00:37:03 +0000331 if (NULL == fLooper) {
332 // no looper means we only draw once
333 fDone = true;
334 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335 }
336 fPaint = paint;
337 return true;
338}
339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340/* Stack helper for managing a SkBounder. In the destructor, if we were
341 given a bounder, we call its commit() method, signifying that we are
342 done accumulating bounds for that draw.
343*/
344class SkAutoBounderCommit {
345public:
346 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
347 ~SkAutoBounderCommit() {
348 if (NULL != fBounder) {
349 fBounder->commit();
350 }
351 }
352private:
353 SkBounder* fBounder;
354};
355
356#include "SkColorPriv.h"
357
358class AutoValidator {
359public:
360 AutoValidator(SkDevice* device) : fDevice(device) {}
361 ~AutoValidator() {
362#ifdef SK_DEBUG
363 const SkBitmap& bm = fDevice->accessBitmap(false);
364 if (bm.config() == SkBitmap::kARGB_4444_Config) {
365 for (int y = 0; y < bm.height(); y++) {
366 const SkPMColor16* p = bm.getAddr16(0, y);
367 for (int x = 0; x < bm.width(); x++) {
368 SkPMColor16 c = p[x];
369 SkPMColor16Assert(c);
370 }
371 }
372 }
373#endif
374 }
375private:
376 SkDevice* fDevice;
377};
378
379////////// macros to place around the internal draw calls //////////////////
380
reed@google.com4e2b3d32011-04-07 14:18:59 +0000381#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000383 AutoDrawLooper looper(this, paint); \
384 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385 SkAutoBounderCommit ac(fBounder); \
386 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000387
reed@google.com4e2b3d32011-04-07 14:18:59 +0000388#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389
390////////////////////////////////////////////////////////////////////////////
391
392SkDevice* SkCanvas::init(SkDevice* device) {
393 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000394 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000396 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000397 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000398 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000399 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400
401 fMCRec = (MCRec*)fMCStack.push_back();
402 new (fMCRec) MCRec(NULL, 0);
403
404 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
405 fMCRec->fTopLayer = fMCRec->fLayer;
406 fMCRec->fNext = NULL;
407
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000408 fExternalMatrix.reset();
409 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000410 fUseExternalMatrix = false;
411
reed@android.com8a1c16f2008-12-17 15:59:43 +0000412 return this->setDevice(device);
413}
414
reed@google.comcde92112011-07-06 20:00:52 +0000415SkCanvas::SkCanvas()
416: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000417 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000418
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000419 this->init(NULL);
420}
421
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000423 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 inc_canvas();
425
426 this->init(device);
427}
428
429SkCanvas::SkCanvas(const SkBitmap& bitmap)
430 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
431 inc_canvas();
432
reed@google.comcde92112011-07-06 20:00:52 +0000433 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434}
435
436SkCanvas::~SkCanvas() {
437 // free up the contents of our deque
438 this->restoreToCount(1); // restore everything but the last
439 this->internalRestore(); // restore the last, since we're going away
440
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000441 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000442
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 dec_canvas();
444}
445
446SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
447 SkRefCnt_SafeAssign(fBounder, bounder);
448 return bounder;
449}
450
451SkDrawFilter* SkCanvas::getDrawFilter() const {
452 return fMCRec->fFilter;
453}
454
455SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
456 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
457 return filter;
458}
459
460///////////////////////////////////////////////////////////////////////////////
461
462SkDevice* SkCanvas::getDevice() const {
463 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000464 SkDeque::F2BIter iter(fMCStack);
465 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 SkASSERT(rec && rec->fLayer);
467 return rec->fLayer->fDevice;
468}
469
reed@google.com9266fed2011-03-30 00:18:03 +0000470SkDevice* SkCanvas::getTopDevice() const {
471 return fMCRec->fTopLayer->fDevice;
472}
473
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474SkDevice* SkCanvas::setDevice(SkDevice* device) {
475 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000476 SkDeque::F2BIter iter(fMCStack);
477 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 SkASSERT(rec && rec->fLayer);
479 SkDevice* rootDevice = rec->fLayer->fDevice;
480
481 if (rootDevice == device) {
482 return device;
483 }
reed@google.com4b226022011-01-11 18:32:13 +0000484
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 /* Notify the devices that they are going in/out of scope, so they can do
486 things like lock/unlock their pixels, etc.
487 */
488 if (device) {
489 device->lockPixels();
490 }
491 if (rootDevice) {
492 rootDevice->unlockPixels();
493 }
494
495 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
496 rootDevice = device;
497
498 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 /* Now we update our initial region to have the bounds of the new device,
501 and then intersect all of the clips in our stack with these bounds,
502 to ensure that we can't draw outside of the device's bounds (and trash
503 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 NOTE: this is only a partial-fix, since if the new device is larger than
506 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000507 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
509 reconstruct the correct clips, so this approximation will have to do.
510 The caller really needs to restore() back to the base if they want to
511 accurately take advantage of the new device bounds.
512 */
513
514 if (NULL == device) {
515 rec->fRegion->setEmpty();
516 while ((rec = (MCRec*)iter.next()) != NULL) {
517 (void)rec->fRegion->setEmpty();
518 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000519 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 } else {
521 // compute our total bounds for all devices
522 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 bounds.set(0, 0, device->width(), device->height());
525
526 // now jam our 1st clip to be bounds, and intersect the rest with that
527 rec->fRegion->setRect(bounds);
528 while ((rec = (MCRec*)iter.next()) != NULL) {
529 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
530 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000531 fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 }
533 return device;
534}
535
reed@google.comaf951c92011-06-16 19:10:39 +0000536SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
537 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 device->unref();
539 return device;
540}
541
reed@google.com51df9e32010-12-23 19:29:18 +0000542bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
543 SkDevice* device = this->getDevice();
544 if (!device) {
545 return false;
546 }
547 return device->readPixels(srcRect, bitmap);
548}
549
reed@google.com4b226022011-01-11 18:32:13 +0000550//////////////////////////////////////////////////////////////////////////////
551
reed@google.com51df9e32010-12-23 19:29:18 +0000552bool SkCanvas::readPixels(SkBitmap* bitmap) {
553 SkDevice* device = this->getDevice();
554 if (!device) {
555 return false;
556 }
557 SkIRect bounds;
558 bounds.set(0, 0, device->width(), device->height());
559 return this->readPixels(bounds, bitmap);
560}
561
562void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
563 SkDevice* device = this->getDevice();
564 if (device) {
565 device->writePixels(bitmap, x, y);
566 }
567}
568
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569//////////////////////////////////////////////////////////////////////////////
570
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571void SkCanvas::updateDeviceCMCache() {
572 if (fDeviceCMDirty) {
573 const SkMatrix& totalMatrix = this->getTotalMatrix();
574 const SkRegion& totalClip = this->getTotalClip();
575 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000576
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000578 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000579 if (fUseExternalMatrix) {
580 layer->updateExternalMatrix(fExternalMatrix,
581 fExternalInverse);
582 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 } else {
584 SkRegion clip;
585 clip = totalClip; // make a copy
586 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000587 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000588 if (fUseExternalMatrix) {
589 layer->updateExternalMatrix(fExternalMatrix,
590 fExternalInverse);
591 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 } while ((layer = layer->fNext) != NULL);
593 }
594 fDeviceCMDirty = false;
595 }
596}
597
reed@android.comf2b98d62010-12-20 18:26:13 +0000598void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000599 const SkRegion& clip,
600 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000602 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000603 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000604 fLastDeviceToGainFocus = device;
605 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606}
607
608///////////////////////////////////////////////////////////////////////////////
609
610int SkCanvas::internalSave(SaveFlags flags) {
611 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000612
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 MCRec* newTop = (MCRec*)fMCStack.push_back();
614 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 newTop->fNext = fMCRec;
617 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000618
reed@google.com5c3d1472011-02-22 19:12:23 +0000619 fClipStack.save();
620 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
621
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 return saveCount;
623}
624
625int SkCanvas::save(SaveFlags flags) {
626 // call shared impl
627 return this->internalSave(flags);
628}
629
630#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
631#define C16MASK (1 << SkBitmap::kRGB_565_Config)
632#define C8MASK (1 << SkBitmap::kA8_Config)
633
634static SkBitmap::Config resolve_config(SkCanvas* canvas,
635 const SkIRect& bounds,
636 SkCanvas::SaveFlags flags,
637 bool* isOpaque) {
638 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
639
640#if 0
641 // loop through and union all the configs we may draw into
642 uint32_t configMask = 0;
643 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
644 {
645 SkDevice* device = canvas->getLayerDevice(i);
646 if (device->intersects(bounds))
647 configMask |= 1 << device->config();
648 }
649
650 // if the caller wants alpha or fullcolor, we can't return 565
651 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
652 SkCanvas::kHasAlphaLayer_SaveFlag))
653 configMask &= ~C16MASK;
654
655 switch (configMask) {
656 case C8MASK: // if we only have A8, return that
657 return SkBitmap::kA8_Config;
658
659 case C16MASK: // if we only have 565, return that
660 return SkBitmap::kRGB_565_Config;
661
662 default:
663 return SkBitmap::kARGB_8888_Config; // default answer
664 }
665#else
666 return SkBitmap::kARGB_8888_Config; // default answer
667#endif
668}
669
670static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
671 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
672}
673
674int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
675 SaveFlags flags) {
676 // do this before we create the layer. We don't call the public save() since
677 // that would invoke a possibly overridden virtual
678 int count = this->internalSave(flags);
679
680 fDeviceCMDirty = true;
681
682 SkIRect ir;
683 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000684 if (clipBounds.isEmpty()) {
685 return count;
686 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687
688 if (NULL != bounds) {
689 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000690
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691 this->getTotalMatrix().mapRect(&r, *bounds);
692 r.roundOut(&ir);
693 // early exit if the layer's bounds are clipped out
694 if (!ir.intersect(clipBounds)) {
695 if (bounds_affects_clip(flags))
696 fMCRec->fRegion->setEmpty();
697 return count;
698 }
699 } else { // no user bounds, so just use the clip
700 ir = clipBounds;
701 }
702
reed@google.com5c3d1472011-02-22 19:12:23 +0000703 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 // early exit if the clip is now empty
705 if (bounds_affects_clip(flags) &&
706 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
707 return count;
708 }
709
710 bool isOpaque;
711 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
712
bsalomon@google.come97f0852011-06-17 13:10:25 +0000713 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
714 isOpaque);
715
reed@google.com6f8f2922011-03-04 22:27:10 +0000716 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
718 device->unref();
719
720 layer->fNext = fMCRec->fTopLayer;
721 fMCRec->fLayer = layer;
722 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
723
724 return count;
725}
726
727int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
728 SaveFlags flags) {
729 if (0xFF == alpha) {
730 return this->saveLayer(bounds, NULL, flags);
731 } else {
732 SkPaint tmpPaint;
733 tmpPaint.setAlpha(alpha);
734 return this->saveLayer(bounds, &tmpPaint, flags);
735 }
736}
737
738void SkCanvas::restore() {
739 // check for underflow
740 if (fMCStack.count() > 1) {
741 this->internalRestore();
742 }
743}
744
745void SkCanvas::internalRestore() {
746 SkASSERT(fMCStack.count() != 0);
747
748 fDeviceCMDirty = true;
749 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000750 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751
reed@google.com5c3d1472011-02-22 19:12:23 +0000752 fClipStack.restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 // reserve our layer (if any)
754 DeviceCM* layer = fMCRec->fLayer; // may be null
755 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
756 fMCRec->fLayer = NULL;
757
758 // now do the normal restore()
759 fMCRec->~MCRec(); // balanced in save()
760 fMCStack.pop_back();
761 fMCRec = (MCRec*)fMCStack.back();
762
763 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
764 since if we're being recorded, we don't want to record this (the
765 recorder will have already recorded the restore).
766 */
767 if (NULL != layer) {
768 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000769 const SkIPoint& origin = layer->fDevice->getOrigin();
770 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 layer->fPaint);
772 // reset this, since drawDevice will have set it to true
773 fDeviceCMDirty = true;
774 }
775 SkDELETE(layer);
776 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000777
778 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779}
780
781int SkCanvas::getSaveCount() const {
782 return fMCStack.count();
783}
784
785void SkCanvas::restoreToCount(int count) {
786 // sanity check
787 if (count < 1) {
788 count = 1;
789 }
790 while (fMCStack.count() > count) {
791 this->restore();
792 }
793}
794
795/////////////////////////////////////////////////////////////////////////////
796
797// can't draw it if its empty, or its too big for a fixed-point width or height
798static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000799 return bitmap.width() <= 0 || bitmap.height() <= 0
800#ifndef SK_ALLOW_OVER_32K_BITMAPS
801 || bitmap.width() > 32767 || bitmap.height() > 32767
802#endif
803 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804}
805
reed@android.comf2b98d62010-12-20 18:26:13 +0000806void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 const SkMatrix& matrix, const SkPaint* paint) {
808 if (reject_bitmap(bitmap)) {
809 return;
810 }
811
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000812 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000814 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000816 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817}
818
819void SkCanvas::drawDevice(SkDevice* device, int x, int y,
820 const SkPaint* paint) {
821 SkPaint tmp;
822 if (NULL == paint) {
823 tmp.setDither(true);
824 paint = &tmp;
825 }
reed@google.com4b226022011-01-11 18:32:13 +0000826
reed@google.com4e2b3d32011-04-07 14:18:59 +0000827 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 while (iter.next()) {
829 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000830 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000832 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833}
834
835/////////////////////////////////////////////////////////////////////////////
836
837bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
838 fDeviceCMDirty = true;
839 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000840 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 return fMCRec->fMatrix->preTranslate(dx, dy);
842}
843
844bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
845 fDeviceCMDirty = true;
846 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000847 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 return fMCRec->fMatrix->preScale(sx, sy);
849}
850
851bool SkCanvas::rotate(SkScalar degrees) {
852 fDeviceCMDirty = true;
853 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000854 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 return fMCRec->fMatrix->preRotate(degrees);
856}
857
858bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
859 fDeviceCMDirty = true;
860 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000861 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 return fMCRec->fMatrix->preSkew(sx, sy);
863}
864
865bool SkCanvas::concat(const SkMatrix& matrix) {
866 fDeviceCMDirty = true;
867 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000868 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000869 return fMCRec->fMatrix->preConcat(matrix);
870}
871
872void SkCanvas::setMatrix(const SkMatrix& matrix) {
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 *fMCRec->fMatrix = matrix;
877}
878
879// this is not virtual, so it must call a virtual method so that subclasses
880// will see its action
881void SkCanvas::resetMatrix() {
882 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000883
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 matrix.reset();
885 this->setMatrix(matrix);
886}
887
888//////////////////////////////////////////////////////////////////////////////
889
890bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000891 AutoValidateClip avc(this);
892
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 fDeviceCMDirty = true;
894 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000895 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896
897 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000898 // for these simpler matrices, we can stay a rect ever after applying
899 // the matrix. This means we don't have to a) make a path, and b) tell
900 // the region code to scan-convert the path, only to discover that it
901 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 SkRect r;
903 SkIRect ir;
904
905 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000906 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 r.round(&ir);
908 return fMCRec->fRegion->op(ir, op);
909 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000910 // since we're rotate or some such thing, we convert the rect to a path
911 // and clip against that, since it can handle any matrix. However, to
912 // avoid recursion in the case where we are subclassed (e.g. Pictures)
913 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 SkPath path;
915
916 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000917 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 }
919}
920
reed@google.com819c9212011-02-23 18:56:55 +0000921static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
922 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000923 // base is used to limit the size (and therefore memory allocation) of the
924 // region that results from scan converting devPath.
925 SkRegion base;
926
reed@google.com819c9212011-02-23 18:56:55 +0000927 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000928 // since we are intersect, we can do better (tighter) with currRgn's
929 // bounds, than just using the device. However, if currRgn is complex,
930 // our region blitter may hork, so we do that case in two steps.
931 if (currRgn->isRect()) {
932 return currRgn->setPath(devPath, *currRgn);
933 } else {
934 base.setRect(currRgn->getBounds());
935 SkRegion rgn;
936 rgn.setPath(devPath, base);
937 return currRgn->op(rgn, op);
938 }
reed@google.com819c9212011-02-23 18:56:55 +0000939 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000940 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
941 base.setRect(0, 0, bm.width(), bm.height());
942
943 if (SkRegion::kReplace_Op == op) {
944 return currRgn->setPath(devPath, base);
945 } else {
946 SkRegion rgn;
947 rgn.setPath(devPath, base);
948 return currRgn->op(rgn, op);
949 }
950 }
951}
952
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000954 AutoValidateClip avc(this);
955
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 fDeviceCMDirty = true;
957 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000958 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959
960 SkPath devPath;
961 path.transform(*fMCRec->fMatrix, &devPath);
962
reed@google.com5c3d1472011-02-22 19:12:23 +0000963 // if we called path.swap() we could avoid a deep copy of this path
964 fClipStack.clipDevPath(devPath, op);
965
reed@google.com819c9212011-02-23 18:56:55 +0000966 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967}
968
969bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000970 AutoValidateClip avc(this);
971
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 fDeviceCMDirty = true;
973 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000974 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975
reed@google.com5c3d1472011-02-22 19:12:23 +0000976 // todo: signal fClipStack that we have a region, and therefore (I guess)
977 // we have to ignore it, and use the region directly?
978 fClipStack.clipDevRect(rgn.getBounds());
979
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 return fMCRec->fRegion->op(rgn, op);
981}
982
reed@google.com819c9212011-02-23 18:56:55 +0000983#ifdef SK_DEBUG
984void SkCanvas::validateClip() const {
985 // construct clipRgn from the clipstack
986 const SkDevice* device = this->getDevice();
987 SkIRect ir;
988 ir.set(0, 0, device->width(), device->height());
989 SkRegion clipRgn(ir);
990
991 SkClipStack::B2FIter iter(fClipStack);
992 const SkClipStack::B2FIter::Clip* clip;
993 while ((clip = iter.next()) != NULL) {
994 if (clip->fPath) {
995 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
996 } else if (clip->fRect) {
997 clip->fRect->round(&ir);
998 clipRgn.op(ir, clip->fOp);
999 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001000 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001001 }
1002 }
1003
reed@google.com6f8f2922011-03-04 22:27:10 +00001004#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001005 // now compare against the current rgn
1006 const SkRegion& rgn = this->getTotalClip();
1007 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001008#endif
reed@google.com819c9212011-02-23 18:56:55 +00001009}
1010#endif
1011
reed@google.com5c3d1472011-02-22 19:12:23 +00001012///////////////////////////////////////////////////////////////////////////////
1013
reed@android.comba09de42010-02-05 20:46:05 +00001014void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001016 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1017 fLocalBoundsCompareTypeBW;
1018
1019 if (!this->getClipBounds(&r, et)) {
1020 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001022 rCompare.set(SkScalarToCompareType(r.fLeft),
1023 SkScalarToCompareType(r.fTop),
1024 SkScalarToCompareType(r.fRight),
1025 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001026 }
1027}
1028
reed@android.comd252db02009-04-01 18:31:44 +00001029/* current impl ignores edgetype, and relies on
1030 getLocalClipBoundsCompareType(), which always returns a value assuming
1031 antialiasing (worst case)
1032 */
reed@android.comba09de42010-02-05 20:46:05 +00001033bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001034
1035 if (!rect.hasValidCoordinates())
1036 return true;
1037
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 if (fMCRec->fRegion->isEmpty()) {
1039 return true;
1040 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041
tomhudson@google.com8d430182011-06-06 19:11:19 +00001042 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001043 SkRect dst;
1044 fMCRec->fMatrix->mapRect(&dst, rect);
1045 SkIRect idst;
1046 dst.roundOut(&idst);
1047 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1048 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001049 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001050
reed@android.coma380ae42009-07-21 01:17:02 +00001051 // for speed, do the most likely reject compares first
1052 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1053 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1054 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1055 return true;
1056 }
1057 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1058 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1059 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1060 return true;
1061 }
1062 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064}
1065
1066bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001067 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068}
1069
1070bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1071 /* current impl ignores edgetype, and relies on
1072 getLocalClipBoundsCompareType(), which always returns a value assuming
1073 antialiasing (worst case)
1074 */
1075
1076 if (fMCRec->fRegion->isEmpty()) {
1077 return true;
1078 }
reed@google.com4b226022011-01-11 18:32:13 +00001079
reed@android.comaefd2bc2009-03-30 21:02:14 +00001080 SkScalarCompareType userT = SkScalarToCompareType(top);
1081 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001082
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001084 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 if (userT >= userB) {
1086 return true;
1087 }
reed@google.com4b226022011-01-11 18:32:13 +00001088
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 // check if we are above or below the local clip bounds
1090 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1091 return userT >= clipR.fBottom || userB <= clipR.fTop;
1092}
1093
1094bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1095 const SkRegion& clip = *fMCRec->fRegion;
1096 if (clip.isEmpty()) {
1097 if (bounds) {
1098 bounds->setEmpty();
1099 }
1100 return false;
1101 }
1102
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001103 SkMatrix inverse;
1104 // if we can't invert the CTM, we can't return local clip bounds
1105 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001106 if (bounds) {
1107 bounds->setEmpty();
1108 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001109 return false;
1110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001112 if (NULL != bounds) {
1113 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114 // get the clip's bounds
1115 const SkIRect& ibounds = clip.getBounds();
1116 // adjust it outwards if we are antialiasing
1117 int inset = (kAA_EdgeType == et);
1118 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1119 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001120
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121 // invert into local coordinates
1122 inverse.mapRect(bounds, r);
1123 }
1124 return true;
1125}
1126
1127const SkMatrix& SkCanvas::getTotalMatrix() const {
1128 return *fMCRec->fMatrix;
1129}
1130
1131const SkRegion& SkCanvas::getTotalClip() const {
1132 return *fMCRec->fRegion;
1133}
1134
reed@google.com7d7ca792011-02-23 22:39:18 +00001135const SkClipStack& SkCanvas::getTotalClipStack() const {
1136 return fClipStack;
1137}
1138
reed@android.comf2b98d62010-12-20 18:26:13 +00001139void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1140 if (NULL == matrix || matrix->isIdentity()) {
1141 if (fUseExternalMatrix) {
1142 fDeviceCMDirty = true;
1143 }
1144 fUseExternalMatrix = false;
1145 } else {
1146 fUseExternalMatrix = true;
1147 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001148
reed@android.comf2b98d62010-12-20 18:26:13 +00001149 fExternalMatrix = *matrix;
1150 matrix->invert(&fExternalInverse);
1151 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001152}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153
bsalomon@google.come97f0852011-06-17 13:10:25 +00001154SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1155 int width, int height,
1156 bool isOpaque) {
reed@google.comcde92112011-07-06 20:00:52 +00001157 SkDevice* device = this->getDevice();
1158 if (device) {
1159 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1160 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001161 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001162 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001163 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001164}
1165
bsalomon@google.come97f0852011-06-17 13:10:25 +00001166SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1167 int width, int height,
1168 bool isOpaque) {
1169 SkDevice* device = this->getDevice();
1170 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001171 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001172 } else {
1173 return NULL;
1174 }
1175}
1176
1177
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178//////////////////////////////////////////////////////////////////////////////
1179// These are the virtual drawing methods
1180//////////////////////////////////////////////////////////////////////////////
1181
reed@google.com2a981812011-04-14 18:59:28 +00001182void SkCanvas::clear(SkColor color) {
1183 SkDrawIter iter(this);
1184
1185 while (iter.next()) {
1186 iter.fDevice->clear(color);
1187 }
1188}
1189
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190void SkCanvas::drawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001191 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192
1193 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001194 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 }
1196
reed@google.com4e2b3d32011-04-07 14:18:59 +00001197 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198}
1199
1200void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1201 const SkPaint& paint) {
1202 if ((long)count <= 0) {
1203 return;
1204 }
1205
1206 SkASSERT(pts != NULL);
1207
reed@google.com4e2b3d32011-04-07 14:18:59 +00001208 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001209
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001211 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 }
reed@google.com4b226022011-01-11 18:32:13 +00001213
reed@google.com4e2b3d32011-04-07 14:18:59 +00001214 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215}
1216
1217void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1218 if (paint.canComputeFastBounds()) {
1219 SkRect storage;
1220 if (this->quickReject(paint.computeFastBounds(r, &storage),
1221 paint2EdgeType(&paint))) {
1222 return;
1223 }
1224 }
reed@google.com4b226022011-01-11 18:32:13 +00001225
reed@google.com4e2b3d32011-04-07 14:18:59 +00001226 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227
1228 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001229 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 }
1231
reed@google.com4e2b3d32011-04-07 14:18:59 +00001232 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233}
1234
1235void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1236 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001237 SkRect storage;
1238 const SkRect& bounds = path.getBounds();
1239 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 paint2EdgeType(&paint))) {
1241 return;
1242 }
1243 }
1244
reed@google.com4e2b3d32011-04-07 14:18:59 +00001245 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246
1247 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001248 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 }
1250
reed@google.com4e2b3d32011-04-07 14:18:59 +00001251 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252}
1253
1254void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1255 const SkPaint* paint) {
1256 SkDEBUGCODE(bitmap.validate();)
1257
1258 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1259 SkRect fastBounds;
1260 fastBounds.set(x, y,
1261 x + SkIntToScalar(bitmap.width()),
1262 y + SkIntToScalar(bitmap.height()));
1263 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1264 return;
1265 }
1266 }
reed@google.com4b226022011-01-11 18:32:13 +00001267
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268 SkMatrix matrix;
1269 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001270 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271}
1272
1273void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1274 const SkRect& dst, const SkPaint* paint) {
1275 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1276 return;
1277 }
reed@google.com4b226022011-01-11 18:32:13 +00001278
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279 // do this now, to avoid the cost of calling extract for RLE bitmaps
1280 if (this->quickReject(dst, paint2EdgeType(paint))) {
1281 return;
1282 }
reed@google.com4b226022011-01-11 18:32:13 +00001283
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001285
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001287 SkRect tmpSrc;
1288 if (src) {
1289 tmpSrc.set(*src);
1290 // if the extract process clipped off the top or left of the
1291 // original, we adjust for that here to get the position right.
1292 if (tmpSrc.fLeft > 0) {
1293 tmpSrc.fRight -= tmpSrc.fLeft;
1294 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001295 }
reed@android.com87899992009-10-16 14:48:38 +00001296 if (tmpSrc.fTop > 0) {
1297 tmpSrc.fBottom -= tmpSrc.fTop;
1298 tmpSrc.fTop = 0;
1299 }
1300 } else {
1301 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1302 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 }
reed@android.com87899992009-10-16 14:48:38 +00001304 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001305
1306 // ensure that src is "valid" before we pass it to our internal routines
1307 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1308 SkIRect tmpISrc;
1309 if (src) {
1310 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001311 if (!tmpISrc.intersect(*src)) {
1312 return;
1313 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001314 src = &tmpISrc;
1315 }
1316 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317}
1318
1319void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1320 const SkPaint* paint) {
1321 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001322 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323}
1324
reed@android.comf2b98d62010-12-20 18:26:13 +00001325void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1326 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001328
reed@google.com4e2b3d32011-04-07 14:18:59 +00001329 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001330
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001332 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 }
reed@android.com9b039062009-02-11 15:09:58 +00001334
reed@google.com4e2b3d32011-04-07 14:18:59 +00001335 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336}
1337
1338void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1339 const SkPaint* paint) {
1340 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001341
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 if (reject_bitmap(bitmap)) {
1343 return;
1344 }
reed@google.com4b226022011-01-11 18:32:13 +00001345
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 SkPaint tmp;
1347 if (NULL == paint) {
1348 paint = &tmp;
1349 }
reed@google.com4b226022011-01-11 18:32:13 +00001350
reed@google.com4e2b3d32011-04-07 14:18:59 +00001351 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001352
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 while (iter.next()) {
1354 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001355 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001357 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358}
1359
reed@google.comf67e4cf2011-03-15 20:56:58 +00001360class SkDeviceFilteredPaint {
1361public:
1362 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1363 SkDevice::TextFlags flags;
1364 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001365 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001366 newPaint->setFlags(flags.fFlags);
1367 newPaint->setHinting(flags.fHinting);
1368 fPaint = newPaint;
1369 } else {
1370 fPaint = &paint;
1371 }
1372 }
1373
reed@google.comf67e4cf2011-03-15 20:56:58 +00001374 const SkPaint& paint() const { return *fPaint; }
1375
1376private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001377 const SkPaint* fPaint;
1378 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001379};
1380
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381void SkCanvas::drawText(const void* text, size_t byteLength,
1382 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001383 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384
1385 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001386 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001387 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 }
1389
reed@google.com4e2b3d32011-04-07 14:18:59 +00001390 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391}
1392
1393void SkCanvas::drawPosText(const void* text, size_t byteLength,
1394 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001395 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001396
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001398 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001400 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 }
reed@google.com4b226022011-01-11 18:32:13 +00001402
reed@google.com4e2b3d32011-04-07 14:18:59 +00001403 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404}
1405
1406void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1407 const SkScalar xpos[], SkScalar constY,
1408 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001409 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001410
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001412 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001414 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415 }
reed@google.com4b226022011-01-11 18:32:13 +00001416
reed@google.com4e2b3d32011-04-07 14:18:59 +00001417 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418}
1419
1420void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1421 const SkPath& path, const SkMatrix* matrix,
1422 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001423 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424
1425 while (iter.next()) {
1426 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001427 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 }
1429
reed@google.com4e2b3d32011-04-07 14:18:59 +00001430 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431}
1432
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001433#ifdef ANDROID
1434void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1435 const SkPoint pos[], const SkPaint& paint,
1436 const SkPath& path, const SkMatrix* matrix) {
1437
reed@google.com4e2b3d32011-04-07 14:18:59 +00001438 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001439
1440 while (iter.next()) {
1441 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001442 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001443 }
1444
reed@google.com4e2b3d32011-04-07 14:18:59 +00001445 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001446}
1447#endif
1448
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1450 const SkPoint verts[], const SkPoint texs[],
1451 const SkColor colors[], SkXfermode* xmode,
1452 const uint16_t indices[], int indexCount,
1453 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001454 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001455
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 while (iter.next()) {
1457 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001458 colors, xmode, indices, indexCount,
1459 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 }
reed@google.com4b226022011-01-11 18:32:13 +00001461
reed@google.com4e2b3d32011-04-07 14:18:59 +00001462 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463}
1464
reed@android.comcb608442009-12-04 21:32:27 +00001465void SkCanvas::drawData(const void* data, size_t length) {
1466 // do nothing. Subclasses may do something with the data
1467}
1468
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469//////////////////////////////////////////////////////////////////////////////
1470// These methods are NOT virtual, and therefore must call back into virtual
1471// methods, rather than actually drawing themselves.
1472//////////////////////////////////////////////////////////////////////////////
1473
1474void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001475 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 SkPaint paint;
1477
1478 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001479 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001480 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481 }
1482 this->drawPaint(paint);
1483}
1484
reed@android.com845fdac2009-06-23 03:01:32 +00001485void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486 SkPaint paint;
1487
1488 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001489 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001490 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491 }
1492 this->drawPaint(paint);
1493}
1494
1495void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1496 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001497
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 pt.set(x, y);
1499 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1500}
1501
1502void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1503 SkPoint pt;
1504 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001505
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 pt.set(x, y);
1507 paint.setColor(color);
1508 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1509}
1510
1511void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1512 const SkPaint& paint) {
1513 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001514
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515 pts[0].set(x0, y0);
1516 pts[1].set(x1, y1);
1517 this->drawPoints(kLines_PointMode, 2, pts, paint);
1518}
1519
1520void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1521 SkScalar right, SkScalar bottom,
1522 const SkPaint& paint) {
1523 SkRect r;
1524
1525 r.set(left, top, right, bottom);
1526 this->drawRect(r, paint);
1527}
1528
1529void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1530 const SkPaint& paint) {
1531 if (radius < 0) {
1532 radius = 0;
1533 }
1534
1535 SkRect r;
1536 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001537
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 if (paint.canComputeFastBounds()) {
1539 SkRect storage;
1540 if (this->quickReject(paint.computeFastBounds(r, &storage),
1541 paint2EdgeType(&paint))) {
1542 return;
1543 }
1544 }
reed@google.com4b226022011-01-11 18:32:13 +00001545
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546 SkPath path;
1547 path.addOval(r);
1548 this->drawPath(path, paint);
1549}
1550
1551void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1552 const SkPaint& paint) {
1553 if (rx > 0 && ry > 0) {
1554 if (paint.canComputeFastBounds()) {
1555 SkRect storage;
1556 if (this->quickReject(paint.computeFastBounds(r, &storage),
1557 paint2EdgeType(&paint))) {
1558 return;
1559 }
1560 }
1561
1562 SkPath path;
1563 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1564 this->drawPath(path, paint);
1565 } else {
1566 this->drawRect(r, paint);
1567 }
1568}
1569
1570void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1571 if (paint.canComputeFastBounds()) {
1572 SkRect storage;
1573 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1574 paint2EdgeType(&paint))) {
1575 return;
1576 }
1577 }
1578
1579 SkPath path;
1580 path.addOval(oval);
1581 this->drawPath(path, paint);
1582}
1583
1584void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1585 SkScalar sweepAngle, bool useCenter,
1586 const SkPaint& paint) {
1587 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1588 this->drawOval(oval, paint);
1589 } else {
1590 SkPath path;
1591 if (useCenter) {
1592 path.moveTo(oval.centerX(), oval.centerY());
1593 }
1594 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1595 if (useCenter) {
1596 path.close();
1597 }
1598 this->drawPath(path, paint);
1599 }
1600}
1601
1602void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1603 const SkPath& path, SkScalar hOffset,
1604 SkScalar vOffset, const SkPaint& paint) {
1605 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001606
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 matrix.setTranslate(hOffset, vOffset);
1608 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1609}
1610
reed@android.comf76bacf2009-05-13 14:00:33 +00001611///////////////////////////////////////////////////////////////////////////////
1612
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613void SkCanvas::drawPicture(SkPicture& picture) {
1614 int saveCount = save();
1615 picture.draw(this);
1616 restoreToCount(saveCount);
1617}
1618
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619///////////////////////////////////////////////////////////////////////////////
1620///////////////////////////////////////////////////////////////////////////////
1621
1622SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001623 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624
1625 SkASSERT(canvas);
1626
1627 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1628 fDone = !fImpl->next();
1629}
1630
1631SkCanvas::LayerIter::~LayerIter() {
1632 fImpl->~SkDrawIter();
1633}
1634
1635void SkCanvas::LayerIter::next() {
1636 fDone = !fImpl->next();
1637}
1638
1639SkDevice* SkCanvas::LayerIter::device() const {
1640 return fImpl->getDevice();
1641}
1642
1643const SkMatrix& SkCanvas::LayerIter::matrix() const {
1644 return fImpl->getMatrix();
1645}
1646
1647const SkPaint& SkCanvas::LayerIter::paint() const {
1648 const SkPaint* paint = fImpl->getPaint();
1649 if (NULL == paint) {
1650 paint = &fDefaultPaint;
1651 }
1652 return *paint;
1653}
1654
1655const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1656int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1657int SkCanvas::LayerIter::y() const { return fImpl->getY(); }