blob: e518b459e760a0986134ddbae9eb468b6696930b [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"
reed@android.comf76bacf2009-05-13 14:00:33 +000025#include "SkShape.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkTemplates.h"
27#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
53///////////////////////////////////////////////////////////////////////////////
54// Helpers for computing fast bounds for quickReject tests
55
56static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
57 return paint != NULL && paint->isAntiAlias() ?
58 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
59}
60
61///////////////////////////////////////////////////////////////////////////////
62
63/* This is the record we keep for each SkDevice that the user installs.
64 The clip/matrix/proc are fields that reflect the top of the save/restore
65 stack. Whenever the canvas changes, it marks a dirty flag, and then before
66 these are used (assuming we're not on a layer) we rebuild these cache
67 values: they reflect the top of the save stack, but translated and clipped
68 by the device's XY offset and bitmap-bounds.
69*/
70struct DeviceCM {
71 DeviceCM* fNext;
72 SkDevice* fDevice;
73 SkRegion fClip;
74 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000075 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000076 // optional, related to canvas' external matrix
77 const SkMatrix* fMVMatrix;
78 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000079
80 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
81 : fNext(NULL) {
82 if (NULL != device) {
83 device->ref();
84 device->lockPixels();
85 }
reed@google.com4b226022011-01-11 18:32:13 +000086 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
88 }
89
90 ~DeviceCM() {
91 if (NULL != fDevice) {
92 fDevice->unlockPixels();
93 fDevice->unref();
94 }
95 SkDELETE(fPaint);
96 }
reed@google.com4b226022011-01-11 18:32:13 +000097
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +000099 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000100 int x = fDevice->getOrigin().x();
101 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 int width = fDevice->width();
103 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000104
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 if ((x | y) == 0) {
106 fMatrix = &totalMatrix;
107 fClip = totalClip;
108 } else {
109 fMatrixStorage = totalMatrix;
110 fMatrixStorage.postTranslate(SkIntToScalar(-x),
111 SkIntToScalar(-y));
112 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 totalClip.translate(-x, -y, &fClip);
115 }
116
117 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
118
119 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000120
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 if (updateClip) {
122 updateClip->op(x, y, x + width, y + height,
123 SkRegion::kDifference_Op);
124 }
reed@google.com4b226022011-01-11 18:32:13 +0000125
reed@google.com46799cd2011-02-22 20:56:26 +0000126 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127
128#ifdef SK_DEBUG
129 if (!fClip.isEmpty()) {
130 SkIRect deviceR;
131 deviceR.set(0, 0, width, height);
132 SkASSERT(deviceR.contains(fClip.getBounds()));
133 }
134#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000135 // default is to assume no external matrix
136 fMVMatrix = NULL;
137 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000139
140 // can only be called after calling updateMC()
141 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
142 fMVMatrixStorage.setConcat(extI, *fMatrix);
143 fMVMatrix = &fMVMatrixStorage;
144 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
145 }
146
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000148 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149};
150
151/* This is the record we keep for each save/restore level in the stack.
152 Since a level optionally copies the matrix and/or stack, we have pointers
153 for these fields. If the value is copied for this level, the copy is
154 stored in the ...Storage field, and the pointer points to that. If the
155 value is not copied for this level, we ignore ...Storage, and just point
156 at the corresponding value in the previous level in the stack.
157*/
158class SkCanvas::MCRec {
159public:
160 MCRec* fNext;
161 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
162 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
163 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000164
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 DeviceCM* fLayer;
166 /* If there are any layers in the stack, this points to the top-most
167 one that is at or below this level in the stack (so we know what
168 bitmap/device to draw into from this level. This value is NOT
169 reference counted, since the real owner is either our fLayer field,
170 or a previous one in a lower level.)
171 */
172 DeviceCM* fTopLayer;
173
174 MCRec(const MCRec* prev, int flags) {
175 if (NULL != prev) {
176 if (flags & SkCanvas::kMatrix_SaveFlag) {
177 fMatrixStorage = *prev->fMatrix;
178 fMatrix = &fMatrixStorage;
179 } else {
180 fMatrix = prev->fMatrix;
181 }
reed@google.com4b226022011-01-11 18:32:13 +0000182
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 if (flags & SkCanvas::kClip_SaveFlag) {
184 fRegionStorage = *prev->fRegion;
185 fRegion = &fRegionStorage;
186 } else {
187 fRegion = prev->fRegion;
188 }
189
190 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000191 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192
193 fTopLayer = prev->fTopLayer;
194 } else { // no prev
195 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000196
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 fMatrix = &fMatrixStorage;
198 fRegion = &fRegionStorage;
199 fFilter = NULL;
200 fTopLayer = NULL;
201 }
202 fLayer = NULL;
203
204 // don't bother initializing fNext
205 inc_rec();
206 }
207 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000208 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209 SkDELETE(fLayer);
210 dec_rec();
211 }
reed@google.com4b226022011-01-11 18:32:13 +0000212
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213private:
214 SkMatrix fMatrixStorage;
215 SkRegion fRegionStorage;
216};
217
218class SkDrawIter : public SkDraw {
219public:
220 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
221 fCanvas = canvas;
222 canvas->updateDeviceCMCache();
223
reed@google.com7d7ca792011-02-23 22:39:18 +0000224 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 fBounder = canvas->getBounder();
226 fCurrLayer = canvas->fMCRec->fTopLayer;
227 fSkipEmptyClips = skipEmptyClips;
228 }
reed@google.com4b226022011-01-11 18:32:13 +0000229
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 bool next() {
231 // skip over recs with empty clips
232 if (fSkipEmptyClips) {
233 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
234 fCurrLayer = fCurrLayer->fNext;
235 }
236 }
237
238 if (NULL != fCurrLayer) {
239 const DeviceCM* rec = fCurrLayer;
240
241 fMatrix = rec->fMatrix;
242 fClip = &rec->fClip;
243 fDevice = rec->fDevice;
244 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000246 fMVMatrix = rec->fMVMatrix;
247 fExtMatrix = rec->fExtMatrix;
248 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249
250 fCurrLayer = rec->fNext;
251 if (fBounder) {
252 fBounder->setClip(fClip);
253 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000255
bsalomon@google.comd302f142011-03-03 13:54:13 +0000256 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 return true;
258 }
259 return false;
260 }
reed@google.com4b226022011-01-11 18:32:13 +0000261
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000263 int getX() const { return fDevice->getOrigin().x(); }
264 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 const SkMatrix& getMatrix() const { return *fMatrix; }
266 const SkRegion& getClip() const { return *fClip; }
267 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000268
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269private:
270 SkCanvas* fCanvas;
271 const DeviceCM* fCurrLayer;
272 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 SkBool8 fSkipEmptyClips;
274
275 typedef SkDraw INHERITED;
276};
277
278/////////////////////////////////////////////////////////////////////////////
279
280class AutoDrawLooper {
281public:
282 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, SkDrawFilter::Type t)
283 : fCanvas(canvas), fPaint((SkPaint*)&paint), fType(t) {
284 if ((fLooper = paint.getLooper()) != NULL) {
285 fLooper->init(canvas, (SkPaint*)&paint);
286 } else {
287 fOnce = true;
288 }
289 fFilter = canvas->getDrawFilter();
290 fNeedFilterRestore = false;
291 }
292
293 ~AutoDrawLooper() {
294 if (fNeedFilterRestore) {
295 SkASSERT(fFilter);
296 fFilter->restore(fCanvas, fPaint, fType);
297 }
298 if (NULL != fLooper) {
299 fLooper->restore();
300 }
301 }
reed@google.com4b226022011-01-11 18:32:13 +0000302
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 bool next() {
304 SkDrawFilter* filter = fFilter;
305
306 // if we drew earlier with a filter, then we need to restore first
307 if (fNeedFilterRestore) {
308 SkASSERT(filter);
309 filter->restore(fCanvas, fPaint, fType);
310 fNeedFilterRestore = false;
311 }
reed@google.com4b226022011-01-11 18:32:13 +0000312
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 bool result;
reed@google.com4b226022011-01-11 18:32:13 +0000314
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315 if (NULL != fLooper) {
316 result = fLooper->next();
317 } else {
318 result = fOnce;
319 fOnce = false;
320 }
321
322 // if we're gonna draw, give the filter a chance to do its work
323 if (result && NULL != filter) {
324 fNeedFilterRestore = result = filter->filter(fCanvas, fPaint,
325 fType);
326 }
327 return result;
328 }
reed@google.com4b226022011-01-11 18:32:13 +0000329
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330private:
331 SkDrawLooper* fLooper;
332 SkDrawFilter* fFilter;
333 SkCanvas* fCanvas;
334 SkPaint* fPaint;
335 SkDrawFilter::Type fType;
336 bool fOnce;
337 bool fNeedFilterRestore;
reed@google.com4b226022011-01-11 18:32:13 +0000338
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339};
340
341/* Stack helper for managing a SkBounder. In the destructor, if we were
342 given a bounder, we call its commit() method, signifying that we are
343 done accumulating bounds for that draw.
344*/
345class SkAutoBounderCommit {
346public:
347 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
348 ~SkAutoBounderCommit() {
349 if (NULL != fBounder) {
350 fBounder->commit();
351 }
352 }
353private:
354 SkBounder* fBounder;
355};
356
357#include "SkColorPriv.h"
358
359class AutoValidator {
360public:
361 AutoValidator(SkDevice* device) : fDevice(device) {}
362 ~AutoValidator() {
363#ifdef SK_DEBUG
364 const SkBitmap& bm = fDevice->accessBitmap(false);
365 if (bm.config() == SkBitmap::kARGB_4444_Config) {
366 for (int y = 0; y < bm.height(); y++) {
367 const SkPMColor16* p = bm.getAddr16(0, y);
368 for (int x = 0; x < bm.width(); x++) {
369 SkPMColor16 c = p[x];
370 SkPMColor16Assert(c);
371 }
372 }
373 }
374#endif
375 }
376private:
377 SkDevice* fDevice;
378};
379
380////////// macros to place around the internal draw calls //////////////////
381
382#define ITER_BEGIN(paint, type) \
383/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
384 AutoDrawLooper looper(this, paint, type); \
385 while (looper.next()) { \
386 SkAutoBounderCommit ac(fBounder); \
387 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000388
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389#define ITER_END }
390
391////////////////////////////////////////////////////////////////////////////
392
393SkDevice* SkCanvas::init(SkDevice* device) {
394 fBounder = NULL;
395 fLocalBoundsCompareTypeDirty = true;
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
reed@android.comf2b98d62010-12-20 18:26:13 +0000407 fUseExternalMatrix = false;
408
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 return this->setDevice(device);
410}
411
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000412SkCanvas::SkCanvas(SkDeviceFactory* factory)
413 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
414 fDeviceFactory(factory) {
415 inc_canvas();
416
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000417 if (!factory)
418 fDeviceFactory = SkNEW(SkRasterDeviceFactory);
419
420 this->init(NULL);
421}
422
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423SkCanvas::SkCanvas(SkDevice* device)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000424 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
425 fDeviceFactory(device->getDeviceFactory()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 inc_canvas();
427
428 this->init(device);
429}
430
431SkCanvas::SkCanvas(const SkBitmap& bitmap)
432 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
433 inc_canvas();
434
reed@android.comf2b98d62010-12-20 18:26:13 +0000435 SkDevice* device = SkNEW_ARGS(SkDevice, (this, bitmap, false));
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000436 fDeviceFactory = device->getDeviceFactory();
437 this->init(device)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438}
439
440SkCanvas::~SkCanvas() {
441 // free up the contents of our deque
442 this->restoreToCount(1); // restore everything but the last
443 this->internalRestore(); // restore the last, since we're going away
444
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000445 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000446 SkDELETE(fDeviceFactory);
447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 dec_canvas();
449}
450
451SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
452 SkRefCnt_SafeAssign(fBounder, bounder);
453 return bounder;
454}
455
456SkDrawFilter* SkCanvas::getDrawFilter() const {
457 return fMCRec->fFilter;
458}
459
460SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
461 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
462 return filter;
463}
464
465///////////////////////////////////////////////////////////////////////////////
466
467SkDevice* SkCanvas::getDevice() const {
468 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000469 SkDeque::F2BIter iter(fMCStack);
470 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000471 SkASSERT(rec && rec->fLayer);
472 return rec->fLayer->fDevice;
473}
474
reed@google.com9266fed2011-03-30 00:18:03 +0000475SkDevice* SkCanvas::getTopDevice() const {
476 return fMCRec->fTopLayer->fDevice;
477}
478
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479SkDevice* SkCanvas::setDevice(SkDevice* device) {
480 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000481 SkDeque::F2BIter iter(fMCStack);
482 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483 SkASSERT(rec && rec->fLayer);
484 SkDevice* rootDevice = rec->fLayer->fDevice;
485
486 if (rootDevice == device) {
487 return device;
488 }
reed@google.com4b226022011-01-11 18:32:13 +0000489
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 /* Notify the devices that they are going in/out of scope, so they can do
491 things like lock/unlock their pixels, etc.
492 */
493 if (device) {
494 device->lockPixels();
495 }
496 if (rootDevice) {
497 rootDevice->unlockPixels();
498 }
499
500 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
501 rootDevice = device;
502
503 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 /* Now we update our initial region to have the bounds of the new device,
506 and then intersect all of the clips in our stack with these bounds,
507 to ensure that we can't draw outside of the device's bounds (and trash
508 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000509
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 NOTE: this is only a partial-fix, since if the new device is larger than
511 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000512 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
514 reconstruct the correct clips, so this approximation will have to do.
515 The caller really needs to restore() back to the base if they want to
516 accurately take advantage of the new device bounds.
517 */
518
519 if (NULL == device) {
520 rec->fRegion->setEmpty();
521 while ((rec = (MCRec*)iter.next()) != NULL) {
522 (void)rec->fRegion->setEmpty();
523 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000524 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 } else {
526 // compute our total bounds for all devices
527 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 bounds.set(0, 0, device->width(), device->height());
530
531 // now jam our 1st clip to be bounds, and intersect the rest with that
532 rec->fRegion->setRect(bounds);
533 while ((rec = (MCRec*)iter.next()) != NULL) {
534 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
535 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000536 fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 }
538 return device;
539}
540
reed@android.comf2b98d62010-12-20 18:26:13 +0000541SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap, bool forLayer) {
542 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (this, bitmap, forLayer)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 device->unref();
544 return device;
545}
546
reed@google.com51df9e32010-12-23 19:29:18 +0000547bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
548 SkDevice* device = this->getDevice();
549 if (!device) {
550 return false;
551 }
552 return device->readPixels(srcRect, bitmap);
553}
554
reed@google.com4b226022011-01-11 18:32:13 +0000555SkDeviceFactory* SkCanvas::setDeviceFactory(SkDeviceFactory* factory) {
556 SkDELETE(fDeviceFactory);
557 fDeviceFactory = factory;
reed@google.com9b2135a2011-01-11 19:45:38 +0000558 return factory;
reed@google.com4b226022011-01-11 18:32:13 +0000559}
560
561//////////////////////////////////////////////////////////////////////////////
562
reed@google.com51df9e32010-12-23 19:29:18 +0000563bool SkCanvas::readPixels(SkBitmap* bitmap) {
564 SkDevice* device = this->getDevice();
565 if (!device) {
566 return false;
567 }
568 SkIRect bounds;
569 bounds.set(0, 0, device->width(), device->height());
570 return this->readPixels(bounds, bitmap);
571}
572
573void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
574 SkDevice* device = this->getDevice();
575 if (device) {
576 device->writePixels(bitmap, x, y);
577 }
578}
579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580//////////////////////////////////////////////////////////////////////////////
581
582bool SkCanvas::getViewport(SkIPoint* size) const {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000583 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000584 return false;
585 if (size)
586 size->set(getDevice()->width(), getDevice()->height());
587 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588}
589
590bool SkCanvas::setViewport(int width, int height) {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000591 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000592 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000593
594 this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height,
595 false, false))->unref();
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000596 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597}
598
599void SkCanvas::updateDeviceCMCache() {
600 if (fDeviceCMDirty) {
601 const SkMatrix& totalMatrix = this->getTotalMatrix();
602 const SkRegion& totalClip = this->getTotalClip();
603 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000606 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000607 if (fUseExternalMatrix) {
608 layer->updateExternalMatrix(fExternalMatrix,
609 fExternalInverse);
610 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 } else {
612 SkRegion clip;
613 clip = totalClip; // make a copy
614 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000615 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000616 if (fUseExternalMatrix) {
617 layer->updateExternalMatrix(fExternalMatrix,
618 fExternalInverse);
619 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 } while ((layer = layer->fNext) != NULL);
621 }
622 fDeviceCMDirty = false;
623 }
624}
625
reed@android.comf2b98d62010-12-20 18:26:13 +0000626void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000627 const SkRegion& clip,
628 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000630 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000631 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000632 fLastDeviceToGainFocus = device;
633 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000634}
635
636///////////////////////////////////////////////////////////////////////////////
637
638int SkCanvas::internalSave(SaveFlags flags) {
639 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000640
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641 MCRec* newTop = (MCRec*)fMCStack.push_back();
642 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000643
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 newTop->fNext = fMCRec;
645 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000646
reed@google.com5c3d1472011-02-22 19:12:23 +0000647 fClipStack.save();
648 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 return saveCount;
651}
652
653int SkCanvas::save(SaveFlags flags) {
654 // call shared impl
655 return this->internalSave(flags);
656}
657
658#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
659#define C16MASK (1 << SkBitmap::kRGB_565_Config)
660#define C8MASK (1 << SkBitmap::kA8_Config)
661
662static SkBitmap::Config resolve_config(SkCanvas* canvas,
663 const SkIRect& bounds,
664 SkCanvas::SaveFlags flags,
665 bool* isOpaque) {
666 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
667
668#if 0
669 // loop through and union all the configs we may draw into
670 uint32_t configMask = 0;
671 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
672 {
673 SkDevice* device = canvas->getLayerDevice(i);
674 if (device->intersects(bounds))
675 configMask |= 1 << device->config();
676 }
677
678 // if the caller wants alpha or fullcolor, we can't return 565
679 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
680 SkCanvas::kHasAlphaLayer_SaveFlag))
681 configMask &= ~C16MASK;
682
683 switch (configMask) {
684 case C8MASK: // if we only have A8, return that
685 return SkBitmap::kA8_Config;
686
687 case C16MASK: // if we only have 565, return that
688 return SkBitmap::kRGB_565_Config;
689
690 default:
691 return SkBitmap::kARGB_8888_Config; // default answer
692 }
693#else
694 return SkBitmap::kARGB_8888_Config; // default answer
695#endif
696}
697
698static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
699 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
700}
701
702int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
703 SaveFlags flags) {
704 // do this before we create the layer. We don't call the public save() since
705 // that would invoke a possibly overridden virtual
706 int count = this->internalSave(flags);
707
708 fDeviceCMDirty = true;
709
710 SkIRect ir;
711 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000712 if (clipBounds.isEmpty()) {
713 return count;
714 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715
716 if (NULL != bounds) {
717 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000718
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 this->getTotalMatrix().mapRect(&r, *bounds);
720 r.roundOut(&ir);
721 // early exit if the layer's bounds are clipped out
722 if (!ir.intersect(clipBounds)) {
723 if (bounds_affects_clip(flags))
724 fMCRec->fRegion->setEmpty();
725 return count;
726 }
727 } else { // no user bounds, so just use the clip
728 ir = clipBounds;
729 }
730
reed@google.com5c3d1472011-02-22 19:12:23 +0000731 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 // early exit if the clip is now empty
733 if (bounds_affects_clip(flags) &&
734 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
735 return count;
736 }
737
738 bool isOpaque;
739 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
740
741 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
742 isOpaque, true);
reed@google.com6f8f2922011-03-04 22:27:10 +0000743 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
745 device->unref();
746
747 layer->fNext = fMCRec->fTopLayer;
748 fMCRec->fLayer = layer;
749 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
750
751 return count;
752}
753
754int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
755 SaveFlags flags) {
756 if (0xFF == alpha) {
757 return this->saveLayer(bounds, NULL, flags);
758 } else {
759 SkPaint tmpPaint;
760 tmpPaint.setAlpha(alpha);
761 return this->saveLayer(bounds, &tmpPaint, flags);
762 }
763}
764
765void SkCanvas::restore() {
766 // check for underflow
767 if (fMCStack.count() > 1) {
768 this->internalRestore();
769 }
770}
771
772void SkCanvas::internalRestore() {
773 SkASSERT(fMCStack.count() != 0);
774
775 fDeviceCMDirty = true;
776 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000777 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778
reed@google.com5c3d1472011-02-22 19:12:23 +0000779 fClipStack.restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 // reserve our layer (if any)
781 DeviceCM* layer = fMCRec->fLayer; // may be null
782 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
783 fMCRec->fLayer = NULL;
784
785 // now do the normal restore()
786 fMCRec->~MCRec(); // balanced in save()
787 fMCStack.pop_back();
788 fMCRec = (MCRec*)fMCStack.back();
789
790 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
791 since if we're being recorded, we don't want to record this (the
792 recorder will have already recorded the restore).
793 */
794 if (NULL != layer) {
795 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000796 const SkIPoint& origin = layer->fDevice->getOrigin();
797 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 layer->fPaint);
799 // reset this, since drawDevice will have set it to true
800 fDeviceCMDirty = true;
801 }
802 SkDELETE(layer);
803 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000804
805 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000806}
807
808int SkCanvas::getSaveCount() const {
809 return fMCStack.count();
810}
811
812void SkCanvas::restoreToCount(int count) {
813 // sanity check
814 if (count < 1) {
815 count = 1;
816 }
817 while (fMCStack.count() > count) {
818 this->restore();
819 }
820}
821
822/////////////////////////////////////////////////////////////////////////////
823
824// can't draw it if its empty, or its too big for a fixed-point width or height
825static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000826 return bitmap.width() <= 0 || bitmap.height() <= 0
827#ifndef SK_ALLOW_OVER_32K_BITMAPS
828 || bitmap.width() > 32767 || bitmap.height() > 32767
829#endif
830 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831}
832
reed@android.comf2b98d62010-12-20 18:26:13 +0000833void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834 const SkMatrix& matrix, const SkPaint* paint) {
835 if (reject_bitmap(bitmap)) {
836 return;
837 }
838
839 if (NULL == paint) {
840 SkPaint tmpPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000841 this->commonDrawBitmap(bitmap, srcRect, matrix, tmpPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 } else {
reed@android.comf2b98d62010-12-20 18:26:13 +0000843 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 }
845}
846
847void SkCanvas::drawDevice(SkDevice* device, int x, int y,
848 const SkPaint* paint) {
849 SkPaint tmp;
850 if (NULL == paint) {
851 tmp.setDither(true);
852 paint = &tmp;
853 }
reed@google.com4b226022011-01-11 18:32:13 +0000854
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
856 while (iter.next()) {
857 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
858 *paint);
859 }
860 ITER_END
861}
862
863/////////////////////////////////////////////////////////////////////////////
864
865bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
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->preTranslate(dx, dy);
870}
871
872bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
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->preScale(sx, sy);
877}
878
879bool SkCanvas::rotate(SkScalar degrees) {
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 return fMCRec->fMatrix->preRotate(degrees);
884}
885
886bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
887 fDeviceCMDirty = true;
888 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000889 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000890 return fMCRec->fMatrix->preSkew(sx, sy);
891}
892
893bool SkCanvas::concat(const SkMatrix& matrix) {
894 fDeviceCMDirty = true;
895 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000896 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 return fMCRec->fMatrix->preConcat(matrix);
898}
899
900void SkCanvas::setMatrix(const SkMatrix& matrix) {
901 fDeviceCMDirty = true;
902 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000903 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 *fMCRec->fMatrix = matrix;
905}
906
907// this is not virtual, so it must call a virtual method so that subclasses
908// will see its action
909void SkCanvas::resetMatrix() {
910 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000911
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 matrix.reset();
913 this->setMatrix(matrix);
914}
915
916//////////////////////////////////////////////////////////////////////////////
917
918bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000919 AutoValidateClip avc(this);
920
reed@android.com8a1c16f2008-12-17 15:59:43 +0000921 fDeviceCMDirty = true;
922 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000923 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924
925 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000926 // for these simpler matrices, we can stay a rect ever after applying
927 // the matrix. This means we don't have to a) make a path, and b) tell
928 // the region code to scan-convert the path, only to discover that it
929 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 SkRect r;
931 SkIRect ir;
932
933 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000934 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 r.round(&ir);
936 return fMCRec->fRegion->op(ir, op);
937 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000938 // since we're rotate or some such thing, we convert the rect to a path
939 // and clip against that, since it can handle any matrix. However, to
940 // avoid recursion in the case where we are subclassed (e.g. Pictures)
941 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 SkPath path;
943
944 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000945 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 }
947}
948
reed@google.com819c9212011-02-23 18:56:55 +0000949static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
950 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000951 // base is used to limit the size (and therefore memory allocation) of the
952 // region that results from scan converting devPath.
953 SkRegion base;
954
reed@google.com819c9212011-02-23 18:56:55 +0000955 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000956 // since we are intersect, we can do better (tighter) with currRgn's
957 // bounds, than just using the device. However, if currRgn is complex,
958 // our region blitter may hork, so we do that case in two steps.
959 if (currRgn->isRect()) {
960 return currRgn->setPath(devPath, *currRgn);
961 } else {
962 base.setRect(currRgn->getBounds());
963 SkRegion rgn;
964 rgn.setPath(devPath, base);
965 return currRgn->op(rgn, op);
966 }
reed@google.com819c9212011-02-23 18:56:55 +0000967 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000968 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
969 base.setRect(0, 0, bm.width(), bm.height());
970
971 if (SkRegion::kReplace_Op == op) {
972 return currRgn->setPath(devPath, base);
973 } else {
974 SkRegion rgn;
975 rgn.setPath(devPath, base);
976 return currRgn->op(rgn, op);
977 }
978 }
979}
980
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000982 AutoValidateClip avc(this);
983
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 fDeviceCMDirty = true;
985 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000986 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987
988 SkPath devPath;
989 path.transform(*fMCRec->fMatrix, &devPath);
990
reed@google.com5c3d1472011-02-22 19:12:23 +0000991 // if we called path.swap() we could avoid a deep copy of this path
992 fClipStack.clipDevPath(devPath, op);
993
reed@google.com819c9212011-02-23 18:56:55 +0000994 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995}
996
997bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000998 AutoValidateClip avc(this);
999
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 fDeviceCMDirty = true;
1001 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001002 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003
reed@google.com5c3d1472011-02-22 19:12:23 +00001004 // todo: signal fClipStack that we have a region, and therefore (I guess)
1005 // we have to ignore it, and use the region directly?
1006 fClipStack.clipDevRect(rgn.getBounds());
1007
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 return fMCRec->fRegion->op(rgn, op);
1009}
1010
reed@google.com819c9212011-02-23 18:56:55 +00001011#ifdef SK_DEBUG
1012void SkCanvas::validateClip() const {
1013 // construct clipRgn from the clipstack
1014 const SkDevice* device = this->getDevice();
1015 SkIRect ir;
1016 ir.set(0, 0, device->width(), device->height());
1017 SkRegion clipRgn(ir);
1018
1019 SkClipStack::B2FIter iter(fClipStack);
1020 const SkClipStack::B2FIter::Clip* clip;
1021 while ((clip = iter.next()) != NULL) {
1022 if (clip->fPath) {
1023 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1024 } else if (clip->fRect) {
1025 clip->fRect->round(&ir);
1026 clipRgn.op(ir, clip->fOp);
1027 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001028 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001029 }
1030 }
1031
reed@google.com6f8f2922011-03-04 22:27:10 +00001032#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001033 // now compare against the current rgn
1034 const SkRegion& rgn = this->getTotalClip();
1035 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001036#endif
reed@google.com819c9212011-02-23 18:56:55 +00001037}
1038#endif
1039
reed@google.com5c3d1472011-02-22 19:12:23 +00001040///////////////////////////////////////////////////////////////////////////////
1041
reed@android.comba09de42010-02-05 20:46:05 +00001042void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001044 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1045 fLocalBoundsCompareTypeBW;
1046
1047 if (!this->getClipBounds(&r, et)) {
1048 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001050 rCompare.set(SkScalarToCompareType(r.fLeft),
1051 SkScalarToCompareType(r.fTop),
1052 SkScalarToCompareType(r.fRight),
1053 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054 }
1055}
1056
reed@android.comd252db02009-04-01 18:31:44 +00001057/* current impl ignores edgetype, and relies on
1058 getLocalClipBoundsCompareType(), which always returns a value assuming
1059 antialiasing (worst case)
1060 */
reed@android.comba09de42010-02-05 20:46:05 +00001061bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001062
1063 if (!rect.hasValidCoordinates())
1064 return true;
1065
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 if (fMCRec->fRegion->isEmpty()) {
1067 return true;
1068 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069
reed@android.coma380ae42009-07-21 01:17:02 +00001070 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1071 SkRect dst;
1072 fMCRec->fMatrix->mapRect(&dst, rect);
1073 SkIRect idst;
1074 dst.roundOut(&idst);
1075 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1076 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001077 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001078
reed@android.coma380ae42009-07-21 01:17:02 +00001079 // for speed, do the most likely reject compares first
1080 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1081 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1082 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1083 return true;
1084 }
1085 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1086 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1087 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1088 return true;
1089 }
1090 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092}
1093
1094bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001095 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096}
1097
1098bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1099 /* current impl ignores edgetype, and relies on
1100 getLocalClipBoundsCompareType(), which always returns a value assuming
1101 antialiasing (worst case)
1102 */
1103
1104 if (fMCRec->fRegion->isEmpty()) {
1105 return true;
1106 }
reed@google.com4b226022011-01-11 18:32:13 +00001107
reed@android.comaefd2bc2009-03-30 21:02:14 +00001108 SkScalarCompareType userT = SkScalarToCompareType(top);
1109 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001110
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001112 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 if (userT >= userB) {
1114 return true;
1115 }
reed@google.com4b226022011-01-11 18:32:13 +00001116
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 // check if we are above or below the local clip bounds
1118 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1119 return userT >= clipR.fBottom || userB <= clipR.fTop;
1120}
1121
1122bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1123 const SkRegion& clip = *fMCRec->fRegion;
1124 if (clip.isEmpty()) {
1125 if (bounds) {
1126 bounds->setEmpty();
1127 }
1128 return false;
1129 }
1130
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001131 SkMatrix inverse;
1132 // if we can't invert the CTM, we can't return local clip bounds
1133 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001134 if (bounds) {
1135 bounds->setEmpty();
1136 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001137 return false;
1138 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001140 if (NULL != bounds) {
1141 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 // get the clip's bounds
1143 const SkIRect& ibounds = clip.getBounds();
1144 // adjust it outwards if we are antialiasing
1145 int inset = (kAA_EdgeType == et);
1146 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1147 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001148
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 // invert into local coordinates
1150 inverse.mapRect(bounds, r);
1151 }
1152 return true;
1153}
1154
1155const SkMatrix& SkCanvas::getTotalMatrix() const {
1156 return *fMCRec->fMatrix;
1157}
1158
1159const SkRegion& SkCanvas::getTotalClip() const {
1160 return *fMCRec->fRegion;
1161}
1162
reed@google.com7d7ca792011-02-23 22:39:18 +00001163const SkClipStack& SkCanvas::getTotalClipStack() const {
1164 return fClipStack;
1165}
1166
reed@android.comf2b98d62010-12-20 18:26:13 +00001167void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1168 if (NULL == matrix || matrix->isIdentity()) {
1169 if (fUseExternalMatrix) {
1170 fDeviceCMDirty = true;
1171 }
1172 fUseExternalMatrix = false;
1173 } else {
1174 fUseExternalMatrix = true;
1175 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001176
reed@android.comf2b98d62010-12-20 18:26:13 +00001177 fExternalMatrix = *matrix;
1178 matrix->invert(&fExternalInverse);
1179 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001180}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181
reed@android.comf2b98d62010-12-20 18:26:13 +00001182SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1183 bool isOpaque, bool forLayer) {
1184 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185}
1186
1187//////////////////////////////////////////////////////////////////////////////
1188// These are the virtual drawing methods
1189//////////////////////////////////////////////////////////////////////////////
1190
1191void SkCanvas::drawPaint(const SkPaint& paint) {
1192 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
1193
1194 while (iter.next()) {
1195 iter.fDevice->drawPaint(iter, paint);
1196 }
1197
1198 ITER_END
1199}
1200
1201void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1202 const SkPaint& paint) {
1203 if ((long)count <= 0) {
1204 return;
1205 }
1206
1207 SkASSERT(pts != NULL);
1208
1209 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001210
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 while (iter.next()) {
1212 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1213 }
reed@google.com4b226022011-01-11 18:32:13 +00001214
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 ITER_END
1216}
1217
1218void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1219 if (paint.canComputeFastBounds()) {
1220 SkRect storage;
1221 if (this->quickReject(paint.computeFastBounds(r, &storage),
1222 paint2EdgeType(&paint))) {
1223 return;
1224 }
1225 }
reed@google.com4b226022011-01-11 18:32:13 +00001226
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1228
1229 while (iter.next()) {
1230 iter.fDevice->drawRect(iter, r, paint);
1231 }
1232
1233 ITER_END
1234}
1235
1236void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1237 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001238 SkRect storage;
1239 const SkRect& bounds = path.getBounds();
1240 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 paint2EdgeType(&paint))) {
1242 return;
1243 }
1244 }
1245
1246 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1247
1248 while (iter.next()) {
1249 iter.fDevice->drawPath(iter, path, paint);
1250 }
1251
1252 ITER_END
1253}
1254
1255void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1256 const SkPaint* paint) {
1257 SkDEBUGCODE(bitmap.validate();)
1258
1259 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1260 SkRect fastBounds;
1261 fastBounds.set(x, y,
1262 x + SkIntToScalar(bitmap.width()),
1263 y + SkIntToScalar(bitmap.height()));
1264 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1265 return;
1266 }
1267 }
reed@google.com4b226022011-01-11 18:32:13 +00001268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 SkMatrix matrix;
1270 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001271 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272}
1273
1274void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1275 const SkRect& dst, const SkPaint* paint) {
1276 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1277 return;
1278 }
reed@google.com4b226022011-01-11 18:32:13 +00001279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 // do this now, to avoid the cost of calling extract for RLE bitmaps
1281 if (this->quickReject(dst, paint2EdgeType(paint))) {
1282 return;
1283 }
reed@google.com4b226022011-01-11 18:32:13 +00001284
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001286
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001288 SkRect tmpSrc;
1289 if (src) {
1290 tmpSrc.set(*src);
1291 // if the extract process clipped off the top or left of the
1292 // original, we adjust for that here to get the position right.
1293 if (tmpSrc.fLeft > 0) {
1294 tmpSrc.fRight -= tmpSrc.fLeft;
1295 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001296 }
reed@android.com87899992009-10-16 14:48:38 +00001297 if (tmpSrc.fTop > 0) {
1298 tmpSrc.fBottom -= tmpSrc.fTop;
1299 tmpSrc.fTop = 0;
1300 }
1301 } else {
1302 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1303 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304 }
reed@android.com87899992009-10-16 14:48:38 +00001305 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001306
1307 // ensure that src is "valid" before we pass it to our internal routines
1308 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1309 SkIRect tmpISrc;
1310 if (src) {
1311 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001312 if (!tmpISrc.intersect(*src)) {
1313 return;
1314 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001315 src = &tmpISrc;
1316 }
1317 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318}
1319
1320void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1321 const SkPaint* paint) {
1322 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001323 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324}
1325
reed@android.comf2b98d62010-12-20 18:26:13 +00001326void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1327 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001329
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001331
reed@android.com8a1c16f2008-12-17 15:59:43 +00001332 while (iter.next()) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001333 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 }
reed@android.com9b039062009-02-11 15:09:58 +00001335
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336 ITER_END
1337}
1338
1339void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1340 const SkPaint* paint) {
1341 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001342
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 if (reject_bitmap(bitmap)) {
1344 return;
1345 }
reed@google.com4b226022011-01-11 18:32:13 +00001346
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 SkPaint tmp;
1348 if (NULL == paint) {
1349 paint = &tmp;
1350 }
reed@google.com4b226022011-01-11 18:32:13 +00001351
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001353
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 while (iter.next()) {
1355 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1356 *paint);
1357 }
1358 ITER_END
1359}
1360
reed@google.comf67e4cf2011-03-15 20:56:58 +00001361class SkDeviceFilteredPaint {
1362public:
1363 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1364 SkDevice::TextFlags flags;
1365 if (device->filterTextFlags(paint, &flags)) {
1366 SkPaint* newPaint = new (fStorage) SkPaint(paint);
1367 newPaint->setFlags(flags.fFlags);
1368 newPaint->setHinting(flags.fHinting);
1369 fPaint = newPaint;
1370 } else {
1371 fPaint = &paint;
1372 }
1373 }
1374
1375 ~SkDeviceFilteredPaint() {
1376 if (reinterpret_cast<SkPaint*>(fStorage) == fPaint) {
1377 fPaint->~SkPaint();
1378 }
1379 }
1380
1381 const SkPaint& paint() const { return *fPaint; }
1382
1383private:
1384 // points to either fStorage or the caller's paint
1385 const SkPaint* fPaint;
1386 // we rely on the fPaint above to ensure proper alignment of fStorage
1387 char fStorage[sizeof(SkPaint)];
1388};
1389
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390void SkCanvas::drawText(const void* text, size_t byteLength,
1391 SkScalar x, SkScalar y, const SkPaint& paint) {
1392 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1393
1394 while (iter.next()) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001395 SkDeviceFilteredPaint dfp(iter.fDevice, paint);
1396 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 }
1398
1399 ITER_END
1400}
1401
1402void SkCanvas::drawPosText(const void* text, size_t byteLength,
1403 const SkPoint pos[], const SkPaint& paint) {
1404 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001405
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 while (iter.next()) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001407 SkDeviceFilteredPaint dfp(iter.fDevice, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001409 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 }
reed@google.com4b226022011-01-11 18:32:13 +00001411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 ITER_END
1413}
1414
1415void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1416 const SkScalar xpos[], SkScalar constY,
1417 const SkPaint& paint) {
1418 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001419
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 while (iter.next()) {
reed@google.comf67e4cf2011-03-15 20:56:58 +00001421 SkDeviceFilteredPaint dfp(iter.fDevice, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001423 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 }
reed@google.com4b226022011-01-11 18:32:13 +00001425
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 ITER_END
1427}
1428
1429void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1430 const SkPath& path, const SkMatrix* matrix,
1431 const SkPaint& paint) {
1432 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1433
1434 while (iter.next()) {
1435 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1436 matrix, paint);
1437 }
1438
1439 ITER_END
1440}
1441
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001442#ifdef ANDROID
1443void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1444 const SkPoint pos[], const SkPaint& paint,
1445 const SkPath& path, const SkMatrix* matrix) {
1446
1447 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1448
1449 while (iter.next()) {
1450 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
1451 paint, path, matrix);
1452 }
1453
1454 ITER_END
1455}
1456#endif
1457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1459 const SkPoint verts[], const SkPoint texs[],
1460 const SkColor colors[], SkXfermode* xmode,
1461 const uint16_t indices[], int indexCount,
1462 const SkPaint& paint) {
1463 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001464
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 while (iter.next()) {
1466 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1467 colors, xmode, indices, indexCount, paint);
1468 }
reed@google.com4b226022011-01-11 18:32:13 +00001469
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470 ITER_END
1471}
1472
reed@android.comcb608442009-12-04 21:32:27 +00001473void SkCanvas::drawData(const void* data, size_t length) {
1474 // do nothing. Subclasses may do something with the data
1475}
1476
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477//////////////////////////////////////////////////////////////////////////////
1478// These methods are NOT virtual, and therefore must call back into virtual
1479// methods, rather than actually drawing themselves.
1480//////////////////////////////////////////////////////////////////////////////
1481
1482void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001483 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 SkPaint paint;
1485
1486 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001487 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001488 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 }
1490 this->drawPaint(paint);
1491}
1492
reed@android.com845fdac2009-06-23 03:01:32 +00001493void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 SkPaint paint;
1495
1496 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001497 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001498 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 }
1500 this->drawPaint(paint);
1501}
1502
1503void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1504 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001505
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 pt.set(x, y);
1507 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1508}
1509
1510void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1511 SkPoint pt;
1512 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001513
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 pt.set(x, y);
1515 paint.setColor(color);
1516 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1517}
1518
1519void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1520 const SkPaint& paint) {
1521 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001522
reed@android.com8a1c16f2008-12-17 15:59:43 +00001523 pts[0].set(x0, y0);
1524 pts[1].set(x1, y1);
1525 this->drawPoints(kLines_PointMode, 2, pts, paint);
1526}
1527
1528void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1529 SkScalar right, SkScalar bottom,
1530 const SkPaint& paint) {
1531 SkRect r;
1532
1533 r.set(left, top, right, bottom);
1534 this->drawRect(r, paint);
1535}
1536
1537void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1538 const SkPaint& paint) {
1539 if (radius < 0) {
1540 radius = 0;
1541 }
1542
1543 SkRect r;
1544 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001545
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546 if (paint.canComputeFastBounds()) {
1547 SkRect storage;
1548 if (this->quickReject(paint.computeFastBounds(r, &storage),
1549 paint2EdgeType(&paint))) {
1550 return;
1551 }
1552 }
reed@google.com4b226022011-01-11 18:32:13 +00001553
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 SkPath path;
1555 path.addOval(r);
1556 this->drawPath(path, paint);
1557}
1558
1559void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1560 const SkPaint& paint) {
1561 if (rx > 0 && ry > 0) {
1562 if (paint.canComputeFastBounds()) {
1563 SkRect storage;
1564 if (this->quickReject(paint.computeFastBounds(r, &storage),
1565 paint2EdgeType(&paint))) {
1566 return;
1567 }
1568 }
1569
1570 SkPath path;
1571 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1572 this->drawPath(path, paint);
1573 } else {
1574 this->drawRect(r, paint);
1575 }
1576}
1577
1578void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1579 if (paint.canComputeFastBounds()) {
1580 SkRect storage;
1581 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1582 paint2EdgeType(&paint))) {
1583 return;
1584 }
1585 }
1586
1587 SkPath path;
1588 path.addOval(oval);
1589 this->drawPath(path, paint);
1590}
1591
1592void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1593 SkScalar sweepAngle, bool useCenter,
1594 const SkPaint& paint) {
1595 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1596 this->drawOval(oval, paint);
1597 } else {
1598 SkPath path;
1599 if (useCenter) {
1600 path.moveTo(oval.centerX(), oval.centerY());
1601 }
1602 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1603 if (useCenter) {
1604 path.close();
1605 }
1606 this->drawPath(path, paint);
1607 }
1608}
1609
1610void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1611 const SkPath& path, SkScalar hOffset,
1612 SkScalar vOffset, const SkPaint& paint) {
1613 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001614
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615 matrix.setTranslate(hOffset, vOffset);
1616 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1617}
1618
reed@android.comf76bacf2009-05-13 14:00:33 +00001619///////////////////////////////////////////////////////////////////////////////
1620
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621void SkCanvas::drawPicture(SkPicture& picture) {
1622 int saveCount = save();
1623 picture.draw(this);
1624 restoreToCount(saveCount);
1625}
1626
reed@android.comf76bacf2009-05-13 14:00:33 +00001627void SkCanvas::drawShape(SkShape* shape) {
1628 // shape baseclass takes care of save/restore
1629 shape->draw(this);
1630}
1631
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632///////////////////////////////////////////////////////////////////////////////
1633///////////////////////////////////////////////////////////////////////////////
1634
1635SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001636 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637
1638 SkASSERT(canvas);
1639
1640 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1641 fDone = !fImpl->next();
1642}
1643
1644SkCanvas::LayerIter::~LayerIter() {
1645 fImpl->~SkDrawIter();
1646}
1647
1648void SkCanvas::LayerIter::next() {
1649 fDone = !fImpl->next();
1650}
1651
1652SkDevice* SkCanvas::LayerIter::device() const {
1653 return fImpl->getDevice();
1654}
1655
1656const SkMatrix& SkCanvas::LayerIter::matrix() const {
1657 return fImpl->getMatrix();
1658}
1659
1660const SkPaint& SkCanvas::LayerIter::paint() const {
1661 const SkPaint* paint = fImpl->getPaint();
1662 if (NULL == paint) {
1663 paint = &fDefaultPaint;
1664 }
1665 return *paint;
1666}
1667
1668const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1669int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1670int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1671