blob: 823ce8f99679e54978ded02b7e258ae50c2b6af7 [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);
714
reed@google.com6f8f2922011-03-04 22:27:10 +0000715 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
717 device->unref();
718
719 layer->fNext = fMCRec->fTopLayer;
720 fMCRec->fLayer = layer;
721 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
722
723 return count;
724}
725
726int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
727 SaveFlags flags) {
728 if (0xFF == alpha) {
729 return this->saveLayer(bounds, NULL, flags);
730 } else {
731 SkPaint tmpPaint;
732 tmpPaint.setAlpha(alpha);
733 return this->saveLayer(bounds, &tmpPaint, flags);
734 }
735}
736
737void SkCanvas::restore() {
738 // check for underflow
739 if (fMCStack.count() > 1) {
740 this->internalRestore();
741 }
742}
743
744void SkCanvas::internalRestore() {
745 SkASSERT(fMCStack.count() != 0);
746
747 fDeviceCMDirty = true;
748 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000749 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750
reed@google.com5c3d1472011-02-22 19:12:23 +0000751 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000752 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 DeviceCM* layer = fMCRec->fLayer; // may be null
754 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
755 fMCRec->fLayer = NULL;
756
757 // now do the normal restore()
758 fMCRec->~MCRec(); // balanced in save()
759 fMCStack.pop_back();
760 fMCRec = (MCRec*)fMCStack.back();
761
762 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
763 since if we're being recorded, we don't want to record this (the
764 recorder will have already recorded the restore).
765 */
766 if (NULL != layer) {
767 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000768 const SkIPoint& origin = layer->fDevice->getOrigin();
769 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 layer->fPaint);
771 // reset this, since drawDevice will have set it to true
772 fDeviceCMDirty = true;
773 }
774 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000775 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000776
777 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778}
779
780int SkCanvas::getSaveCount() const {
781 return fMCStack.count();
782}
783
784void SkCanvas::restoreToCount(int count) {
785 // sanity check
786 if (count < 1) {
787 count = 1;
788 }
789 while (fMCStack.count() > count) {
790 this->restore();
791 }
792}
793
794/////////////////////////////////////////////////////////////////////////////
795
796// can't draw it if its empty, or its too big for a fixed-point width or height
797static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000798 return bitmap.width() <= 0 || bitmap.height() <= 0
799#ifndef SK_ALLOW_OVER_32K_BITMAPS
800 || bitmap.width() > 32767 || bitmap.height() > 32767
801#endif
802 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803}
804
reed@android.comf2b98d62010-12-20 18:26:13 +0000805void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806 const SkMatrix& matrix, const SkPaint* paint) {
807 if (reject_bitmap(bitmap)) {
808 return;
809 }
810
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000811 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000813 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000815 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816}
817
818void SkCanvas::drawDevice(SkDevice* device, int x, int y,
819 const SkPaint* paint) {
820 SkPaint tmp;
821 if (NULL == paint) {
822 tmp.setDither(true);
823 paint = &tmp;
824 }
reed@google.com4b226022011-01-11 18:32:13 +0000825
reed@google.com4e2b3d32011-04-07 14:18:59 +0000826 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 while (iter.next()) {
828 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000829 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000831 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832}
833
834/////////////////////////////////////////////////////////////////////////////
835
836bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
837 fDeviceCMDirty = true;
838 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000839 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 return fMCRec->fMatrix->preTranslate(dx, dy);
841}
842
843bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
844 fDeviceCMDirty = true;
845 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000846 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 return fMCRec->fMatrix->preScale(sx, sy);
848}
849
850bool SkCanvas::rotate(SkScalar degrees) {
851 fDeviceCMDirty = true;
852 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000853 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 return fMCRec->fMatrix->preRotate(degrees);
855}
856
857bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
858 fDeviceCMDirty = true;
859 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000860 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 return fMCRec->fMatrix->preSkew(sx, sy);
862}
863
864bool SkCanvas::concat(const SkMatrix& matrix) {
865 fDeviceCMDirty = true;
866 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000867 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 return fMCRec->fMatrix->preConcat(matrix);
869}
870
871void SkCanvas::setMatrix(const SkMatrix& matrix) {
872 fDeviceCMDirty = true;
873 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000874 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 *fMCRec->fMatrix = matrix;
876}
877
878// this is not virtual, so it must call a virtual method so that subclasses
879// will see its action
880void SkCanvas::resetMatrix() {
881 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000882
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 matrix.reset();
884 this->setMatrix(matrix);
885}
886
887//////////////////////////////////////////////////////////////////////////////
888
889bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000890 AutoValidateClip avc(this);
891
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 fDeviceCMDirty = true;
893 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000894 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895
896 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000897 // for these simpler matrices, we can stay a rect ever after applying
898 // the matrix. This means we don't have to a) make a path, and b) tell
899 // the region code to scan-convert the path, only to discover that it
900 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 SkRect r;
902 SkIRect ir;
903
904 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000905 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 r.round(&ir);
907 return fMCRec->fRegion->op(ir, op);
908 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000909 // since we're rotate or some such thing, we convert the rect to a path
910 // and clip against that, since it can handle any matrix. However, to
911 // avoid recursion in the case where we are subclassed (e.g. Pictures)
912 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 SkPath path;
914
915 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000916 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 }
918}
919
reed@google.com819c9212011-02-23 18:56:55 +0000920static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
921 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000922 // base is used to limit the size (and therefore memory allocation) of the
923 // region that results from scan converting devPath.
924 SkRegion base;
925
reed@google.com819c9212011-02-23 18:56:55 +0000926 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000927 // since we are intersect, we can do better (tighter) with currRgn's
928 // bounds, than just using the device. However, if currRgn is complex,
929 // our region blitter may hork, so we do that case in two steps.
930 if (currRgn->isRect()) {
931 return currRgn->setPath(devPath, *currRgn);
932 } else {
933 base.setRect(currRgn->getBounds());
934 SkRegion rgn;
935 rgn.setPath(devPath, base);
936 return currRgn->op(rgn, op);
937 }
reed@google.com819c9212011-02-23 18:56:55 +0000938 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000939 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
940 base.setRect(0, 0, bm.width(), bm.height());
941
942 if (SkRegion::kReplace_Op == op) {
943 return currRgn->setPath(devPath, base);
944 } else {
945 SkRegion rgn;
946 rgn.setPath(devPath, base);
947 return currRgn->op(rgn, op);
948 }
949 }
950}
951
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000953 AutoValidateClip avc(this);
954
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 fDeviceCMDirty = true;
956 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000957 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958
959 SkPath devPath;
960 path.transform(*fMCRec->fMatrix, &devPath);
961
reed@google.com5c3d1472011-02-22 19:12:23 +0000962 // if we called path.swap() we could avoid a deep copy of this path
963 fClipStack.clipDevPath(devPath, op);
964
reed@google.com819c9212011-02-23 18:56:55 +0000965 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966}
967
968bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000969 AutoValidateClip avc(this);
970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 fDeviceCMDirty = true;
972 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000973 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974
reed@google.com5c3d1472011-02-22 19:12:23 +0000975 // todo: signal fClipStack that we have a region, and therefore (I guess)
976 // we have to ignore it, and use the region directly?
977 fClipStack.clipDevRect(rgn.getBounds());
978
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 return fMCRec->fRegion->op(rgn, op);
980}
981
reed@google.com819c9212011-02-23 18:56:55 +0000982#ifdef SK_DEBUG
983void SkCanvas::validateClip() const {
984 // construct clipRgn from the clipstack
985 const SkDevice* device = this->getDevice();
986 SkIRect ir;
987 ir.set(0, 0, device->width(), device->height());
988 SkRegion clipRgn(ir);
989
990 SkClipStack::B2FIter iter(fClipStack);
991 const SkClipStack::B2FIter::Clip* clip;
992 while ((clip = iter.next()) != NULL) {
993 if (clip->fPath) {
994 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
995 } else if (clip->fRect) {
996 clip->fRect->round(&ir);
997 clipRgn.op(ir, clip->fOp);
998 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +0000999 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001000 }
1001 }
1002
reed@google.com6f8f2922011-03-04 22:27:10 +00001003#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001004 // now compare against the current rgn
1005 const SkRegion& rgn = this->getTotalClip();
1006 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001007#endif
reed@google.com819c9212011-02-23 18:56:55 +00001008}
1009#endif
1010
reed@google.com5c3d1472011-02-22 19:12:23 +00001011///////////////////////////////////////////////////////////////////////////////
1012
reed@android.comba09de42010-02-05 20:46:05 +00001013void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001015 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1016 fLocalBoundsCompareTypeBW;
1017
1018 if (!this->getClipBounds(&r, et)) {
1019 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001021 rCompare.set(SkScalarToCompareType(r.fLeft),
1022 SkScalarToCompareType(r.fTop),
1023 SkScalarToCompareType(r.fRight),
1024 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 }
1026}
1027
reed@android.comd252db02009-04-01 18:31:44 +00001028/* current impl ignores edgetype, and relies on
1029 getLocalClipBoundsCompareType(), which always returns a value assuming
1030 antialiasing (worst case)
1031 */
reed@android.comba09de42010-02-05 20:46:05 +00001032bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001033
1034 if (!rect.hasValidCoordinates())
1035 return true;
1036
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 if (fMCRec->fRegion->isEmpty()) {
1038 return true;
1039 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040
tomhudson@google.com8d430182011-06-06 19:11:19 +00001041 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001042 SkRect dst;
1043 fMCRec->fMatrix->mapRect(&dst, rect);
1044 SkIRect idst;
1045 dst.roundOut(&idst);
1046 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1047 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001048 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001049
reed@android.coma380ae42009-07-21 01:17:02 +00001050 // for speed, do the most likely reject compares first
1051 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1052 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1053 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1054 return true;
1055 }
1056 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1057 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1058 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1059 return true;
1060 }
1061 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063}
1064
1065bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001066 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067}
1068
1069bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1070 /* current impl ignores edgetype, and relies on
1071 getLocalClipBoundsCompareType(), which always returns a value assuming
1072 antialiasing (worst case)
1073 */
1074
1075 if (fMCRec->fRegion->isEmpty()) {
1076 return true;
1077 }
reed@google.com4b226022011-01-11 18:32:13 +00001078
reed@android.comaefd2bc2009-03-30 21:02:14 +00001079 SkScalarCompareType userT = SkScalarToCompareType(top);
1080 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001081
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001083 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 if (userT >= userB) {
1085 return true;
1086 }
reed@google.com4b226022011-01-11 18:32:13 +00001087
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 // check if we are above or below the local clip bounds
1089 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1090 return userT >= clipR.fBottom || userB <= clipR.fTop;
1091}
1092
1093bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1094 const SkRegion& clip = *fMCRec->fRegion;
1095 if (clip.isEmpty()) {
1096 if (bounds) {
1097 bounds->setEmpty();
1098 }
1099 return false;
1100 }
1101
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001102 SkMatrix inverse;
1103 // if we can't invert the CTM, we can't return local clip bounds
1104 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001105 if (bounds) {
1106 bounds->setEmpty();
1107 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001108 return false;
1109 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001111 if (NULL != bounds) {
1112 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 // get the clip's bounds
1114 const SkIRect& ibounds = clip.getBounds();
1115 // adjust it outwards if we are antialiasing
1116 int inset = (kAA_EdgeType == et);
1117 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1118 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001119
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 // invert into local coordinates
1121 inverse.mapRect(bounds, r);
1122 }
1123 return true;
1124}
1125
1126const SkMatrix& SkCanvas::getTotalMatrix() const {
1127 return *fMCRec->fMatrix;
1128}
1129
1130const SkRegion& SkCanvas::getTotalClip() const {
1131 return *fMCRec->fRegion;
1132}
1133
reed@google.com7d7ca792011-02-23 22:39:18 +00001134const SkClipStack& SkCanvas::getTotalClipStack() const {
1135 return fClipStack;
1136}
1137
reed@android.comf2b98d62010-12-20 18:26:13 +00001138void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1139 if (NULL == matrix || matrix->isIdentity()) {
1140 if (fUseExternalMatrix) {
1141 fDeviceCMDirty = true;
1142 }
1143 fUseExternalMatrix = false;
1144 } else {
1145 fUseExternalMatrix = true;
1146 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001147
reed@android.comf2b98d62010-12-20 18:26:13 +00001148 fExternalMatrix = *matrix;
1149 matrix->invert(&fExternalInverse);
1150 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001151}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152
bsalomon@google.come97f0852011-06-17 13:10:25 +00001153SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1154 int width, int height,
1155 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001156 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001157 if (device) {
1158 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1159 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001160 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001161 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001162 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001163}
1164
bsalomon@google.come97f0852011-06-17 13:10:25 +00001165SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1166 int width, int height,
1167 bool isOpaque) {
1168 SkDevice* device = this->getDevice();
1169 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001170 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001171 } else {
1172 return NULL;
1173 }
1174}
1175
1176
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177//////////////////////////////////////////////////////////////////////////////
1178// These are the virtual drawing methods
1179//////////////////////////////////////////////////////////////////////////////
1180
reed@google.com2a981812011-04-14 18:59:28 +00001181void SkCanvas::clear(SkColor color) {
1182 SkDrawIter iter(this);
1183
1184 while (iter.next()) {
1185 iter.fDevice->clear(color);
1186 }
1187}
1188
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189void SkCanvas::drawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001190 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191
1192 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001193 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 }
1195
reed@google.com4e2b3d32011-04-07 14:18:59 +00001196 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197}
1198
1199void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1200 const SkPaint& paint) {
1201 if ((long)count <= 0) {
1202 return;
1203 }
1204
1205 SkASSERT(pts != NULL);
1206
reed@google.com4e2b3d32011-04-07 14:18:59 +00001207 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001208
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001210 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 }
reed@google.com4b226022011-01-11 18:32:13 +00001212
reed@google.com4e2b3d32011-04-07 14:18:59 +00001213 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214}
1215
1216void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1217 if (paint.canComputeFastBounds()) {
1218 SkRect storage;
1219 if (this->quickReject(paint.computeFastBounds(r, &storage),
1220 paint2EdgeType(&paint))) {
1221 return;
1222 }
1223 }
reed@google.com4b226022011-01-11 18:32:13 +00001224
reed@google.com4e2b3d32011-04-07 14:18:59 +00001225 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226
1227 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001228 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 }
1230
reed@google.com4e2b3d32011-04-07 14:18:59 +00001231 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232}
1233
1234void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1235 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001236 SkRect storage;
1237 const SkRect& bounds = path.getBounds();
1238 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239 paint2EdgeType(&paint))) {
1240 return;
1241 }
1242 }
1243
reed@google.com4e2b3d32011-04-07 14:18:59 +00001244 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245
1246 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001247 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 }
1249
reed@google.com4e2b3d32011-04-07 14:18:59 +00001250 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251}
1252
1253void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1254 const SkPaint* paint) {
1255 SkDEBUGCODE(bitmap.validate();)
1256
1257 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1258 SkRect fastBounds;
1259 fastBounds.set(x, y,
1260 x + SkIntToScalar(bitmap.width()),
1261 y + SkIntToScalar(bitmap.height()));
1262 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1263 return;
1264 }
1265 }
reed@google.com4b226022011-01-11 18:32:13 +00001266
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267 SkMatrix matrix;
1268 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001269 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270}
1271
1272void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1273 const SkRect& dst, const SkPaint* paint) {
1274 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1275 return;
1276 }
reed@google.com4b226022011-01-11 18:32:13 +00001277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 // do this now, to avoid the cost of calling extract for RLE bitmaps
1279 if (this->quickReject(dst, paint2EdgeType(paint))) {
1280 return;
1281 }
reed@google.com4b226022011-01-11 18:32:13 +00001282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001284
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001286 SkRect tmpSrc;
1287 if (src) {
1288 tmpSrc.set(*src);
1289 // if the extract process clipped off the top or left of the
1290 // original, we adjust for that here to get the position right.
1291 if (tmpSrc.fLeft > 0) {
1292 tmpSrc.fRight -= tmpSrc.fLeft;
1293 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001294 }
reed@android.com87899992009-10-16 14:48:38 +00001295 if (tmpSrc.fTop > 0) {
1296 tmpSrc.fBottom -= tmpSrc.fTop;
1297 tmpSrc.fTop = 0;
1298 }
1299 } else {
1300 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1301 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 }
reed@android.com87899992009-10-16 14:48:38 +00001303 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001304
1305 // ensure that src is "valid" before we pass it to our internal routines
1306 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1307 SkIRect tmpISrc;
1308 if (src) {
1309 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001310 if (!tmpISrc.intersect(*src)) {
1311 return;
1312 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001313 src = &tmpISrc;
1314 }
1315 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316}
1317
1318void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1319 const SkPaint* paint) {
1320 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001321 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322}
1323
reed@android.comf2b98d62010-12-20 18:26:13 +00001324void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1325 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001327
reed@google.com4e2b3d32011-04-07 14:18:59 +00001328 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001329
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001331 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332 }
reed@android.com9b039062009-02-11 15:09:58 +00001333
reed@google.com4e2b3d32011-04-07 14:18:59 +00001334 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335}
1336
1337void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1338 const SkPaint* paint) {
1339 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001340
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 if (reject_bitmap(bitmap)) {
1342 return;
1343 }
reed@google.com4b226022011-01-11 18:32:13 +00001344
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345 SkPaint tmp;
1346 if (NULL == paint) {
1347 paint = &tmp;
1348 }
reed@google.com4b226022011-01-11 18:32:13 +00001349
reed@google.com4e2b3d32011-04-07 14:18:59 +00001350 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001351
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 while (iter.next()) {
1353 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001354 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001356 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357}
1358
reed@google.comf67e4cf2011-03-15 20:56:58 +00001359class SkDeviceFilteredPaint {
1360public:
1361 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1362 SkDevice::TextFlags flags;
1363 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001364 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001365 newPaint->setFlags(flags.fFlags);
1366 newPaint->setHinting(flags.fHinting);
1367 fPaint = newPaint;
1368 } else {
1369 fPaint = &paint;
1370 }
1371 }
1372
reed@google.comf67e4cf2011-03-15 20:56:58 +00001373 const SkPaint& paint() const { return *fPaint; }
1374
1375private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001376 const SkPaint* fPaint;
1377 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001378};
1379
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380void SkCanvas::drawText(const void* text, size_t byteLength,
1381 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001382 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383
1384 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001385 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001386 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387 }
1388
reed@google.com4e2b3d32011-04-07 14:18:59 +00001389 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390}
1391
1392void SkCanvas::drawPosText(const void* text, size_t byteLength,
1393 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001394 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001395
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001397 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001399 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 }
reed@google.com4b226022011-01-11 18:32:13 +00001401
reed@google.com4e2b3d32011-04-07 14:18:59 +00001402 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403}
1404
1405void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1406 const SkScalar xpos[], SkScalar constY,
1407 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001408 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001409
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001411 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001413 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 }
reed@google.com4b226022011-01-11 18:32:13 +00001415
reed@google.com4e2b3d32011-04-07 14:18:59 +00001416 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417}
1418
1419void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1420 const SkPath& path, const SkMatrix* matrix,
1421 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001422 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423
1424 while (iter.next()) {
1425 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001426 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 }
1428
reed@google.com4e2b3d32011-04-07 14:18:59 +00001429 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430}
1431
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001432#ifdef ANDROID
1433void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1434 const SkPoint pos[], const SkPaint& paint,
1435 const SkPath& path, const SkMatrix* matrix) {
1436
reed@google.com4e2b3d32011-04-07 14:18:59 +00001437 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001438
1439 while (iter.next()) {
1440 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001441 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001442 }
1443
reed@google.com4e2b3d32011-04-07 14:18:59 +00001444 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001445}
1446#endif
1447
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1449 const SkPoint verts[], const SkPoint texs[],
1450 const SkColor colors[], SkXfermode* xmode,
1451 const uint16_t indices[], int indexCount,
1452 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001453 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001454
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455 while (iter.next()) {
1456 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001457 colors, xmode, indices, indexCount,
1458 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459 }
reed@google.com4b226022011-01-11 18:32:13 +00001460
reed@google.com4e2b3d32011-04-07 14:18:59 +00001461 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462}
1463
reed@android.comcb608442009-12-04 21:32:27 +00001464void SkCanvas::drawData(const void* data, size_t length) {
1465 // do nothing. Subclasses may do something with the data
1466}
1467
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468//////////////////////////////////////////////////////////////////////////////
1469// These methods are NOT virtual, and therefore must call back into virtual
1470// methods, rather than actually drawing themselves.
1471//////////////////////////////////////////////////////////////////////////////
1472
1473void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001474 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475 SkPaint paint;
1476
1477 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001478 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001479 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480 }
1481 this->drawPaint(paint);
1482}
1483
reed@android.com845fdac2009-06-23 03:01:32 +00001484void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 SkPaint paint;
1486
1487 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001488 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001489 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490 }
1491 this->drawPaint(paint);
1492}
1493
1494void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1495 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001496
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497 pt.set(x, y);
1498 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1499}
1500
1501void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1502 SkPoint pt;
1503 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001504
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 pt.set(x, y);
1506 paint.setColor(color);
1507 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1508}
1509
1510void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1511 const SkPaint& paint) {
1512 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001513
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 pts[0].set(x0, y0);
1515 pts[1].set(x1, y1);
1516 this->drawPoints(kLines_PointMode, 2, pts, paint);
1517}
1518
1519void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1520 SkScalar right, SkScalar bottom,
1521 const SkPaint& paint) {
1522 SkRect r;
1523
1524 r.set(left, top, right, bottom);
1525 this->drawRect(r, paint);
1526}
1527
1528void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1529 const SkPaint& paint) {
1530 if (radius < 0) {
1531 radius = 0;
1532 }
1533
1534 SkRect r;
1535 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001536
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537 if (paint.canComputeFastBounds()) {
1538 SkRect storage;
1539 if (this->quickReject(paint.computeFastBounds(r, &storage),
1540 paint2EdgeType(&paint))) {
1541 return;
1542 }
1543 }
reed@google.com4b226022011-01-11 18:32:13 +00001544
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545 SkPath path;
1546 path.addOval(r);
1547 this->drawPath(path, paint);
1548}
1549
1550void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1551 const SkPaint& paint) {
1552 if (rx > 0 && ry > 0) {
1553 if (paint.canComputeFastBounds()) {
1554 SkRect storage;
1555 if (this->quickReject(paint.computeFastBounds(r, &storage),
1556 paint2EdgeType(&paint))) {
1557 return;
1558 }
1559 }
1560
1561 SkPath path;
1562 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1563 this->drawPath(path, paint);
1564 } else {
1565 this->drawRect(r, paint);
1566 }
1567}
1568
1569void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1570 if (paint.canComputeFastBounds()) {
1571 SkRect storage;
1572 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1573 paint2EdgeType(&paint))) {
1574 return;
1575 }
1576 }
1577
1578 SkPath path;
1579 path.addOval(oval);
1580 this->drawPath(path, paint);
1581}
1582
1583void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1584 SkScalar sweepAngle, bool useCenter,
1585 const SkPaint& paint) {
1586 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1587 this->drawOval(oval, paint);
1588 } else {
1589 SkPath path;
1590 if (useCenter) {
1591 path.moveTo(oval.centerX(), oval.centerY());
1592 }
1593 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1594 if (useCenter) {
1595 path.close();
1596 }
1597 this->drawPath(path, paint);
1598 }
1599}
1600
1601void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1602 const SkPath& path, SkScalar hOffset,
1603 SkScalar vOffset, const SkPaint& paint) {
1604 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001605
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606 matrix.setTranslate(hOffset, vOffset);
1607 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1608}
1609
reed@android.comf76bacf2009-05-13 14:00:33 +00001610///////////////////////////////////////////////////////////////////////////////
1611
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612void SkCanvas::drawPicture(SkPicture& picture) {
1613 int saveCount = save();
1614 picture.draw(this);
1615 restoreToCount(saveCount);
1616}
1617
reed@android.com8a1c16f2008-12-17 15:59:43 +00001618///////////////////////////////////////////////////////////////////////////////
1619///////////////////////////////////////////////////////////////////////////////
1620
1621SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001622 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623
1624 SkASSERT(canvas);
1625
1626 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1627 fDone = !fImpl->next();
1628}
1629
1630SkCanvas::LayerIter::~LayerIter() {
1631 fImpl->~SkDrawIter();
1632}
1633
1634void SkCanvas::LayerIter::next() {
1635 fDone = !fImpl->next();
1636}
1637
1638SkDevice* SkCanvas::LayerIter::device() const {
1639 return fImpl->getDevice();
1640}
1641
1642const SkMatrix& SkCanvas::LayerIter::matrix() const {
1643 return fImpl->getMatrix();
1644}
1645
1646const SkPaint& SkCanvas::LayerIter::paint() const {
1647 const SkPaint* paint = fImpl->getPaint();
1648 if (NULL == paint) {
1649 paint = &fDefaultPaint;
1650 }
1651 return *paint;
1652}
1653
1654const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1655int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1656int SkCanvas::LayerIter::y() const { return fImpl->getY(); }