blob: a8d218ae56e910c8185a398175d4c391c69f7c5e [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
475SkDevice* SkCanvas::setDevice(SkDevice* device) {
476 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000477 SkDeque::F2BIter iter(fMCStack);
478 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000479 SkASSERT(rec && rec->fLayer);
480 SkDevice* rootDevice = rec->fLayer->fDevice;
481
482 if (rootDevice == device) {
483 return device;
484 }
reed@google.com4b226022011-01-11 18:32:13 +0000485
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 /* Notify the devices that they are going in/out of scope, so they can do
487 things like lock/unlock their pixels, etc.
488 */
489 if (device) {
490 device->lockPixels();
491 }
492 if (rootDevice) {
493 rootDevice->unlockPixels();
494 }
495
496 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
497 rootDevice = device;
498
499 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000500
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 /* Now we update our initial region to have the bounds of the new device,
502 and then intersect all of the clips in our stack with these bounds,
503 to ensure that we can't draw outside of the device's bounds (and trash
504 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000505
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 NOTE: this is only a partial-fix, since if the new device is larger than
507 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000508 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
510 reconstruct the correct clips, so this approximation will have to do.
511 The caller really needs to restore() back to the base if they want to
512 accurately take advantage of the new device bounds.
513 */
514
515 if (NULL == device) {
516 rec->fRegion->setEmpty();
517 while ((rec = (MCRec*)iter.next()) != NULL) {
518 (void)rec->fRegion->setEmpty();
519 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000520 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 } else {
522 // compute our total bounds for all devices
523 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000524
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 bounds.set(0, 0, device->width(), device->height());
526
527 // now jam our 1st clip to be bounds, and intersect the rest with that
528 rec->fRegion->setRect(bounds);
529 while ((rec = (MCRec*)iter.next()) != NULL) {
530 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
531 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000532 fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 }
534 return device;
535}
536
reed@android.comf2b98d62010-12-20 18:26:13 +0000537SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap, bool forLayer) {
538 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (this, bitmap, forLayer)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 device->unref();
540 return device;
541}
542
reed@google.com51df9e32010-12-23 19:29:18 +0000543bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
544 SkDevice* device = this->getDevice();
545 if (!device) {
546 return false;
547 }
548 return device->readPixels(srcRect, bitmap);
549}
550
reed@google.com4b226022011-01-11 18:32:13 +0000551SkDeviceFactory* SkCanvas::setDeviceFactory(SkDeviceFactory* factory) {
552 SkDELETE(fDeviceFactory);
553 fDeviceFactory = factory;
reed@google.com9b2135a2011-01-11 19:45:38 +0000554 return factory;
reed@google.com4b226022011-01-11 18:32:13 +0000555}
556
557//////////////////////////////////////////////////////////////////////////////
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
578bool SkCanvas::getViewport(SkIPoint* size) const {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000579 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000580 return false;
581 if (size)
582 size->set(getDevice()->width(), getDevice()->height());
583 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584}
585
586bool SkCanvas::setViewport(int width, int height) {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000587 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000588 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000589
590 this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height,
591 false, false))->unref();
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000592 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593}
594
595void SkCanvas::updateDeviceCMCache() {
596 if (fDeviceCMDirty) {
597 const SkMatrix& totalMatrix = this->getTotalMatrix();
598 const SkRegion& totalClip = this->getTotalClip();
599 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000602 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000603 if (fUseExternalMatrix) {
604 layer->updateExternalMatrix(fExternalMatrix,
605 fExternalInverse);
606 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 } else {
608 SkRegion clip;
609 clip = totalClip; // make a copy
610 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000611 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000612 if (fUseExternalMatrix) {
613 layer->updateExternalMatrix(fExternalMatrix,
614 fExternalInverse);
615 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 } while ((layer = layer->fNext) != NULL);
617 }
618 fDeviceCMDirty = false;
619 }
620}
621
reed@android.comf2b98d62010-12-20 18:26:13 +0000622void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000623 const SkRegion& clip,
624 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000626 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000627 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000628 fLastDeviceToGainFocus = device;
629 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630}
631
632///////////////////////////////////////////////////////////////////////////////
633
634int SkCanvas::internalSave(SaveFlags flags) {
635 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000636
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 MCRec* newTop = (MCRec*)fMCStack.push_back();
638 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000639
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 newTop->fNext = fMCRec;
641 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000642
reed@google.com5c3d1472011-02-22 19:12:23 +0000643 fClipStack.save();
644 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 return saveCount;
647}
648
649int SkCanvas::save(SaveFlags flags) {
650 // call shared impl
651 return this->internalSave(flags);
652}
653
654#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
655#define C16MASK (1 << SkBitmap::kRGB_565_Config)
656#define C8MASK (1 << SkBitmap::kA8_Config)
657
658static SkBitmap::Config resolve_config(SkCanvas* canvas,
659 const SkIRect& bounds,
660 SkCanvas::SaveFlags flags,
661 bool* isOpaque) {
662 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
663
664#if 0
665 // loop through and union all the configs we may draw into
666 uint32_t configMask = 0;
667 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
668 {
669 SkDevice* device = canvas->getLayerDevice(i);
670 if (device->intersects(bounds))
671 configMask |= 1 << device->config();
672 }
673
674 // if the caller wants alpha or fullcolor, we can't return 565
675 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
676 SkCanvas::kHasAlphaLayer_SaveFlag))
677 configMask &= ~C16MASK;
678
679 switch (configMask) {
680 case C8MASK: // if we only have A8, return that
681 return SkBitmap::kA8_Config;
682
683 case C16MASK: // if we only have 565, return that
684 return SkBitmap::kRGB_565_Config;
685
686 default:
687 return SkBitmap::kARGB_8888_Config; // default answer
688 }
689#else
690 return SkBitmap::kARGB_8888_Config; // default answer
691#endif
692}
693
694static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
695 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
696}
697
698int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
699 SaveFlags flags) {
700 // do this before we create the layer. We don't call the public save() since
701 // that would invoke a possibly overridden virtual
702 int count = this->internalSave(flags);
703
704 fDeviceCMDirty = true;
705
706 SkIRect ir;
707 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000708 if (clipBounds.isEmpty()) {
709 return count;
710 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711
712 if (NULL != bounds) {
713 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 this->getTotalMatrix().mapRect(&r, *bounds);
716 r.roundOut(&ir);
717 // early exit if the layer's bounds are clipped out
718 if (!ir.intersect(clipBounds)) {
719 if (bounds_affects_clip(flags))
720 fMCRec->fRegion->setEmpty();
721 return count;
722 }
723 } else { // no user bounds, so just use the clip
724 ir = clipBounds;
725 }
726
reed@google.com5c3d1472011-02-22 19:12:23 +0000727 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 // early exit if the clip is now empty
729 if (bounds_affects_clip(flags) &&
730 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
731 return count;
732 }
733
734 bool isOpaque;
735 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
736
737 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
738 isOpaque, true);
reed@google.com6f8f2922011-03-04 22:27:10 +0000739 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
741 device->unref();
742
743 layer->fNext = fMCRec->fTopLayer;
744 fMCRec->fLayer = layer;
745 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
746
747 return count;
748}
749
750int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
751 SaveFlags flags) {
752 if (0xFF == alpha) {
753 return this->saveLayer(bounds, NULL, flags);
754 } else {
755 SkPaint tmpPaint;
756 tmpPaint.setAlpha(alpha);
757 return this->saveLayer(bounds, &tmpPaint, flags);
758 }
759}
760
761void SkCanvas::restore() {
762 // check for underflow
763 if (fMCStack.count() > 1) {
764 this->internalRestore();
765 }
766}
767
768void SkCanvas::internalRestore() {
769 SkASSERT(fMCStack.count() != 0);
770
771 fDeviceCMDirty = true;
772 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000773 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774
reed@google.com5c3d1472011-02-22 19:12:23 +0000775 fClipStack.restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 // reserve our layer (if any)
777 DeviceCM* layer = fMCRec->fLayer; // may be null
778 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
779 fMCRec->fLayer = NULL;
780
781 // now do the normal restore()
782 fMCRec->~MCRec(); // balanced in save()
783 fMCStack.pop_back();
784 fMCRec = (MCRec*)fMCStack.back();
785
786 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
787 since if we're being recorded, we don't want to record this (the
788 recorder will have already recorded the restore).
789 */
790 if (NULL != layer) {
791 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000792 const SkIPoint& origin = layer->fDevice->getOrigin();
793 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 layer->fPaint);
795 // reset this, since drawDevice will have set it to true
796 fDeviceCMDirty = true;
797 }
798 SkDELETE(layer);
799 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000800
801 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802}
803
804int SkCanvas::getSaveCount() const {
805 return fMCStack.count();
806}
807
808void SkCanvas::restoreToCount(int count) {
809 // sanity check
810 if (count < 1) {
811 count = 1;
812 }
813 while (fMCStack.count() > count) {
814 this->restore();
815 }
816}
817
818/////////////////////////////////////////////////////////////////////////////
819
820// can't draw it if its empty, or its too big for a fixed-point width or height
821static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000822 return bitmap.width() <= 0 || bitmap.height() <= 0
823#ifndef SK_ALLOW_OVER_32K_BITMAPS
824 || bitmap.width() > 32767 || bitmap.height() > 32767
825#endif
826 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827}
828
reed@android.comf2b98d62010-12-20 18:26:13 +0000829void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 const SkMatrix& matrix, const SkPaint* paint) {
831 if (reject_bitmap(bitmap)) {
832 return;
833 }
834
835 if (NULL == paint) {
836 SkPaint tmpPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000837 this->commonDrawBitmap(bitmap, srcRect, matrix, tmpPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 } else {
reed@android.comf2b98d62010-12-20 18:26:13 +0000839 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 }
841}
842
843void SkCanvas::drawDevice(SkDevice* device, int x, int y,
844 const SkPaint* paint) {
845 SkPaint tmp;
846 if (NULL == paint) {
847 tmp.setDither(true);
848 paint = &tmp;
849 }
reed@google.com4b226022011-01-11 18:32:13 +0000850
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
852 while (iter.next()) {
853 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
854 *paint);
855 }
856 ITER_END
857}
858
859/////////////////////////////////////////////////////////////////////////////
860
861bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
862 fDeviceCMDirty = true;
863 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000864 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 return fMCRec->fMatrix->preTranslate(dx, dy);
866}
867
868bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
869 fDeviceCMDirty = true;
870 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000871 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 return fMCRec->fMatrix->preScale(sx, sy);
873}
874
875bool SkCanvas::rotate(SkScalar degrees) {
876 fDeviceCMDirty = true;
877 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000878 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 return fMCRec->fMatrix->preRotate(degrees);
880}
881
882bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
883 fDeviceCMDirty = true;
884 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000885 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 return fMCRec->fMatrix->preSkew(sx, sy);
887}
888
889bool SkCanvas::concat(const SkMatrix& matrix) {
890 fDeviceCMDirty = true;
891 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000892 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 return fMCRec->fMatrix->preConcat(matrix);
894}
895
896void SkCanvas::setMatrix(const SkMatrix& matrix) {
897 fDeviceCMDirty = true;
898 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000899 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 *fMCRec->fMatrix = matrix;
901}
902
903// this is not virtual, so it must call a virtual method so that subclasses
904// will see its action
905void SkCanvas::resetMatrix() {
906 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000907
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 matrix.reset();
909 this->setMatrix(matrix);
910}
911
912//////////////////////////////////////////////////////////////////////////////
913
914bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000915 AutoValidateClip avc(this);
916
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 fDeviceCMDirty = true;
918 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000919 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920
921 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000922 // for these simpler matrices, we can stay a rect ever after applying
923 // the matrix. This means we don't have to a) make a path, and b) tell
924 // the region code to scan-convert the path, only to discover that it
925 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 SkRect r;
927 SkIRect ir;
928
929 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000930 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 r.round(&ir);
932 return fMCRec->fRegion->op(ir, op);
933 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000934 // since we're rotate or some such thing, we convert the rect to a path
935 // and clip against that, since it can handle any matrix. However, to
936 // avoid recursion in the case where we are subclassed (e.g. Pictures)
937 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 SkPath path;
939
940 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000941 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 }
943}
944
reed@google.com819c9212011-02-23 18:56:55 +0000945static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
946 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000947 // base is used to limit the size (and therefore memory allocation) of the
948 // region that results from scan converting devPath.
949 SkRegion base;
950
reed@google.com819c9212011-02-23 18:56:55 +0000951 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000952 // since we are intersect, we can do better (tighter) with currRgn's
953 // bounds, than just using the device. However, if currRgn is complex,
954 // our region blitter may hork, so we do that case in two steps.
955 if (currRgn->isRect()) {
956 return currRgn->setPath(devPath, *currRgn);
957 } else {
958 base.setRect(currRgn->getBounds());
959 SkRegion rgn;
960 rgn.setPath(devPath, base);
961 return currRgn->op(rgn, op);
962 }
reed@google.com819c9212011-02-23 18:56:55 +0000963 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000964 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
965 base.setRect(0, 0, bm.width(), bm.height());
966
967 if (SkRegion::kReplace_Op == op) {
968 return currRgn->setPath(devPath, base);
969 } else {
970 SkRegion rgn;
971 rgn.setPath(devPath, base);
972 return currRgn->op(rgn, op);
973 }
974 }
975}
976
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000978 AutoValidateClip avc(this);
979
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 fDeviceCMDirty = true;
981 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000982 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983
984 SkPath devPath;
985 path.transform(*fMCRec->fMatrix, &devPath);
986
reed@google.com5c3d1472011-02-22 19:12:23 +0000987 // if we called path.swap() we could avoid a deep copy of this path
988 fClipStack.clipDevPath(devPath, op);
989
reed@google.com819c9212011-02-23 18:56:55 +0000990 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991}
992
993bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000994 AutoValidateClip avc(this);
995
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 fDeviceCMDirty = true;
997 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000998 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999
reed@google.com5c3d1472011-02-22 19:12:23 +00001000 // todo: signal fClipStack that we have a region, and therefore (I guess)
1001 // we have to ignore it, and use the region directly?
1002 fClipStack.clipDevRect(rgn.getBounds());
1003
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 return fMCRec->fRegion->op(rgn, op);
1005}
1006
reed@google.com819c9212011-02-23 18:56:55 +00001007#ifdef SK_DEBUG
1008void SkCanvas::validateClip() const {
1009 // construct clipRgn from the clipstack
1010 const SkDevice* device = this->getDevice();
1011 SkIRect ir;
1012 ir.set(0, 0, device->width(), device->height());
1013 SkRegion clipRgn(ir);
1014
1015 SkClipStack::B2FIter iter(fClipStack);
1016 const SkClipStack::B2FIter::Clip* clip;
1017 while ((clip = iter.next()) != NULL) {
1018 if (clip->fPath) {
1019 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1020 } else if (clip->fRect) {
1021 clip->fRect->round(&ir);
1022 clipRgn.op(ir, clip->fOp);
1023 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001024 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001025 }
1026 }
1027
reed@google.com6f8f2922011-03-04 22:27:10 +00001028#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001029 // now compare against the current rgn
1030 const SkRegion& rgn = this->getTotalClip();
1031 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001032#endif
reed@google.com819c9212011-02-23 18:56:55 +00001033}
1034#endif
1035
reed@google.com5c3d1472011-02-22 19:12:23 +00001036///////////////////////////////////////////////////////////////////////////////
1037
reed@android.comba09de42010-02-05 20:46:05 +00001038void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001040 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1041 fLocalBoundsCompareTypeBW;
1042
1043 if (!this->getClipBounds(&r, et)) {
1044 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001046 rCompare.set(SkScalarToCompareType(r.fLeft),
1047 SkScalarToCompareType(r.fTop),
1048 SkScalarToCompareType(r.fRight),
1049 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 }
1051}
1052
reed@android.comd252db02009-04-01 18:31:44 +00001053/* current impl ignores edgetype, and relies on
1054 getLocalClipBoundsCompareType(), which always returns a value assuming
1055 antialiasing (worst case)
1056 */
reed@android.comba09de42010-02-05 20:46:05 +00001057bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001058
1059 if (!rect.hasValidCoordinates())
1060 return true;
1061
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 if (fMCRec->fRegion->isEmpty()) {
1063 return true;
1064 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065
reed@android.coma380ae42009-07-21 01:17:02 +00001066 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1067 SkRect dst;
1068 fMCRec->fMatrix->mapRect(&dst, rect);
1069 SkIRect idst;
1070 dst.roundOut(&idst);
1071 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1072 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001073 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001074
reed@android.coma380ae42009-07-21 01:17:02 +00001075 // for speed, do the most likely reject compares first
1076 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1077 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1078 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1079 return true;
1080 }
1081 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1082 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1083 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1084 return true;
1085 }
1086 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088}
1089
1090bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001091 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092}
1093
1094bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1095 /* current impl ignores edgetype, and relies on
1096 getLocalClipBoundsCompareType(), which always returns a value assuming
1097 antialiasing (worst case)
1098 */
1099
1100 if (fMCRec->fRegion->isEmpty()) {
1101 return true;
1102 }
reed@google.com4b226022011-01-11 18:32:13 +00001103
reed@android.comaefd2bc2009-03-30 21:02:14 +00001104 SkScalarCompareType userT = SkScalarToCompareType(top);
1105 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001106
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001108 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 if (userT >= userB) {
1110 return true;
1111 }
reed@google.com4b226022011-01-11 18:32:13 +00001112
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 // check if we are above or below the local clip bounds
1114 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1115 return userT >= clipR.fBottom || userB <= clipR.fTop;
1116}
1117
1118bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1119 const SkRegion& clip = *fMCRec->fRegion;
1120 if (clip.isEmpty()) {
1121 if (bounds) {
1122 bounds->setEmpty();
1123 }
1124 return false;
1125 }
1126
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001127 SkMatrix inverse;
1128 // if we can't invert the CTM, we can't return local clip bounds
1129 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001130 if (bounds) {
1131 bounds->setEmpty();
1132 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001133 return false;
1134 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001136 if (NULL != bounds) {
1137 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 // get the clip's bounds
1139 const SkIRect& ibounds = clip.getBounds();
1140 // adjust it outwards if we are antialiasing
1141 int inset = (kAA_EdgeType == et);
1142 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1143 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001144
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 // invert into local coordinates
1146 inverse.mapRect(bounds, r);
1147 }
1148 return true;
1149}
1150
1151const SkMatrix& SkCanvas::getTotalMatrix() const {
1152 return *fMCRec->fMatrix;
1153}
1154
1155const SkRegion& SkCanvas::getTotalClip() const {
1156 return *fMCRec->fRegion;
1157}
1158
reed@google.com7d7ca792011-02-23 22:39:18 +00001159const SkClipStack& SkCanvas::getTotalClipStack() const {
1160 return fClipStack;
1161}
1162
reed@android.comf2b98d62010-12-20 18:26:13 +00001163void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1164 if (NULL == matrix || matrix->isIdentity()) {
1165 if (fUseExternalMatrix) {
1166 fDeviceCMDirty = true;
1167 }
1168 fUseExternalMatrix = false;
1169 } else {
1170 fUseExternalMatrix = true;
1171 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001172
reed@android.comf2b98d62010-12-20 18:26:13 +00001173 fExternalMatrix = *matrix;
1174 matrix->invert(&fExternalInverse);
1175 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001176}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177
reed@android.comf2b98d62010-12-20 18:26:13 +00001178SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1179 bool isOpaque, bool forLayer) {
1180 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181}
1182
1183//////////////////////////////////////////////////////////////////////////////
1184// These are the virtual drawing methods
1185//////////////////////////////////////////////////////////////////////////////
1186
1187void SkCanvas::drawPaint(const SkPaint& paint) {
1188 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
1189
1190 while (iter.next()) {
1191 iter.fDevice->drawPaint(iter, paint);
1192 }
1193
1194 ITER_END
1195}
1196
1197void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1198 const SkPaint& paint) {
1199 if ((long)count <= 0) {
1200 return;
1201 }
1202
1203 SkASSERT(pts != NULL);
1204
1205 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001206
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 while (iter.next()) {
1208 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1209 }
reed@google.com4b226022011-01-11 18:32:13 +00001210
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 ITER_END
1212}
1213
1214void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1215 if (paint.canComputeFastBounds()) {
1216 SkRect storage;
1217 if (this->quickReject(paint.computeFastBounds(r, &storage),
1218 paint2EdgeType(&paint))) {
1219 return;
1220 }
1221 }
reed@google.com4b226022011-01-11 18:32:13 +00001222
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1224
1225 while (iter.next()) {
1226 iter.fDevice->drawRect(iter, r, paint);
1227 }
1228
1229 ITER_END
1230}
1231
1232void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1233 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001234 SkRect storage;
1235 const SkRect& bounds = path.getBounds();
1236 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 paint2EdgeType(&paint))) {
1238 return;
1239 }
1240 }
1241
1242 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1243
1244 while (iter.next()) {
1245 iter.fDevice->drawPath(iter, path, paint);
1246 }
1247
1248 ITER_END
1249}
1250
1251void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1252 const SkPaint* paint) {
1253 SkDEBUGCODE(bitmap.validate();)
1254
1255 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1256 SkRect fastBounds;
1257 fastBounds.set(x, y,
1258 x + SkIntToScalar(bitmap.width()),
1259 y + SkIntToScalar(bitmap.height()));
1260 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1261 return;
1262 }
1263 }
reed@google.com4b226022011-01-11 18:32:13 +00001264
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 SkMatrix matrix;
1266 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001267 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268}
1269
1270void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1271 const SkRect& dst, const SkPaint* paint) {
1272 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1273 return;
1274 }
reed@google.com4b226022011-01-11 18:32:13 +00001275
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276 // do this now, to avoid the cost of calling extract for RLE bitmaps
1277 if (this->quickReject(dst, paint2EdgeType(paint))) {
1278 return;
1279 }
reed@google.com4b226022011-01-11 18:32:13 +00001280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001282
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001284 SkRect tmpSrc;
1285 if (src) {
1286 tmpSrc.set(*src);
1287 // if the extract process clipped off the top or left of the
1288 // original, we adjust for that here to get the position right.
1289 if (tmpSrc.fLeft > 0) {
1290 tmpSrc.fRight -= tmpSrc.fLeft;
1291 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001292 }
reed@android.com87899992009-10-16 14:48:38 +00001293 if (tmpSrc.fTop > 0) {
1294 tmpSrc.fBottom -= tmpSrc.fTop;
1295 tmpSrc.fTop = 0;
1296 }
1297 } else {
1298 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1299 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 }
reed@android.com87899992009-10-16 14:48:38 +00001301 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001302
1303 // ensure that src is "valid" before we pass it to our internal routines
1304 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1305 SkIRect tmpISrc;
1306 if (src) {
1307 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
1308 tmpISrc.intersect(*src);
1309 src = &tmpISrc;
1310 }
1311 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312}
1313
1314void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1315 const SkPaint* paint) {
1316 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001317 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318}
1319
reed@android.comf2b98d62010-12-20 18:26:13 +00001320void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1321 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001323
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001325
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 while (iter.next()) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001327 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 }
reed@android.com9b039062009-02-11 15:09:58 +00001329
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 ITER_END
1331}
1332
1333void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1334 const SkPaint* paint) {
1335 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001336
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 if (reject_bitmap(bitmap)) {
1338 return;
1339 }
reed@google.com4b226022011-01-11 18:32:13 +00001340
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 SkPaint tmp;
1342 if (NULL == paint) {
1343 paint = &tmp;
1344 }
reed@google.com4b226022011-01-11 18:32:13 +00001345
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001347
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 while (iter.next()) {
1349 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1350 *paint);
1351 }
1352 ITER_END
1353}
1354
1355void SkCanvas::drawText(const void* text, size_t byteLength,
1356 SkScalar x, SkScalar y, const SkPaint& paint) {
1357 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1358
1359 while (iter.next()) {
1360 iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
1361 }
1362
1363 ITER_END
1364}
1365
1366void SkCanvas::drawPosText(const void* text, size_t byteLength,
1367 const SkPoint pos[], const SkPaint& paint) {
1368 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001369
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 while (iter.next()) {
1371 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
1372 paint);
1373 }
reed@google.com4b226022011-01-11 18:32:13 +00001374
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 ITER_END
1376}
1377
1378void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1379 const SkScalar xpos[], SkScalar constY,
1380 const SkPaint& paint) {
1381 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001382
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 while (iter.next()) {
1384 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
1385 paint);
1386 }
reed@google.com4b226022011-01-11 18:32:13 +00001387
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 ITER_END
1389}
1390
1391void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1392 const SkPath& path, const SkMatrix* matrix,
1393 const SkPaint& paint) {
1394 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1395
1396 while (iter.next()) {
1397 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1398 matrix, paint);
1399 }
1400
1401 ITER_END
1402}
1403
1404void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1405 const SkPoint verts[], const SkPoint texs[],
1406 const SkColor colors[], SkXfermode* xmode,
1407 const uint16_t indices[], int indexCount,
1408 const SkPaint& paint) {
1409 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001410
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411 while (iter.next()) {
1412 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1413 colors, xmode, indices, indexCount, paint);
1414 }
reed@google.com4b226022011-01-11 18:32:13 +00001415
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 ITER_END
1417}
1418
reed@android.comcb608442009-12-04 21:32:27 +00001419void SkCanvas::drawData(const void* data, size_t length) {
1420 // do nothing. Subclasses may do something with the data
1421}
1422
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423//////////////////////////////////////////////////////////////////////////////
1424// These methods are NOT virtual, and therefore must call back into virtual
1425// methods, rather than actually drawing themselves.
1426//////////////////////////////////////////////////////////////////////////////
1427
1428void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001429 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 SkPaint paint;
1431
1432 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001433 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001434 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 }
1436 this->drawPaint(paint);
1437}
1438
reed@android.com845fdac2009-06-23 03:01:32 +00001439void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 SkPaint paint;
1441
1442 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001443 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001444 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 }
1446 this->drawPaint(paint);
1447}
1448
1449void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1450 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001451
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 pt.set(x, y);
1453 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1454}
1455
1456void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1457 SkPoint pt;
1458 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001459
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 pt.set(x, y);
1461 paint.setColor(color);
1462 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1463}
1464
1465void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1466 const SkPaint& paint) {
1467 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001468
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 pts[0].set(x0, y0);
1470 pts[1].set(x1, y1);
1471 this->drawPoints(kLines_PointMode, 2, pts, paint);
1472}
1473
1474void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1475 SkScalar right, SkScalar bottom,
1476 const SkPaint& paint) {
1477 SkRect r;
1478
1479 r.set(left, top, right, bottom);
1480 this->drawRect(r, paint);
1481}
1482
1483void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1484 const SkPaint& paint) {
1485 if (radius < 0) {
1486 radius = 0;
1487 }
1488
1489 SkRect r;
1490 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001491
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492 if (paint.canComputeFastBounds()) {
1493 SkRect storage;
1494 if (this->quickReject(paint.computeFastBounds(r, &storage),
1495 paint2EdgeType(&paint))) {
1496 return;
1497 }
1498 }
reed@google.com4b226022011-01-11 18:32:13 +00001499
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500 SkPath path;
1501 path.addOval(r);
1502 this->drawPath(path, paint);
1503}
1504
1505void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1506 const SkPaint& paint) {
1507 if (rx > 0 && ry > 0) {
1508 if (paint.canComputeFastBounds()) {
1509 SkRect storage;
1510 if (this->quickReject(paint.computeFastBounds(r, &storage),
1511 paint2EdgeType(&paint))) {
1512 return;
1513 }
1514 }
1515
1516 SkPath path;
1517 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1518 this->drawPath(path, paint);
1519 } else {
1520 this->drawRect(r, paint);
1521 }
1522}
1523
1524void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1525 if (paint.canComputeFastBounds()) {
1526 SkRect storage;
1527 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1528 paint2EdgeType(&paint))) {
1529 return;
1530 }
1531 }
1532
1533 SkPath path;
1534 path.addOval(oval);
1535 this->drawPath(path, paint);
1536}
1537
1538void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1539 SkScalar sweepAngle, bool useCenter,
1540 const SkPaint& paint) {
1541 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1542 this->drawOval(oval, paint);
1543 } else {
1544 SkPath path;
1545 if (useCenter) {
1546 path.moveTo(oval.centerX(), oval.centerY());
1547 }
1548 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1549 if (useCenter) {
1550 path.close();
1551 }
1552 this->drawPath(path, paint);
1553 }
1554}
1555
1556void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1557 const SkPath& path, SkScalar hOffset,
1558 SkScalar vOffset, const SkPaint& paint) {
1559 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001560
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561 matrix.setTranslate(hOffset, vOffset);
1562 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1563}
1564
reed@android.comf76bacf2009-05-13 14:00:33 +00001565///////////////////////////////////////////////////////////////////////////////
1566
reed@android.com8a1c16f2008-12-17 15:59:43 +00001567void SkCanvas::drawPicture(SkPicture& picture) {
1568 int saveCount = save();
1569 picture.draw(this);
1570 restoreToCount(saveCount);
1571}
1572
reed@android.comf76bacf2009-05-13 14:00:33 +00001573void SkCanvas::drawShape(SkShape* shape) {
1574 // shape baseclass takes care of save/restore
1575 shape->draw(this);
1576}
1577
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578///////////////////////////////////////////////////////////////////////////////
1579///////////////////////////////////////////////////////////////////////////////
1580
1581SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001582 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583
1584 SkASSERT(canvas);
1585
1586 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1587 fDone = !fImpl->next();
1588}
1589
1590SkCanvas::LayerIter::~LayerIter() {
1591 fImpl->~SkDrawIter();
1592}
1593
1594void SkCanvas::LayerIter::next() {
1595 fDone = !fImpl->next();
1596}
1597
1598SkDevice* SkCanvas::LayerIter::device() const {
1599 return fImpl->getDevice();
1600}
1601
1602const SkMatrix& SkCanvas::LayerIter::matrix() const {
1603 return fImpl->getMatrix();
1604}
1605
1606const SkPaint& SkCanvas::LayerIter::paint() const {
1607 const SkPaint* paint = fImpl->getPaint();
1608 if (NULL == paint) {
1609 paint = &fDefaultPaint;
1610 }
1611 return *paint;
1612}
1613
1614const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1615int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1616int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1617