blob: 3c17351afde3969aba43505d8f20047b4c80cf76 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
2 * Copyright (C) 2006-2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkCanvas.h"
18#include "SkBounder.h"
19#include "SkDevice.h"
20#include "SkDraw.h"
21#include "SkDrawFilter.h"
22#include "SkDrawLooper.h"
23#include "SkPicture.h"
24#include "SkScalarCompare.h"
25#include "SkTemplates.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
28#include <new>
29
30//#define SK_TRACE_SAVERESTORE
31
32#ifdef SK_TRACE_SAVERESTORE
33 static int gLayerCounter;
34 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
35 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
36
37 static int gRecCounter;
38 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
39 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
40
41 static int gCanvasCounter;
42 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
43 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
44#else
45 #define inc_layer()
46 #define dec_layer()
47 #define inc_rec()
48 #define dec_rec()
49 #define inc_canvas()
50 #define dec_canvas()
51#endif
52
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000053typedef SkTLazy<SkPaint> SkLazyPaint;
54
reed@android.com8a1c16f2008-12-17 15:59:43 +000055///////////////////////////////////////////////////////////////////////////////
56// Helpers for computing fast bounds for quickReject tests
57
58static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
59 return paint != NULL && paint->isAntiAlias() ?
60 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
61}
62
63///////////////////////////////////////////////////////////////////////////////
64
65/* This is the record we keep for each SkDevice that the user installs.
66 The clip/matrix/proc are fields that reflect the top of the save/restore
67 stack. Whenever the canvas changes, it marks a dirty flag, and then before
68 these are used (assuming we're not on a layer) we rebuild these cache
69 values: they reflect the top of the save stack, but translated and clipped
70 by the device's XY offset and bitmap-bounds.
71*/
72struct DeviceCM {
73 DeviceCM* fNext;
74 SkDevice* fDevice;
75 SkRegion fClip;
76 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000077 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000078 // optional, related to canvas' external matrix
79 const SkMatrix* fMVMatrix;
80 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
82 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
83 : fNext(NULL) {
84 if (NULL != device) {
85 device->ref();
86 device->lockPixels();
87 }
reed@google.com4b226022011-01-11 18:32:13 +000088 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
90 }
91
92 ~DeviceCM() {
93 if (NULL != fDevice) {
94 fDevice->unlockPixels();
95 fDevice->unref();
96 }
97 SkDELETE(fPaint);
98 }
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +0000101 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000102 int x = fDevice->getOrigin().x();
103 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 int width = fDevice->width();
105 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 if ((x | y) == 0) {
108 fMatrix = &totalMatrix;
109 fClip = totalClip;
110 } else {
111 fMatrixStorage = totalMatrix;
112 fMatrixStorage.postTranslate(SkIntToScalar(-x),
113 SkIntToScalar(-y));
114 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 totalClip.translate(-x, -y, &fClip);
117 }
118
119 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
120
121 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000122
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 if (updateClip) {
124 updateClip->op(x, y, x + width, y + height,
125 SkRegion::kDifference_Op);
126 }
reed@google.com4b226022011-01-11 18:32:13 +0000127
reed@google.com46799cd2011-02-22 20:56:26 +0000128 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129
130#ifdef SK_DEBUG
131 if (!fClip.isEmpty()) {
132 SkIRect deviceR;
133 deviceR.set(0, 0, width, height);
134 SkASSERT(deviceR.contains(fClip.getBounds()));
135 }
136#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000137 // default is to assume no external matrix
138 fMVMatrix = NULL;
139 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000141
142 // can only be called after calling updateMC()
143 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
144 fMVMatrixStorage.setConcat(extI, *fMatrix);
145 fMVMatrix = &fMVMatrixStorage;
146 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
147 }
148
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000150 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151};
152
153/* This is the record we keep for each save/restore level in the stack.
154 Since a level optionally copies the matrix and/or stack, we have pointers
155 for these fields. If the value is copied for this level, the copy is
156 stored in the ...Storage field, and the pointer points to that. If the
157 value is not copied for this level, we ignore ...Storage, and just point
158 at the corresponding value in the previous level in the stack.
159*/
160class SkCanvas::MCRec {
161public:
162 MCRec* fNext;
163 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
164 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
165 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 DeviceCM* fLayer;
168 /* If there are any layers in the stack, this points to the top-most
169 one that is at or below this level in the stack (so we know what
170 bitmap/device to draw into from this level. This value is NOT
171 reference counted, since the real owner is either our fLayer field,
172 or a previous one in a lower level.)
173 */
174 DeviceCM* fTopLayer;
175
176 MCRec(const MCRec* prev, int flags) {
177 if (NULL != prev) {
178 if (flags & SkCanvas::kMatrix_SaveFlag) {
179 fMatrixStorage = *prev->fMatrix;
180 fMatrix = &fMatrixStorage;
181 } else {
182 fMatrix = prev->fMatrix;
183 }
reed@google.com4b226022011-01-11 18:32:13 +0000184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 if (flags & SkCanvas::kClip_SaveFlag) {
186 fRegionStorage = *prev->fRegion;
187 fRegion = &fRegionStorage;
188 } else {
189 fRegion = prev->fRegion;
190 }
191
192 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000193 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
195 fTopLayer = prev->fTopLayer;
196 } else { // no prev
197 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000198
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 fMatrix = &fMatrixStorage;
200 fRegion = &fRegionStorage;
201 fFilter = NULL;
202 fTopLayer = NULL;
203 }
204 fLayer = NULL;
205
206 // don't bother initializing fNext
207 inc_rec();
208 }
209 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000210 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 SkDELETE(fLayer);
212 dec_rec();
213 }
reed@google.com4b226022011-01-11 18:32:13 +0000214
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215private:
216 SkMatrix fMatrixStorage;
217 SkRegion fRegionStorage;
218};
219
220class SkDrawIter : public SkDraw {
221public:
222 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
223 fCanvas = canvas;
224 canvas->updateDeviceCMCache();
225
reed@google.com7d7ca792011-02-23 22:39:18 +0000226 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 fBounder = canvas->getBounder();
228 fCurrLayer = canvas->fMCRec->fTopLayer;
229 fSkipEmptyClips = skipEmptyClips;
230 }
reed@google.com4b226022011-01-11 18:32:13 +0000231
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 bool next() {
233 // skip over recs with empty clips
234 if (fSkipEmptyClips) {
235 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
236 fCurrLayer = fCurrLayer->fNext;
237 }
238 }
239
240 if (NULL != fCurrLayer) {
241 const DeviceCM* rec = fCurrLayer;
242
243 fMatrix = rec->fMatrix;
244 fClip = &rec->fClip;
245 fDevice = rec->fDevice;
246 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000248 fMVMatrix = rec->fMVMatrix;
249 fExtMatrix = rec->fExtMatrix;
250 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251
252 fCurrLayer = rec->fNext;
253 if (fBounder) {
254 fBounder->setClip(fClip);
255 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000257
bsalomon@google.comd302f142011-03-03 13:54:13 +0000258 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 return true;
260 }
261 return false;
262 }
reed@google.com4b226022011-01-11 18:32:13 +0000263
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000265 int getX() const { return fDevice->getOrigin().x(); }
266 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 const SkMatrix& getMatrix() const { return *fMatrix; }
268 const SkRegion& getClip() const { return *fClip; }
269 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000270
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271private:
272 SkCanvas* fCanvas;
273 const DeviceCM* fCurrLayer;
274 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 SkBool8 fSkipEmptyClips;
276
277 typedef SkDraw INHERITED;
278};
279
280/////////////////////////////////////////////////////////////////////////////
281
282class AutoDrawLooper {
283public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000284 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
285 fCanvas = canvas;
286 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 fPaint = NULL;
289 fSaveCount = canvas->getSaveCount();
290 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291
reed@google.com4e2b3d32011-04-07 14:18:59 +0000292 if (fLooper) {
293 fLooper->init(canvas);
294 }
295 }
296
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000298 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000300
301 const SkPaint& paint() const {
302 SkASSERT(fPaint);
303 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000305
306 bool next(SkDrawFilter::Type drawType);
307
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000309 SkLazyPaint fLazyPaint;
310 SkCanvas* fCanvas;
311 const SkPaint& fOrigPaint;
312 SkDrawLooper* fLooper;
313 SkDrawFilter* fFilter;
314 const SkPaint* fPaint;
315 int fSaveCount;
316 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317};
318
reed@google.com4e2b3d32011-04-07 14:18:59 +0000319bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
320 if (fDone) {
321 fPaint = NULL;
322 return false;
323 }
324 if (!fLooper && !fFilter) {
325 fDone = true;
326 fPaint = &fOrigPaint;
327 return true;
328 }
329
330 SkPaint* paint = fLazyPaint.set(fOrigPaint);
331 if (fLooper && !fLooper->next(fCanvas, paint)) {
332 fDone = true;
333 fPaint = NULL;
334 return false;
335 }
336 if (fFilter) {
337 fFilter->filter(paint, drawType);
mike@reedtribe.org53e3bed2011-04-08 00:37:03 +0000338 if (NULL == fLooper) {
339 // no looper means we only draw once
340 fDone = true;
341 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 }
343 fPaint = paint;
344 return true;
345}
346
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347/* Stack helper for managing a SkBounder. In the destructor, if we were
348 given a bounder, we call its commit() method, signifying that we are
349 done accumulating bounds for that draw.
350*/
351class SkAutoBounderCommit {
352public:
353 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
354 ~SkAutoBounderCommit() {
355 if (NULL != fBounder) {
356 fBounder->commit();
357 }
358 }
359private:
360 SkBounder* fBounder;
361};
362
363#include "SkColorPriv.h"
364
365class AutoValidator {
366public:
367 AutoValidator(SkDevice* device) : fDevice(device) {}
368 ~AutoValidator() {
369#ifdef SK_DEBUG
370 const SkBitmap& bm = fDevice->accessBitmap(false);
371 if (bm.config() == SkBitmap::kARGB_4444_Config) {
372 for (int y = 0; y < bm.height(); y++) {
373 const SkPMColor16* p = bm.getAddr16(0, y);
374 for (int x = 0; x < bm.width(); x++) {
375 SkPMColor16 c = p[x];
376 SkPMColor16Assert(c);
377 }
378 }
379 }
380#endif
381 }
382private:
383 SkDevice* fDevice;
384};
385
386////////// macros to place around the internal draw calls //////////////////
387
reed@google.com4e2b3d32011-04-07 14:18:59 +0000388#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000390 AutoDrawLooper looper(this, paint); \
391 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 SkAutoBounderCommit ac(fBounder); \
393 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000394
reed@google.com4e2b3d32011-04-07 14:18:59 +0000395#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396
397////////////////////////////////////////////////////////////////////////////
398
399SkDevice* SkCanvas::init(SkDevice* device) {
400 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000401 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000403 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000404 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000405 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000406 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407
408 fMCRec = (MCRec*)fMCStack.push_back();
409 new (fMCRec) MCRec(NULL, 0);
410
411 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
412 fMCRec->fTopLayer = fMCRec->fLayer;
413 fMCRec->fNext = NULL;
414
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000415 fExternalMatrix.reset();
416 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000417 fUseExternalMatrix = false;
418
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 return this->setDevice(device);
420}
421
reed@google.comcde92112011-07-06 20:00:52 +0000422SkCanvas::SkCanvas()
423: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000424 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000425
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000426 this->init(NULL);
427}
428
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000430 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 inc_canvas();
432
433 this->init(device);
434}
435
436SkCanvas::SkCanvas(const SkBitmap& bitmap)
437 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
438 inc_canvas();
439
reed@google.comcde92112011-07-06 20:00:52 +0000440 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441}
442
443SkCanvas::~SkCanvas() {
444 // free up the contents of our deque
445 this->restoreToCount(1); // restore everything but the last
446 this->internalRestore(); // restore the last, since we're going away
447
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000448 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 dec_canvas();
451}
452
453SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
454 SkRefCnt_SafeAssign(fBounder, bounder);
455 return bounder;
456}
457
458SkDrawFilter* SkCanvas::getDrawFilter() const {
459 return fMCRec->fFilter;
460}
461
462SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
463 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
464 return filter;
465}
466
467///////////////////////////////////////////////////////////////////////////////
468
469SkDevice* SkCanvas::getDevice() const {
470 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000471 SkDeque::F2BIter iter(fMCStack);
472 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473 SkASSERT(rec && rec->fLayer);
474 return rec->fLayer->fDevice;
475}
476
reed@google.com9266fed2011-03-30 00:18:03 +0000477SkDevice* SkCanvas::getTopDevice() const {
478 return fMCRec->fTopLayer->fDevice;
479}
480
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481SkDevice* SkCanvas::setDevice(SkDevice* device) {
482 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000483 SkDeque::F2BIter iter(fMCStack);
484 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 SkASSERT(rec && rec->fLayer);
486 SkDevice* rootDevice = rec->fLayer->fDevice;
487
488 if (rootDevice == device) {
489 return device;
490 }
reed@google.com4b226022011-01-11 18:32:13 +0000491
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 /* Notify the devices that they are going in/out of scope, so they can do
493 things like lock/unlock their pixels, etc.
494 */
495 if (device) {
496 device->lockPixels();
497 }
498 if (rootDevice) {
499 rootDevice->unlockPixels();
500 }
501
502 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
503 rootDevice = device;
504
505 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 /* Now we update our initial region to have the bounds of the new device,
508 and then intersect all of the clips in our stack with these bounds,
509 to ensure that we can't draw outside of the device's bounds (and trash
510 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 NOTE: this is only a partial-fix, since if the new device is larger than
513 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000514 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
516 reconstruct the correct clips, so this approximation will have to do.
517 The caller really needs to restore() back to the base if they want to
518 accurately take advantage of the new device bounds.
519 */
520
521 if (NULL == device) {
522 rec->fRegion->setEmpty();
523 while ((rec = (MCRec*)iter.next()) != NULL) {
524 (void)rec->fRegion->setEmpty();
525 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000526 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 } else {
528 // compute our total bounds for all devices
529 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000530
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 bounds.set(0, 0, device->width(), device->height());
532
533 // now jam our 1st clip to be bounds, and intersect the rest with that
534 rec->fRegion->setRect(bounds);
535 while ((rec = (MCRec*)iter.next()) != NULL) {
536 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
537 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000538 fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 }
540 return device;
541}
542
reed@google.comaf951c92011-06-16 19:10:39 +0000543SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
544 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 device->unref();
546 return device;
547}
548
reed@google.com51df9e32010-12-23 19:29:18 +0000549bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
550 SkDevice* device = this->getDevice();
551 if (!device) {
552 return false;
553 }
554 return device->readPixels(srcRect, bitmap);
555}
556
reed@google.com4b226022011-01-11 18:32:13 +0000557//////////////////////////////////////////////////////////////////////////////
558
reed@google.com51df9e32010-12-23 19:29:18 +0000559bool SkCanvas::readPixels(SkBitmap* bitmap) {
560 SkDevice* device = this->getDevice();
561 if (!device) {
562 return false;
563 }
564 SkIRect bounds;
565 bounds.set(0, 0, device->width(), device->height());
566 return this->readPixels(bounds, bitmap);
567}
568
569void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
570 SkDevice* device = this->getDevice();
571 if (device) {
572 device->writePixels(bitmap, x, y);
573 }
574}
575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576//////////////////////////////////////////////////////////////////////////////
577
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578void SkCanvas::updateDeviceCMCache() {
579 if (fDeviceCMDirty) {
580 const SkMatrix& totalMatrix = this->getTotalMatrix();
581 const SkRegion& totalClip = this->getTotalClip();
582 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000583
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000585 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000586 if (fUseExternalMatrix) {
587 layer->updateExternalMatrix(fExternalMatrix,
588 fExternalInverse);
589 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 } else {
591 SkRegion clip;
592 clip = totalClip; // make a copy
593 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000594 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000595 if (fUseExternalMatrix) {
596 layer->updateExternalMatrix(fExternalMatrix,
597 fExternalInverse);
598 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 } while ((layer = layer->fNext) != NULL);
600 }
601 fDeviceCMDirty = false;
602 }
603}
604
reed@android.comf2b98d62010-12-20 18:26:13 +0000605void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000606 const SkRegion& clip,
607 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000609 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000610 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000611 fLastDeviceToGainFocus = device;
612 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613}
614
615///////////////////////////////////////////////////////////////////////////////
616
617int SkCanvas::internalSave(SaveFlags flags) {
618 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000619
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 MCRec* newTop = (MCRec*)fMCStack.push_back();
621 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000622
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 newTop->fNext = fMCRec;
624 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000625
reed@google.com5c3d1472011-02-22 19:12:23 +0000626 fClipStack.save();
627 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
628
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 return saveCount;
630}
631
632int SkCanvas::save(SaveFlags flags) {
633 // call shared impl
634 return this->internalSave(flags);
635}
636
637#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
638#define C16MASK (1 << SkBitmap::kRGB_565_Config)
639#define C8MASK (1 << SkBitmap::kA8_Config)
640
641static SkBitmap::Config resolve_config(SkCanvas* canvas,
642 const SkIRect& bounds,
643 SkCanvas::SaveFlags flags,
644 bool* isOpaque) {
645 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
646
647#if 0
648 // loop through and union all the configs we may draw into
649 uint32_t configMask = 0;
650 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
651 {
652 SkDevice* device = canvas->getLayerDevice(i);
653 if (device->intersects(bounds))
654 configMask |= 1 << device->config();
655 }
656
657 // if the caller wants alpha or fullcolor, we can't return 565
658 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
659 SkCanvas::kHasAlphaLayer_SaveFlag))
660 configMask &= ~C16MASK;
661
662 switch (configMask) {
663 case C8MASK: // if we only have A8, return that
664 return SkBitmap::kA8_Config;
665
666 case C16MASK: // if we only have 565, return that
667 return SkBitmap::kRGB_565_Config;
668
669 default:
670 return SkBitmap::kARGB_8888_Config; // default answer
671 }
672#else
673 return SkBitmap::kARGB_8888_Config; // default answer
674#endif
675}
676
677static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
678 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
679}
680
681int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
682 SaveFlags flags) {
683 // do this before we create the layer. We don't call the public save() since
684 // that would invoke a possibly overridden virtual
685 int count = this->internalSave(flags);
686
687 fDeviceCMDirty = true;
688
689 SkIRect ir;
690 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000691 if (clipBounds.isEmpty()) {
692 return count;
693 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694
695 if (NULL != bounds) {
696 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000697
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698 this->getTotalMatrix().mapRect(&r, *bounds);
699 r.roundOut(&ir);
700 // early exit if the layer's bounds are clipped out
701 if (!ir.intersect(clipBounds)) {
702 if (bounds_affects_clip(flags))
703 fMCRec->fRegion->setEmpty();
704 return count;
705 }
706 } else { // no user bounds, so just use the clip
707 ir = clipBounds;
708 }
709
reed@google.com5c3d1472011-02-22 19:12:23 +0000710 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 // early exit if the clip is now empty
712 if (bounds_affects_clip(flags) &&
713 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
714 return count;
715 }
716
717 bool isOpaque;
718 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
719
bsalomon@google.come97f0852011-06-17 13:10:25 +0000720 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
721 isOpaque);
722
reed@google.com6f8f2922011-03-04 22:27:10 +0000723 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
725 device->unref();
726
727 layer->fNext = fMCRec->fTopLayer;
728 fMCRec->fLayer = layer;
729 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
730
731 return count;
732}
733
734int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
735 SaveFlags flags) {
736 if (0xFF == alpha) {
737 return this->saveLayer(bounds, NULL, flags);
738 } else {
739 SkPaint tmpPaint;
740 tmpPaint.setAlpha(alpha);
741 return this->saveLayer(bounds, &tmpPaint, flags);
742 }
743}
744
745void SkCanvas::restore() {
746 // check for underflow
747 if (fMCStack.count() > 1) {
748 this->internalRestore();
749 }
750}
751
752void SkCanvas::internalRestore() {
753 SkASSERT(fMCStack.count() != 0);
754
755 fDeviceCMDirty = true;
756 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000757 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758
reed@google.com5c3d1472011-02-22 19:12:23 +0000759 fClipStack.restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760 // reserve our layer (if any)
761 DeviceCM* layer = fMCRec->fLayer; // may be null
762 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
763 fMCRec->fLayer = NULL;
764
765 // now do the normal restore()
766 fMCRec->~MCRec(); // balanced in save()
767 fMCStack.pop_back();
768 fMCRec = (MCRec*)fMCStack.back();
769
770 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
771 since if we're being recorded, we don't want to record this (the
772 recorder will have already recorded the restore).
773 */
774 if (NULL != layer) {
775 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000776 const SkIPoint& origin = layer->fDevice->getOrigin();
777 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 layer->fPaint);
779 // reset this, since drawDevice will have set it to true
780 fDeviceCMDirty = true;
781 }
782 SkDELETE(layer);
783 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000784
785 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786}
787
788int SkCanvas::getSaveCount() const {
789 return fMCStack.count();
790}
791
792void SkCanvas::restoreToCount(int count) {
793 // sanity check
794 if (count < 1) {
795 count = 1;
796 }
797 while (fMCStack.count() > count) {
798 this->restore();
799 }
800}
801
802/////////////////////////////////////////////////////////////////////////////
803
804// can't draw it if its empty, or its too big for a fixed-point width or height
805static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000806 return bitmap.width() <= 0 || bitmap.height() <= 0
807#ifndef SK_ALLOW_OVER_32K_BITMAPS
808 || bitmap.width() > 32767 || bitmap.height() > 32767
809#endif
810 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811}
812
reed@android.comf2b98d62010-12-20 18:26:13 +0000813void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 const SkMatrix& matrix, const SkPaint* paint) {
815 if (reject_bitmap(bitmap)) {
816 return;
817 }
818
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000819 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000821 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000823 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824}
825
826void SkCanvas::drawDevice(SkDevice* device, int x, int y,
827 const SkPaint* paint) {
828 SkPaint tmp;
829 if (NULL == paint) {
830 tmp.setDither(true);
831 paint = &tmp;
832 }
reed@google.com4b226022011-01-11 18:32:13 +0000833
reed@google.com4e2b3d32011-04-07 14:18:59 +0000834 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 while (iter.next()) {
836 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000837 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000839 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840}
841
842/////////////////////////////////////////////////////////////////////////////
843
844bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
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->preTranslate(dx, dy);
849}
850
851bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
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->preScale(sx, sy);
856}
857
858bool SkCanvas::rotate(SkScalar degrees) {
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->preRotate(degrees);
863}
864
865bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
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->preSkew(sx, sy);
870}
871
872bool SkCanvas::concat(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 return fMCRec->fMatrix->preConcat(matrix);
877}
878
879void SkCanvas::setMatrix(const SkMatrix& matrix) {
880 fDeviceCMDirty = true;
881 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000882 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000883 *fMCRec->fMatrix = matrix;
884}
885
886// this is not virtual, so it must call a virtual method so that subclasses
887// will see its action
888void SkCanvas::resetMatrix() {
889 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000890
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 matrix.reset();
892 this->setMatrix(matrix);
893}
894
895//////////////////////////////////////////////////////////////////////////////
896
897bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000898 AutoValidateClip avc(this);
899
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 fDeviceCMDirty = true;
901 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000902 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903
904 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000905 // for these simpler matrices, we can stay a rect ever after applying
906 // the matrix. This means we don't have to a) make a path, and b) tell
907 // the region code to scan-convert the path, only to discover that it
908 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909 SkRect r;
910 SkIRect ir;
911
912 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000913 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000914 r.round(&ir);
915 return fMCRec->fRegion->op(ir, op);
916 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000917 // since we're rotate or some such thing, we convert the rect to a path
918 // and clip against that, since it can handle any matrix. However, to
919 // avoid recursion in the case where we are subclassed (e.g. Pictures)
920 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 SkPath path;
922
923 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000924 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 }
926}
927
reed@google.com819c9212011-02-23 18:56:55 +0000928static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
929 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000930 // base is used to limit the size (and therefore memory allocation) of the
931 // region that results from scan converting devPath.
932 SkRegion base;
933
reed@google.com819c9212011-02-23 18:56:55 +0000934 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000935 // since we are intersect, we can do better (tighter) with currRgn's
936 // bounds, than just using the device. However, if currRgn is complex,
937 // our region blitter may hork, so we do that case in two steps.
938 if (currRgn->isRect()) {
939 return currRgn->setPath(devPath, *currRgn);
940 } else {
941 base.setRect(currRgn->getBounds());
942 SkRegion rgn;
943 rgn.setPath(devPath, base);
944 return currRgn->op(rgn, op);
945 }
reed@google.com819c9212011-02-23 18:56:55 +0000946 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000947 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
948 base.setRect(0, 0, bm.width(), bm.height());
949
950 if (SkRegion::kReplace_Op == op) {
951 return currRgn->setPath(devPath, base);
952 } else {
953 SkRegion rgn;
954 rgn.setPath(devPath, base);
955 return currRgn->op(rgn, op);
956 }
957 }
958}
959
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000961 AutoValidateClip avc(this);
962
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 fDeviceCMDirty = true;
964 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000965 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966
967 SkPath devPath;
968 path.transform(*fMCRec->fMatrix, &devPath);
969
reed@google.com5c3d1472011-02-22 19:12:23 +0000970 // if we called path.swap() we could avoid a deep copy of this path
971 fClipStack.clipDevPath(devPath, op);
972
reed@google.com819c9212011-02-23 18:56:55 +0000973 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974}
975
976bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000977 AutoValidateClip avc(this);
978
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 fDeviceCMDirty = true;
980 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000981 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982
reed@google.com5c3d1472011-02-22 19:12:23 +0000983 // todo: signal fClipStack that we have a region, and therefore (I guess)
984 // we have to ignore it, and use the region directly?
985 fClipStack.clipDevRect(rgn.getBounds());
986
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 return fMCRec->fRegion->op(rgn, op);
988}
989
reed@google.com819c9212011-02-23 18:56:55 +0000990#ifdef SK_DEBUG
991void SkCanvas::validateClip() const {
992 // construct clipRgn from the clipstack
993 const SkDevice* device = this->getDevice();
994 SkIRect ir;
995 ir.set(0, 0, device->width(), device->height());
996 SkRegion clipRgn(ir);
997
998 SkClipStack::B2FIter iter(fClipStack);
999 const SkClipStack::B2FIter::Clip* clip;
1000 while ((clip = iter.next()) != NULL) {
1001 if (clip->fPath) {
1002 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1003 } else if (clip->fRect) {
1004 clip->fRect->round(&ir);
1005 clipRgn.op(ir, clip->fOp);
1006 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001007 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001008 }
1009 }
1010
reed@google.com6f8f2922011-03-04 22:27:10 +00001011#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001012 // now compare against the current rgn
1013 const SkRegion& rgn = this->getTotalClip();
1014 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001015#endif
reed@google.com819c9212011-02-23 18:56:55 +00001016}
1017#endif
1018
reed@google.com5c3d1472011-02-22 19:12:23 +00001019///////////////////////////////////////////////////////////////////////////////
1020
reed@android.comba09de42010-02-05 20:46:05 +00001021void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001022 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001023 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1024 fLocalBoundsCompareTypeBW;
1025
1026 if (!this->getClipBounds(&r, et)) {
1027 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001029 rCompare.set(SkScalarToCompareType(r.fLeft),
1030 SkScalarToCompareType(r.fTop),
1031 SkScalarToCompareType(r.fRight),
1032 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001033 }
1034}
1035
reed@android.comd252db02009-04-01 18:31:44 +00001036/* current impl ignores edgetype, and relies on
1037 getLocalClipBoundsCompareType(), which always returns a value assuming
1038 antialiasing (worst case)
1039 */
reed@android.comba09de42010-02-05 20:46:05 +00001040bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001041
1042 if (!rect.hasValidCoordinates())
1043 return true;
1044
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 if (fMCRec->fRegion->isEmpty()) {
1046 return true;
1047 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001048
tomhudson@google.com8d430182011-06-06 19:11:19 +00001049 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001050 SkRect dst;
1051 fMCRec->fMatrix->mapRect(&dst, rect);
1052 SkIRect idst;
1053 dst.roundOut(&idst);
1054 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1055 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001056 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001057
reed@android.coma380ae42009-07-21 01:17:02 +00001058 // for speed, do the most likely reject compares first
1059 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1060 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1061 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1062 return true;
1063 }
1064 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1065 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1066 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1067 return true;
1068 }
1069 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071}
1072
1073bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001074 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075}
1076
1077bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1078 /* current impl ignores edgetype, and relies on
1079 getLocalClipBoundsCompareType(), which always returns a value assuming
1080 antialiasing (worst case)
1081 */
1082
1083 if (fMCRec->fRegion->isEmpty()) {
1084 return true;
1085 }
reed@google.com4b226022011-01-11 18:32:13 +00001086
reed@android.comaefd2bc2009-03-30 21:02:14 +00001087 SkScalarCompareType userT = SkScalarToCompareType(top);
1088 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001089
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001091 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 if (userT >= userB) {
1093 return true;
1094 }
reed@google.com4b226022011-01-11 18:32:13 +00001095
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 // check if we are above or below the local clip bounds
1097 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1098 return userT >= clipR.fBottom || userB <= clipR.fTop;
1099}
1100
1101bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1102 const SkRegion& clip = *fMCRec->fRegion;
1103 if (clip.isEmpty()) {
1104 if (bounds) {
1105 bounds->setEmpty();
1106 }
1107 return false;
1108 }
1109
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001110 SkMatrix inverse;
1111 // if we can't invert the CTM, we can't return local clip bounds
1112 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001113 if (bounds) {
1114 bounds->setEmpty();
1115 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001116 return false;
1117 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001119 if (NULL != bounds) {
1120 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121 // get the clip's bounds
1122 const SkIRect& ibounds = clip.getBounds();
1123 // adjust it outwards if we are antialiasing
1124 int inset = (kAA_EdgeType == et);
1125 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1126 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001127
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 // invert into local coordinates
1129 inverse.mapRect(bounds, r);
1130 }
1131 return true;
1132}
1133
1134const SkMatrix& SkCanvas::getTotalMatrix() const {
1135 return *fMCRec->fMatrix;
1136}
1137
1138const SkRegion& SkCanvas::getTotalClip() const {
1139 return *fMCRec->fRegion;
1140}
1141
reed@google.com7d7ca792011-02-23 22:39:18 +00001142const SkClipStack& SkCanvas::getTotalClipStack() const {
1143 return fClipStack;
1144}
1145
reed@android.comf2b98d62010-12-20 18:26:13 +00001146void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1147 if (NULL == matrix || matrix->isIdentity()) {
1148 if (fUseExternalMatrix) {
1149 fDeviceCMDirty = true;
1150 }
1151 fUseExternalMatrix = false;
1152 } else {
1153 fUseExternalMatrix = true;
1154 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001155
reed@android.comf2b98d62010-12-20 18:26:13 +00001156 fExternalMatrix = *matrix;
1157 matrix->invert(&fExternalInverse);
1158 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001159}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160
bsalomon@google.come97f0852011-06-17 13:10:25 +00001161SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1162 int width, int height,
1163 bool isOpaque) {
reed@google.comcde92112011-07-06 20:00:52 +00001164 SkDevice* device = this->getDevice();
1165 if (device) {
1166 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1167 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001168 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001169 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001170 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171}
1172
bsalomon@google.come97f0852011-06-17 13:10:25 +00001173SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1174 int width, int height,
1175 bool isOpaque) {
1176 SkDevice* device = this->getDevice();
1177 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001178 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001179 } else {
1180 return NULL;
1181 }
1182}
1183
1184
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185//////////////////////////////////////////////////////////////////////////////
1186// These are the virtual drawing methods
1187//////////////////////////////////////////////////////////////////////////////
1188
reed@google.com2a981812011-04-14 18:59:28 +00001189void SkCanvas::clear(SkColor color) {
1190 SkDrawIter iter(this);
1191
1192 while (iter.next()) {
1193 iter.fDevice->clear(color);
1194 }
1195}
1196
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197void SkCanvas::drawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001198 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199
1200 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001201 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 }
1203
reed@google.com4e2b3d32011-04-07 14:18:59 +00001204 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205}
1206
1207void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1208 const SkPaint& paint) {
1209 if ((long)count <= 0) {
1210 return;
1211 }
1212
1213 SkASSERT(pts != NULL);
1214
reed@google.com4e2b3d32011-04-07 14:18:59 +00001215 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001216
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001218 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219 }
reed@google.com4b226022011-01-11 18:32:13 +00001220
reed@google.com4e2b3d32011-04-07 14:18:59 +00001221 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222}
1223
1224void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1225 if (paint.canComputeFastBounds()) {
1226 SkRect storage;
1227 if (this->quickReject(paint.computeFastBounds(r, &storage),
1228 paint2EdgeType(&paint))) {
1229 return;
1230 }
1231 }
reed@google.com4b226022011-01-11 18:32:13 +00001232
reed@google.com4e2b3d32011-04-07 14:18:59 +00001233 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234
1235 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001236 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 }
1238
reed@google.com4e2b3d32011-04-07 14:18:59 +00001239 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240}
1241
1242void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1243 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001244 SkRect storage;
1245 const SkRect& bounds = path.getBounds();
1246 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 paint2EdgeType(&paint))) {
1248 return;
1249 }
1250 }
1251
reed@google.com4e2b3d32011-04-07 14:18:59 +00001252 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253
1254 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001255 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 }
1257
reed@google.com4e2b3d32011-04-07 14:18:59 +00001258 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259}
1260
1261void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1262 const SkPaint* paint) {
1263 SkDEBUGCODE(bitmap.validate();)
1264
1265 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1266 SkRect fastBounds;
1267 fastBounds.set(x, y,
1268 x + SkIntToScalar(bitmap.width()),
1269 y + SkIntToScalar(bitmap.height()));
1270 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1271 return;
1272 }
1273 }
reed@google.com4b226022011-01-11 18:32:13 +00001274
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275 SkMatrix matrix;
1276 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001277 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278}
1279
1280void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1281 const SkRect& dst, const SkPaint* paint) {
1282 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1283 return;
1284 }
reed@google.com4b226022011-01-11 18:32:13 +00001285
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 // do this now, to avoid the cost of calling extract for RLE bitmaps
1287 if (this->quickReject(dst, paint2EdgeType(paint))) {
1288 return;
1289 }
reed@google.com4b226022011-01-11 18:32:13 +00001290
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001292
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001294 SkRect tmpSrc;
1295 if (src) {
1296 tmpSrc.set(*src);
1297 // if the extract process clipped off the top or left of the
1298 // original, we adjust for that here to get the position right.
1299 if (tmpSrc.fLeft > 0) {
1300 tmpSrc.fRight -= tmpSrc.fLeft;
1301 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001302 }
reed@android.com87899992009-10-16 14:48:38 +00001303 if (tmpSrc.fTop > 0) {
1304 tmpSrc.fBottom -= tmpSrc.fTop;
1305 tmpSrc.fTop = 0;
1306 }
1307 } else {
1308 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1309 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 }
reed@android.com87899992009-10-16 14:48:38 +00001311 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001312
1313 // ensure that src is "valid" before we pass it to our internal routines
1314 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1315 SkIRect tmpISrc;
1316 if (src) {
1317 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001318 if (!tmpISrc.intersect(*src)) {
1319 return;
1320 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001321 src = &tmpISrc;
1322 }
1323 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324}
1325
1326void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1327 const SkPaint* paint) {
1328 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001329 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330}
1331
reed@android.comf2b98d62010-12-20 18:26:13 +00001332void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1333 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001335
reed@google.com4e2b3d32011-04-07 14:18:59 +00001336 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001337
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001339 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 }
reed@android.com9b039062009-02-11 15:09:58 +00001341
reed@google.com4e2b3d32011-04-07 14:18:59 +00001342 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343}
1344
1345void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1346 const SkPaint* paint) {
1347 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001348
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 if (reject_bitmap(bitmap)) {
1350 return;
1351 }
reed@google.com4b226022011-01-11 18:32:13 +00001352
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 SkPaint tmp;
1354 if (NULL == paint) {
1355 paint = &tmp;
1356 }
reed@google.com4b226022011-01-11 18:32:13 +00001357
reed@google.com4e2b3d32011-04-07 14:18:59 +00001358 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001359
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 while (iter.next()) {
1361 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001362 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001364 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365}
1366
reed@google.comf67e4cf2011-03-15 20:56:58 +00001367class SkDeviceFilteredPaint {
1368public:
1369 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1370 SkDevice::TextFlags flags;
1371 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001372 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001373 newPaint->setFlags(flags.fFlags);
1374 newPaint->setHinting(flags.fHinting);
1375 fPaint = newPaint;
1376 } else {
1377 fPaint = &paint;
1378 }
1379 }
1380
reed@google.comf67e4cf2011-03-15 20:56:58 +00001381 const SkPaint& paint() const { return *fPaint; }
1382
1383private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001384 const SkPaint* fPaint;
1385 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001386};
1387
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388void SkCanvas::drawText(const void* text, size_t byteLength,
1389 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001390 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391
1392 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001393 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001394 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 }
1396
reed@google.com4e2b3d32011-04-07 14:18:59 +00001397 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398}
1399
1400void SkCanvas::drawPosText(const void* text, size_t byteLength,
1401 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001402 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001405 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001407 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 }
reed@google.com4b226022011-01-11 18:32:13 +00001409
reed@google.com4e2b3d32011-04-07 14:18:59 +00001410 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411}
1412
1413void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1414 const SkScalar xpos[], SkScalar constY,
1415 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001416 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001417
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001419 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001421 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 }
reed@google.com4b226022011-01-11 18:32:13 +00001423
reed@google.com4e2b3d32011-04-07 14:18:59 +00001424 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425}
1426
1427void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1428 const SkPath& path, const SkMatrix* matrix,
1429 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001430 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431
1432 while (iter.next()) {
1433 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001434 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 }
1436
reed@google.com4e2b3d32011-04-07 14:18:59 +00001437 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438}
1439
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001440#ifdef ANDROID
1441void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1442 const SkPoint pos[], const SkPaint& paint,
1443 const SkPath& path, const SkMatrix* matrix) {
1444
reed@google.com4e2b3d32011-04-07 14:18:59 +00001445 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001446
1447 while (iter.next()) {
1448 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001449 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001450 }
1451
reed@google.com4e2b3d32011-04-07 14:18:59 +00001452 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001453}
1454#endif
1455
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1457 const SkPoint verts[], const SkPoint texs[],
1458 const SkColor colors[], SkXfermode* xmode,
1459 const uint16_t indices[], int indexCount,
1460 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001461 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001462
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 while (iter.next()) {
1464 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001465 colors, xmode, indices, indexCount,
1466 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 }
reed@google.com4b226022011-01-11 18:32:13 +00001468
reed@google.com4e2b3d32011-04-07 14:18:59 +00001469 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470}
1471
reed@android.comcb608442009-12-04 21:32:27 +00001472void SkCanvas::drawData(const void* data, size_t length) {
1473 // do nothing. Subclasses may do something with the data
1474}
1475
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476//////////////////////////////////////////////////////////////////////////////
1477// These methods are NOT virtual, and therefore must call back into virtual
1478// methods, rather than actually drawing themselves.
1479//////////////////////////////////////////////////////////////////////////////
1480
1481void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001482 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 SkPaint paint;
1484
1485 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001486 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001487 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 }
1489 this->drawPaint(paint);
1490}
1491
reed@android.com845fdac2009-06-23 03:01:32 +00001492void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493 SkPaint paint;
1494
1495 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001496 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001497 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 }
1499 this->drawPaint(paint);
1500}
1501
1502void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1503 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001504
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 pt.set(x, y);
1506 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1507}
1508
1509void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1510 SkPoint pt;
1511 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001512
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 pt.set(x, y);
1514 paint.setColor(color);
1515 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1516}
1517
1518void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1519 const SkPaint& paint) {
1520 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001521
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522 pts[0].set(x0, y0);
1523 pts[1].set(x1, y1);
1524 this->drawPoints(kLines_PointMode, 2, pts, paint);
1525}
1526
1527void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1528 SkScalar right, SkScalar bottom,
1529 const SkPaint& paint) {
1530 SkRect r;
1531
1532 r.set(left, top, right, bottom);
1533 this->drawRect(r, paint);
1534}
1535
1536void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1537 const SkPaint& paint) {
1538 if (radius < 0) {
1539 radius = 0;
1540 }
1541
1542 SkRect r;
1543 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001544
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545 if (paint.canComputeFastBounds()) {
1546 SkRect storage;
1547 if (this->quickReject(paint.computeFastBounds(r, &storage),
1548 paint2EdgeType(&paint))) {
1549 return;
1550 }
1551 }
reed@google.com4b226022011-01-11 18:32:13 +00001552
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553 SkPath path;
1554 path.addOval(r);
1555 this->drawPath(path, paint);
1556}
1557
1558void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1559 const SkPaint& paint) {
1560 if (rx > 0 && ry > 0) {
1561 if (paint.canComputeFastBounds()) {
1562 SkRect storage;
1563 if (this->quickReject(paint.computeFastBounds(r, &storage),
1564 paint2EdgeType(&paint))) {
1565 return;
1566 }
1567 }
1568
1569 SkPath path;
1570 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1571 this->drawPath(path, paint);
1572 } else {
1573 this->drawRect(r, paint);
1574 }
1575}
1576
1577void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1578 if (paint.canComputeFastBounds()) {
1579 SkRect storage;
1580 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1581 paint2EdgeType(&paint))) {
1582 return;
1583 }
1584 }
1585
1586 SkPath path;
1587 path.addOval(oval);
1588 this->drawPath(path, paint);
1589}
1590
1591void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1592 SkScalar sweepAngle, bool useCenter,
1593 const SkPaint& paint) {
1594 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1595 this->drawOval(oval, paint);
1596 } else {
1597 SkPath path;
1598 if (useCenter) {
1599 path.moveTo(oval.centerX(), oval.centerY());
1600 }
1601 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1602 if (useCenter) {
1603 path.close();
1604 }
1605 this->drawPath(path, paint);
1606 }
1607}
1608
1609void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1610 const SkPath& path, SkScalar hOffset,
1611 SkScalar vOffset, const SkPaint& paint) {
1612 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001613
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614 matrix.setTranslate(hOffset, vOffset);
1615 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1616}
1617
reed@android.comf76bacf2009-05-13 14:00:33 +00001618///////////////////////////////////////////////////////////////////////////////
1619
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620void SkCanvas::drawPicture(SkPicture& picture) {
1621 int saveCount = save();
1622 picture.draw(this);
1623 restoreToCount(saveCount);
1624}
1625
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626///////////////////////////////////////////////////////////////////////////////
1627///////////////////////////////////////////////////////////////////////////////
1628
1629SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001630 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631
1632 SkASSERT(canvas);
1633
1634 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1635 fDone = !fImpl->next();
1636}
1637
1638SkCanvas::LayerIter::~LayerIter() {
1639 fImpl->~SkDrawIter();
1640}
1641
1642void SkCanvas::LayerIter::next() {
1643 fDone = !fImpl->next();
1644}
1645
1646SkDevice* SkCanvas::LayerIter::device() const {
1647 return fImpl->getDevice();
1648}
1649
1650const SkMatrix& SkCanvas::LayerIter::matrix() const {
1651 return fImpl->getMatrix();
1652}
1653
1654const SkPaint& SkCanvas::LayerIter::paint() const {
1655 const SkPaint* paint = fImpl->getPaint();
1656 if (NULL == paint) {
1657 paint = &fDefaultPaint;
1658 }
1659 return *paint;
1660}
1661
1662const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1663int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1664int SkCanvas::LayerIter::y() const { return fImpl->getY(); }