blob: 1ca31df93b5b6aa0774cde8e5d38a1889e02d991 [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;
75 SkPaint* fPaint; // may be null (in the future)
76 int16_t fX, fY; // relative to base matrix/clip
reed@android.comf2b98d62010-12-20 18:26:13 +000077 // optional, related to canvas' external matrix
78 const SkMatrix* fMVMatrix;
79 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
81 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
82 : fNext(NULL) {
83 if (NULL != device) {
84 device->ref();
85 device->lockPixels();
86 }
reed@google.com4b226022011-01-11 18:32:13 +000087 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 fX = SkToS16(x);
89 fY = SkToS16(y);
90 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
91 }
92
93 ~DeviceCM() {
94 if (NULL != fDevice) {
95 fDevice->unlockPixels();
96 fDevice->unref();
97 }
98 SkDELETE(fPaint);
99 }
reed@google.com4b226022011-01-11 18:32:13 +0000100
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +0000102 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 int x = fX;
104 int y = fY;
105 int width = fDevice->width();
106 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 if ((x | y) == 0) {
109 fMatrix = &totalMatrix;
110 fClip = totalClip;
111 } else {
112 fMatrixStorage = totalMatrix;
113 fMatrixStorage.postTranslate(SkIntToScalar(-x),
114 SkIntToScalar(-y));
115 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 totalClip.translate(-x, -y, &fClip);
118 }
119
120 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
121
122 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 if (updateClip) {
125 updateClip->op(x, y, x + width, y + height,
126 SkRegion::kDifference_Op);
127 }
reed@google.com4b226022011-01-11 18:32:13 +0000128
reed@google.com46799cd2011-02-22 20:56:26 +0000129 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130
131#ifdef SK_DEBUG
132 if (!fClip.isEmpty()) {
133 SkIRect deviceR;
134 deviceR.set(0, 0, width, height);
135 SkASSERT(deviceR.contains(fClip.getBounds()));
136 }
137#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000138 // default is to assume no external matrix
139 fMVMatrix = NULL;
140 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000142
143 // can only be called after calling updateMC()
144 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
145 fMVMatrixStorage.setConcat(extI, *fMatrix);
146 fMVMatrix = &fMVMatrixStorage;
147 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
148 }
149
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 void translateClip() {
151 if (fX | fY) {
152 fClip.translate(fX, fY);
153 }
154 }
155
156private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000157 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158};
159
160/* This is the record we keep for each save/restore level in the stack.
161 Since a level optionally copies the matrix and/or stack, we have pointers
162 for these fields. If the value is copied for this level, the copy is
163 stored in the ...Storage field, and the pointer points to that. If the
164 value is not copied for this level, we ignore ...Storage, and just point
165 at the corresponding value in the previous level in the stack.
166*/
167class SkCanvas::MCRec {
168public:
169 MCRec* fNext;
170 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
171 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
172 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000173
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 DeviceCM* fLayer;
175 /* If there are any layers in the stack, this points to the top-most
176 one that is at or below this level in the stack (so we know what
177 bitmap/device to draw into from this level. This value is NOT
178 reference counted, since the real owner is either our fLayer field,
179 or a previous one in a lower level.)
180 */
181 DeviceCM* fTopLayer;
182
183 MCRec(const MCRec* prev, int flags) {
184 if (NULL != prev) {
185 if (flags & SkCanvas::kMatrix_SaveFlag) {
186 fMatrixStorage = *prev->fMatrix;
187 fMatrix = &fMatrixStorage;
188 } else {
189 fMatrix = prev->fMatrix;
190 }
reed@google.com4b226022011-01-11 18:32:13 +0000191
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 if (flags & SkCanvas::kClip_SaveFlag) {
193 fRegionStorage = *prev->fRegion;
194 fRegion = &fRegionStorage;
195 } else {
196 fRegion = prev->fRegion;
197 }
198
199 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000200 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201
202 fTopLayer = prev->fTopLayer;
203 } else { // no prev
204 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000205
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 fMatrix = &fMatrixStorage;
207 fRegion = &fRegionStorage;
208 fFilter = NULL;
209 fTopLayer = NULL;
210 }
211 fLayer = NULL;
212
213 // don't bother initializing fNext
214 inc_rec();
215 }
216 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000217 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 SkDELETE(fLayer);
219 dec_rec();
220 }
reed@google.com4b226022011-01-11 18:32:13 +0000221
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222private:
223 SkMatrix fMatrixStorage;
224 SkRegion fRegionStorage;
225};
226
227class SkDrawIter : public SkDraw {
228public:
229 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
230 fCanvas = canvas;
231 canvas->updateDeviceCMCache();
232
233 fBounder = canvas->getBounder();
234 fCurrLayer = canvas->fMCRec->fTopLayer;
235 fSkipEmptyClips = skipEmptyClips;
236 }
reed@google.com4b226022011-01-11 18:32:13 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 bool next() {
239 // skip over recs with empty clips
240 if (fSkipEmptyClips) {
241 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
242 fCurrLayer = fCurrLayer->fNext;
243 }
244 }
245
246 if (NULL != fCurrLayer) {
247 const DeviceCM* rec = fCurrLayer;
248
249 fMatrix = rec->fMatrix;
250 fClip = &rec->fClip;
251 fDevice = rec->fDevice;
252 fBitmap = &fDevice->accessBitmap(true);
253 fLayerX = rec->fX;
254 fLayerY = rec->fY;
255 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000256 fMVMatrix = rec->fMVMatrix;
257 fExtMatrix = rec->fExtMatrix;
258 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259
260 fCurrLayer = rec->fNext;
261 if (fBounder) {
262 fBounder->setClip(fClip);
263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000265
reed@android.comf2b98d62010-12-20 18:26:13 +0000266 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 return true;
268 }
269 return false;
270 }
reed@google.com4b226022011-01-11 18:32:13 +0000271
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 int getX() const { return fLayerX; }
273 int getY() const { return fLayerY; }
274 SkDevice* getDevice() const { return fDevice; }
275 const SkMatrix& getMatrix() const { return *fMatrix; }
276 const SkRegion& getClip() const { return *fClip; }
277 const SkPaint* getPaint() const { return fPaint; }
278private:
279 SkCanvas* fCanvas;
280 const DeviceCM* fCurrLayer;
281 const SkPaint* fPaint; // May be null.
282 int fLayerX;
283 int fLayerY;
284 SkBool8 fSkipEmptyClips;
285
286 typedef SkDraw INHERITED;
287};
288
289/////////////////////////////////////////////////////////////////////////////
290
291class AutoDrawLooper {
292public:
293 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, SkDrawFilter::Type t)
294 : fCanvas(canvas), fPaint((SkPaint*)&paint), fType(t) {
295 if ((fLooper = paint.getLooper()) != NULL) {
296 fLooper->init(canvas, (SkPaint*)&paint);
297 } else {
298 fOnce = true;
299 }
300 fFilter = canvas->getDrawFilter();
301 fNeedFilterRestore = false;
302 }
303
304 ~AutoDrawLooper() {
305 if (fNeedFilterRestore) {
306 SkASSERT(fFilter);
307 fFilter->restore(fCanvas, fPaint, fType);
308 }
309 if (NULL != fLooper) {
310 fLooper->restore();
311 }
312 }
reed@google.com4b226022011-01-11 18:32:13 +0000313
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 bool next() {
315 SkDrawFilter* filter = fFilter;
316
317 // if we drew earlier with a filter, then we need to restore first
318 if (fNeedFilterRestore) {
319 SkASSERT(filter);
320 filter->restore(fCanvas, fPaint, fType);
321 fNeedFilterRestore = false;
322 }
reed@google.com4b226022011-01-11 18:32:13 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 bool result;
reed@google.com4b226022011-01-11 18:32:13 +0000325
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 if (NULL != fLooper) {
327 result = fLooper->next();
328 } else {
329 result = fOnce;
330 fOnce = false;
331 }
332
333 // if we're gonna draw, give the filter a chance to do its work
334 if (result && NULL != filter) {
335 fNeedFilterRestore = result = filter->filter(fCanvas, fPaint,
336 fType);
337 }
338 return result;
339 }
reed@google.com4b226022011-01-11 18:32:13 +0000340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341private:
342 SkDrawLooper* fLooper;
343 SkDrawFilter* fFilter;
344 SkCanvas* fCanvas;
345 SkPaint* fPaint;
346 SkDrawFilter::Type fType;
347 bool fOnce;
348 bool fNeedFilterRestore;
reed@google.com4b226022011-01-11 18:32:13 +0000349
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350};
351
352/* Stack helper for managing a SkBounder. In the destructor, if we were
353 given a bounder, we call its commit() method, signifying that we are
354 done accumulating bounds for that draw.
355*/
356class SkAutoBounderCommit {
357public:
358 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
359 ~SkAutoBounderCommit() {
360 if (NULL != fBounder) {
361 fBounder->commit();
362 }
363 }
364private:
365 SkBounder* fBounder;
366};
367
368#include "SkColorPriv.h"
369
370class AutoValidator {
371public:
372 AutoValidator(SkDevice* device) : fDevice(device) {}
373 ~AutoValidator() {
374#ifdef SK_DEBUG
375 const SkBitmap& bm = fDevice->accessBitmap(false);
376 if (bm.config() == SkBitmap::kARGB_4444_Config) {
377 for (int y = 0; y < bm.height(); y++) {
378 const SkPMColor16* p = bm.getAddr16(0, y);
379 for (int x = 0; x < bm.width(); x++) {
380 SkPMColor16 c = p[x];
381 SkPMColor16Assert(c);
382 }
383 }
384 }
385#endif
386 }
387private:
388 SkDevice* fDevice;
389};
390
391////////// macros to place around the internal draw calls //////////////////
392
393#define ITER_BEGIN(paint, type) \
394/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
395 AutoDrawLooper looper(this, paint, type); \
396 while (looper.next()) { \
397 SkAutoBounderCommit ac(fBounder); \
398 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000399
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400#define ITER_END }
401
402////////////////////////////////////////////////////////////////////////////
403
404SkDevice* SkCanvas::init(SkDevice* device) {
405 fBounder = NULL;
406 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000407 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000408 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000409 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410
411 fMCRec = (MCRec*)fMCStack.push_back();
412 new (fMCRec) MCRec(NULL, 0);
413
414 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
415 fMCRec->fTopLayer = fMCRec->fLayer;
416 fMCRec->fNext = NULL;
417
reed@android.comf2b98d62010-12-20 18:26:13 +0000418 fUseExternalMatrix = false;
419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 return this->setDevice(device);
421}
422
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000423SkCanvas::SkCanvas(SkDeviceFactory* factory)
424 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
425 fDeviceFactory(factory) {
426 inc_canvas();
427
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000428 if (!factory)
429 fDeviceFactory = SkNEW(SkRasterDeviceFactory);
430
431 this->init(NULL);
432}
433
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434SkCanvas::SkCanvas(SkDevice* device)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000435 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
436 fDeviceFactory(device->getDeviceFactory()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 inc_canvas();
438
439 this->init(device);
440}
441
442SkCanvas::SkCanvas(const SkBitmap& bitmap)
443 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
444 inc_canvas();
445
reed@android.comf2b98d62010-12-20 18:26:13 +0000446 SkDevice* device = SkNEW_ARGS(SkDevice, (this, bitmap, false));
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000447 fDeviceFactory = device->getDeviceFactory();
448 this->init(device)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449}
450
451SkCanvas::~SkCanvas() {
452 // free up the contents of our deque
453 this->restoreToCount(1); // restore everything but the last
454 this->internalRestore(); // restore the last, since we're going away
455
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000456 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000457 SkDELETE(fDeviceFactory);
458
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 dec_canvas();
460}
461
462SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
463 SkRefCnt_SafeAssign(fBounder, bounder);
464 return bounder;
465}
466
467SkDrawFilter* SkCanvas::getDrawFilter() const {
468 return fMCRec->fFilter;
469}
470
471SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
472 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
473 return filter;
474}
475
476///////////////////////////////////////////////////////////////////////////////
477
478SkDevice* SkCanvas::getDevice() const {
479 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000480 SkDeque::F2BIter iter(fMCStack);
481 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 SkASSERT(rec && rec->fLayer);
483 return rec->fLayer->fDevice;
484}
485
486SkDevice* SkCanvas::setDevice(SkDevice* device) {
487 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000488 SkDeque::F2BIter iter(fMCStack);
489 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 SkASSERT(rec && rec->fLayer);
491 SkDevice* rootDevice = rec->fLayer->fDevice;
492
493 if (rootDevice == device) {
494 return device;
495 }
reed@google.com4b226022011-01-11 18:32:13 +0000496
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 /* Notify the devices that they are going in/out of scope, so they can do
498 things like lock/unlock their pixels, etc.
499 */
500 if (device) {
501 device->lockPixels();
502 }
503 if (rootDevice) {
504 rootDevice->unlockPixels();
505 }
506
507 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
508 rootDevice = device;
509
510 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 /* Now we update our initial region to have the bounds of the new device,
513 and then intersect all of the clips in our stack with these bounds,
514 to ensure that we can't draw outside of the device's bounds (and trash
515 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 NOTE: this is only a partial-fix, since if the new device is larger than
518 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000519 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
521 reconstruct the correct clips, so this approximation will have to do.
522 The caller really needs to restore() back to the base if they want to
523 accurately take advantage of the new device bounds.
524 */
525
526 if (NULL == device) {
527 rec->fRegion->setEmpty();
528 while ((rec = (MCRec*)iter.next()) != NULL) {
529 (void)rec->fRegion->setEmpty();
530 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000531 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 } else {
533 // compute our total bounds for all devices
534 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 bounds.set(0, 0, device->width(), device->height());
537
538 // now jam our 1st clip to be bounds, and intersect the rest with that
539 rec->fRegion->setRect(bounds);
540 while ((rec = (MCRec*)iter.next()) != NULL) {
541 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
542 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000543 fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 }
545 return device;
546}
547
reed@android.comf2b98d62010-12-20 18:26:13 +0000548SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap, bool forLayer) {
549 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (this, bitmap, forLayer)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 device->unref();
551 return device;
552}
553
reed@google.com51df9e32010-12-23 19:29:18 +0000554bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
555 SkDevice* device = this->getDevice();
556 if (!device) {
557 return false;
558 }
559 return device->readPixels(srcRect, bitmap);
560}
561
reed@google.com4b226022011-01-11 18:32:13 +0000562SkDeviceFactory* SkCanvas::setDeviceFactory(SkDeviceFactory* factory) {
563 SkDELETE(fDeviceFactory);
564 fDeviceFactory = factory;
reed@google.com9b2135a2011-01-11 19:45:38 +0000565 return factory;
reed@google.com4b226022011-01-11 18:32:13 +0000566}
567
568//////////////////////////////////////////////////////////////////////////////
569
reed@google.com51df9e32010-12-23 19:29:18 +0000570bool SkCanvas::readPixels(SkBitmap* bitmap) {
571 SkDevice* device = this->getDevice();
572 if (!device) {
573 return false;
574 }
575 SkIRect bounds;
576 bounds.set(0, 0, device->width(), device->height());
577 return this->readPixels(bounds, bitmap);
578}
579
580void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
581 SkDevice* device = this->getDevice();
582 if (device) {
583 device->writePixels(bitmap, x, y);
584 }
585}
586
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587//////////////////////////////////////////////////////////////////////////////
588
589bool SkCanvas::getViewport(SkIPoint* size) const {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000590 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000591 return false;
592 if (size)
593 size->set(getDevice()->width(), getDevice()->height());
594 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595}
596
597bool SkCanvas::setViewport(int width, int height) {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000598 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000599 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000600
601 this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height,
602 false, false))->unref();
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000603 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604}
605
606void SkCanvas::updateDeviceCMCache() {
607 if (fDeviceCMDirty) {
608 const SkMatrix& totalMatrix = this->getTotalMatrix();
609 const SkRegion& totalClip = this->getTotalClip();
610 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000613 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000614 if (fUseExternalMatrix) {
615 layer->updateExternalMatrix(fExternalMatrix,
616 fExternalInverse);
617 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 } else {
619 SkRegion clip;
620 clip = totalClip; // make a copy
621 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000622 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000623 if (fUseExternalMatrix) {
624 layer->updateExternalMatrix(fExternalMatrix,
625 fExternalInverse);
626 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 } while ((layer = layer->fNext) != NULL);
628 }
629 fDeviceCMDirty = false;
630 }
631}
632
reed@android.comf2b98d62010-12-20 18:26:13 +0000633void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
634 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000636 if (fLastDeviceToGainFocus != device) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000637 device->gainFocus(this, matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000638 fLastDeviceToGainFocus = device;
639 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640}
641
642///////////////////////////////////////////////////////////////////////////////
643
644int SkCanvas::internalSave(SaveFlags flags) {
645 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000646
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 MCRec* newTop = (MCRec*)fMCStack.push_back();
648 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 newTop->fNext = fMCRec;
651 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000652
reed@google.com5c3d1472011-02-22 19:12:23 +0000653 fClipStack.save();
654 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
655
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 return saveCount;
657}
658
659int SkCanvas::save(SaveFlags flags) {
660 // call shared impl
661 return this->internalSave(flags);
662}
663
664#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
665#define C16MASK (1 << SkBitmap::kRGB_565_Config)
666#define C8MASK (1 << SkBitmap::kA8_Config)
667
668static SkBitmap::Config resolve_config(SkCanvas* canvas,
669 const SkIRect& bounds,
670 SkCanvas::SaveFlags flags,
671 bool* isOpaque) {
672 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
673
674#if 0
675 // loop through and union all the configs we may draw into
676 uint32_t configMask = 0;
677 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
678 {
679 SkDevice* device = canvas->getLayerDevice(i);
680 if (device->intersects(bounds))
681 configMask |= 1 << device->config();
682 }
683
684 // if the caller wants alpha or fullcolor, we can't return 565
685 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
686 SkCanvas::kHasAlphaLayer_SaveFlag))
687 configMask &= ~C16MASK;
688
689 switch (configMask) {
690 case C8MASK: // if we only have A8, return that
691 return SkBitmap::kA8_Config;
692
693 case C16MASK: // if we only have 565, return that
694 return SkBitmap::kRGB_565_Config;
695
696 default:
697 return SkBitmap::kARGB_8888_Config; // default answer
698 }
699#else
700 return SkBitmap::kARGB_8888_Config; // default answer
701#endif
702}
703
704static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
705 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
706}
707
708int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
709 SaveFlags flags) {
710 // do this before we create the layer. We don't call the public save() since
711 // that would invoke a possibly overridden virtual
712 int count = this->internalSave(flags);
713
714 fDeviceCMDirty = true;
715
716 SkIRect ir;
717 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000718 if (clipBounds.isEmpty()) {
719 return count;
720 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721
722 if (NULL != bounds) {
723 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000724
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 this->getTotalMatrix().mapRect(&r, *bounds);
726 r.roundOut(&ir);
727 // early exit if the layer's bounds are clipped out
728 if (!ir.intersect(clipBounds)) {
729 if (bounds_affects_clip(flags))
730 fMCRec->fRegion->setEmpty();
731 return count;
732 }
733 } else { // no user bounds, so just use the clip
734 ir = clipBounds;
735 }
736
reed@google.com5c3d1472011-02-22 19:12:23 +0000737 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 // early exit if the clip is now empty
739 if (bounds_affects_clip(flags) &&
740 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
741 return count;
742 }
743
744 bool isOpaque;
745 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
746
747 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
748 isOpaque, true);
749 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
750 device->unref();
751
752 layer->fNext = fMCRec->fTopLayer;
753 fMCRec->fLayer = layer;
754 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
755
756 return count;
757}
758
759int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
760 SaveFlags flags) {
761 if (0xFF == alpha) {
762 return this->saveLayer(bounds, NULL, flags);
763 } else {
764 SkPaint tmpPaint;
765 tmpPaint.setAlpha(alpha);
766 return this->saveLayer(bounds, &tmpPaint, flags);
767 }
768}
769
770void SkCanvas::restore() {
771 // check for underflow
772 if (fMCStack.count() > 1) {
773 this->internalRestore();
774 }
775}
776
777void SkCanvas::internalRestore() {
778 SkASSERT(fMCStack.count() != 0);
779
780 fDeviceCMDirty = true;
781 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000782 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783
reed@google.com5c3d1472011-02-22 19:12:23 +0000784 fClipStack.restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 // reserve our layer (if any)
786 DeviceCM* layer = fMCRec->fLayer; // may be null
787 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
788 fMCRec->fLayer = NULL;
789
790 // now do the normal restore()
791 fMCRec->~MCRec(); // balanced in save()
792 fMCStack.pop_back();
793 fMCRec = (MCRec*)fMCStack.back();
794
795 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
796 since if we're being recorded, we don't want to record this (the
797 recorder will have already recorded the restore).
798 */
799 if (NULL != layer) {
800 if (layer->fNext) {
801 this->drawDevice(layer->fDevice, layer->fX, layer->fY,
802 layer->fPaint);
803 // reset this, since drawDevice will have set it to true
804 fDeviceCMDirty = true;
805 }
806 SkDELETE(layer);
807 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000808
809 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810}
811
812int SkCanvas::getSaveCount() const {
813 return fMCStack.count();
814}
815
816void SkCanvas::restoreToCount(int count) {
817 // sanity check
818 if (count < 1) {
819 count = 1;
820 }
821 while (fMCStack.count() > count) {
822 this->restore();
823 }
824}
825
826/////////////////////////////////////////////////////////////////////////////
827
828// can't draw it if its empty, or its too big for a fixed-point width or height
829static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000830 return bitmap.width() <= 0 || bitmap.height() <= 0
831#ifndef SK_ALLOW_OVER_32K_BITMAPS
832 || bitmap.width() > 32767 || bitmap.height() > 32767
833#endif
834 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835}
836
reed@android.comf2b98d62010-12-20 18:26:13 +0000837void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 const SkMatrix& matrix, const SkPaint* paint) {
839 if (reject_bitmap(bitmap)) {
840 return;
841 }
842
843 if (NULL == paint) {
844 SkPaint tmpPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000845 this->commonDrawBitmap(bitmap, srcRect, matrix, tmpPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 } else {
reed@android.comf2b98d62010-12-20 18:26:13 +0000847 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848 }
849}
850
851void SkCanvas::drawDevice(SkDevice* device, int x, int y,
852 const SkPaint* paint) {
853 SkPaint tmp;
854 if (NULL == paint) {
855 tmp.setDither(true);
856 paint = &tmp;
857 }
reed@google.com4b226022011-01-11 18:32:13 +0000858
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
860 while (iter.next()) {
861 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
862 *paint);
863 }
864 ITER_END
865}
866
867/////////////////////////////////////////////////////////////////////////////
868
869bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
870 fDeviceCMDirty = true;
871 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000872 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 return fMCRec->fMatrix->preTranslate(dx, dy);
874}
875
876bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
877 fDeviceCMDirty = true;
878 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000879 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 return fMCRec->fMatrix->preScale(sx, sy);
881}
882
883bool SkCanvas::rotate(SkScalar degrees) {
884 fDeviceCMDirty = true;
885 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000886 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 return fMCRec->fMatrix->preRotate(degrees);
888}
889
890bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
891 fDeviceCMDirty = true;
892 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000893 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 return fMCRec->fMatrix->preSkew(sx, sy);
895}
896
897bool SkCanvas::concat(const SkMatrix& matrix) {
898 fDeviceCMDirty = true;
899 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000900 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 return fMCRec->fMatrix->preConcat(matrix);
902}
903
904void SkCanvas::setMatrix(const SkMatrix& matrix) {
905 fDeviceCMDirty = true;
906 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000907 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 *fMCRec->fMatrix = matrix;
909}
910
911// this is not virtual, so it must call a virtual method so that subclasses
912// will see its action
913void SkCanvas::resetMatrix() {
914 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000915
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 matrix.reset();
917 this->setMatrix(matrix);
918}
919
920//////////////////////////////////////////////////////////////////////////////
921
922bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000923 AutoValidateClip avc(this);
924
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925 fDeviceCMDirty = true;
926 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000927 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928
929 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000930 // for these simpler matrices, we can stay a rect ever after applying
931 // the matrix. This means we don't have to a) make a path, and b) tell
932 // the region code to scan-convert the path, only to discover that it
933 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 SkRect r;
935 SkIRect ir;
936
937 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000938 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 r.round(&ir);
940 return fMCRec->fRegion->op(ir, op);
941 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000942 // since we're rotate or some such thing, we convert the rect to a path
943 // and clip against that, since it can handle any matrix. However, to
944 // avoid recursion in the case where we are subclassed (e.g. Pictures)
945 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 SkPath path;
947
948 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000949 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 }
951}
952
reed@google.com819c9212011-02-23 18:56:55 +0000953static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
954 const SkPath& devPath, SkRegion::Op op) {
955 if (SkRegion::kIntersect_Op == op) {
956 return currRgn->setPath(devPath, *currRgn);
957 } else {
958 SkRegion base;
959 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
960 base.setRect(0, 0, bm.width(), bm.height());
961
962 if (SkRegion::kReplace_Op == op) {
963 return currRgn->setPath(devPath, base);
964 } else {
965 SkRegion rgn;
966 rgn.setPath(devPath, base);
967 return currRgn->op(rgn, op);
968 }
969 }
970}
971
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000973 AutoValidateClip avc(this);
974
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 fDeviceCMDirty = true;
976 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000977 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978
979 SkPath devPath;
980 path.transform(*fMCRec->fMatrix, &devPath);
981
reed@google.com5c3d1472011-02-22 19:12:23 +0000982 // if we called path.swap() we could avoid a deep copy of this path
983 fClipStack.clipDevPath(devPath, op);
984
reed@google.com819c9212011-02-23 18:56:55 +0000985 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986}
987
988bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000989 AutoValidateClip avc(this);
990
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 fDeviceCMDirty = true;
992 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000993 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994
reed@google.com5c3d1472011-02-22 19:12:23 +0000995 // todo: signal fClipStack that we have a region, and therefore (I guess)
996 // we have to ignore it, and use the region directly?
997 fClipStack.clipDevRect(rgn.getBounds());
998
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 return fMCRec->fRegion->op(rgn, op);
1000}
1001
reed@google.com819c9212011-02-23 18:56:55 +00001002#ifdef SK_DEBUG
1003void SkCanvas::validateClip() const {
1004 // construct clipRgn from the clipstack
1005 const SkDevice* device = this->getDevice();
1006 SkIRect ir;
1007 ir.set(0, 0, device->width(), device->height());
1008 SkRegion clipRgn(ir);
1009
1010 SkClipStack::B2FIter iter(fClipStack);
1011 const SkClipStack::B2FIter::Clip* clip;
1012 while ((clip = iter.next()) != NULL) {
1013 if (clip->fPath) {
1014 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1015 } else if (clip->fRect) {
1016 clip->fRect->round(&ir);
1017 clipRgn.op(ir, clip->fOp);
1018 } else {
1019 break;
1020 }
1021 }
1022
1023 // now compare against the current rgn
1024 const SkRegion& rgn = this->getTotalClip();
1025 SkASSERT(rgn == clipRgn);
1026}
1027#endif
1028
reed@google.com5c3d1472011-02-22 19:12:23 +00001029///////////////////////////////////////////////////////////////////////////////
1030
reed@android.comba09de42010-02-05 20:46:05 +00001031void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001033 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1034 fLocalBoundsCompareTypeBW;
1035
1036 if (!this->getClipBounds(&r, et)) {
1037 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001039 rCompare.set(SkScalarToCompareType(r.fLeft),
1040 SkScalarToCompareType(r.fTop),
1041 SkScalarToCompareType(r.fRight),
1042 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043 }
1044}
1045
reed@android.comd252db02009-04-01 18:31:44 +00001046/* current impl ignores edgetype, and relies on
1047 getLocalClipBoundsCompareType(), which always returns a value assuming
1048 antialiasing (worst case)
1049 */
reed@android.comba09de42010-02-05 20:46:05 +00001050bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001051
1052 if (!rect.hasValidCoordinates())
1053 return true;
1054
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 if (fMCRec->fRegion->isEmpty()) {
1056 return true;
1057 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058
reed@android.coma380ae42009-07-21 01:17:02 +00001059 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1060 SkRect dst;
1061 fMCRec->fMatrix->mapRect(&dst, rect);
1062 SkIRect idst;
1063 dst.roundOut(&idst);
1064 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1065 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001066 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001067
reed@android.coma380ae42009-07-21 01:17:02 +00001068 // for speed, do the most likely reject compares first
1069 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1070 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1071 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1072 return true;
1073 }
1074 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1075 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1076 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1077 return true;
1078 }
1079 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081}
1082
1083bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001084 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085}
1086
1087bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1088 /* current impl ignores edgetype, and relies on
1089 getLocalClipBoundsCompareType(), which always returns a value assuming
1090 antialiasing (worst case)
1091 */
1092
1093 if (fMCRec->fRegion->isEmpty()) {
1094 return true;
1095 }
reed@google.com4b226022011-01-11 18:32:13 +00001096
reed@android.comaefd2bc2009-03-30 21:02:14 +00001097 SkScalarCompareType userT = SkScalarToCompareType(top);
1098 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001099
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001101 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 if (userT >= userB) {
1103 return true;
1104 }
reed@google.com4b226022011-01-11 18:32:13 +00001105
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 // check if we are above or below the local clip bounds
1107 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1108 return userT >= clipR.fBottom || userB <= clipR.fTop;
1109}
1110
1111bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1112 const SkRegion& clip = *fMCRec->fRegion;
1113 if (clip.isEmpty()) {
1114 if (bounds) {
1115 bounds->setEmpty();
1116 }
1117 return false;
1118 }
1119
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001120 SkMatrix inverse;
1121 // if we can't invert the CTM, we can't return local clip bounds
1122 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001123 if (bounds) {
1124 bounds->setEmpty();
1125 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001126 return false;
1127 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001129 if (NULL != bounds) {
1130 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 // get the clip's bounds
1132 const SkIRect& ibounds = clip.getBounds();
1133 // adjust it outwards if we are antialiasing
1134 int inset = (kAA_EdgeType == et);
1135 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1136 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001137
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 // invert into local coordinates
1139 inverse.mapRect(bounds, r);
1140 }
1141 return true;
1142}
1143
1144const SkMatrix& SkCanvas::getTotalMatrix() const {
1145 return *fMCRec->fMatrix;
1146}
1147
1148const SkRegion& SkCanvas::getTotalClip() const {
1149 return *fMCRec->fRegion;
1150}
1151
reed@android.comf2b98d62010-12-20 18:26:13 +00001152void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1153 if (NULL == matrix || matrix->isIdentity()) {
1154 if (fUseExternalMatrix) {
1155 fDeviceCMDirty = true;
1156 }
1157 fUseExternalMatrix = false;
1158 } else {
1159 fUseExternalMatrix = true;
1160 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001161
reed@android.comf2b98d62010-12-20 18:26:13 +00001162 fExternalMatrix = *matrix;
1163 matrix->invert(&fExternalInverse);
1164 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001165}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166
reed@android.comf2b98d62010-12-20 18:26:13 +00001167SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1168 bool isOpaque, bool forLayer) {
1169 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001170}
1171
1172//////////////////////////////////////////////////////////////////////////////
1173// These are the virtual drawing methods
1174//////////////////////////////////////////////////////////////////////////////
1175
1176void SkCanvas::drawPaint(const SkPaint& paint) {
1177 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
1178
1179 while (iter.next()) {
1180 iter.fDevice->drawPaint(iter, paint);
1181 }
1182
1183 ITER_END
1184}
1185
1186void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1187 const SkPaint& paint) {
1188 if ((long)count <= 0) {
1189 return;
1190 }
1191
1192 SkASSERT(pts != NULL);
1193
1194 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001195
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 while (iter.next()) {
1197 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1198 }
reed@google.com4b226022011-01-11 18:32:13 +00001199
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 ITER_END
1201}
1202
1203void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1204 if (paint.canComputeFastBounds()) {
1205 SkRect storage;
1206 if (this->quickReject(paint.computeFastBounds(r, &storage),
1207 paint2EdgeType(&paint))) {
1208 return;
1209 }
1210 }
reed@google.com4b226022011-01-11 18:32:13 +00001211
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1213
1214 while (iter.next()) {
1215 iter.fDevice->drawRect(iter, r, paint);
1216 }
1217
1218 ITER_END
1219}
1220
1221void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1222 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001223 SkRect storage;
1224 const SkRect& bounds = path.getBounds();
1225 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 paint2EdgeType(&paint))) {
1227 return;
1228 }
1229 }
1230
1231 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1232
1233 while (iter.next()) {
1234 iter.fDevice->drawPath(iter, path, paint);
1235 }
1236
1237 ITER_END
1238}
1239
1240void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1241 const SkPaint* paint) {
1242 SkDEBUGCODE(bitmap.validate();)
1243
1244 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1245 SkRect fastBounds;
1246 fastBounds.set(x, y,
1247 x + SkIntToScalar(bitmap.width()),
1248 y + SkIntToScalar(bitmap.height()));
1249 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1250 return;
1251 }
1252 }
reed@google.com4b226022011-01-11 18:32:13 +00001253
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 SkMatrix matrix;
1255 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001256 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257}
1258
1259void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1260 const SkRect& dst, const SkPaint* paint) {
1261 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1262 return;
1263 }
reed@google.com4b226022011-01-11 18:32:13 +00001264
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 // do this now, to avoid the cost of calling extract for RLE bitmaps
1266 if (this->quickReject(dst, paint2EdgeType(paint))) {
1267 return;
1268 }
reed@google.com4b226022011-01-11 18:32:13 +00001269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001271
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001273 SkRect tmpSrc;
1274 if (src) {
1275 tmpSrc.set(*src);
1276 // if the extract process clipped off the top or left of the
1277 // original, we adjust for that here to get the position right.
1278 if (tmpSrc.fLeft > 0) {
1279 tmpSrc.fRight -= tmpSrc.fLeft;
1280 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001281 }
reed@android.com87899992009-10-16 14:48:38 +00001282 if (tmpSrc.fTop > 0) {
1283 tmpSrc.fBottom -= tmpSrc.fTop;
1284 tmpSrc.fTop = 0;
1285 }
1286 } else {
1287 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1288 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 }
reed@android.com87899992009-10-16 14:48:38 +00001290 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001291
1292 // ensure that src is "valid" before we pass it to our internal routines
1293 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1294 SkIRect tmpISrc;
1295 if (src) {
1296 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
1297 tmpISrc.intersect(*src);
1298 src = &tmpISrc;
1299 }
1300 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301}
1302
1303void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1304 const SkPaint* paint) {
1305 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001306 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307}
1308
reed@android.comf2b98d62010-12-20 18:26:13 +00001309void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1310 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001312
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001314
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 while (iter.next()) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001316 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 }
reed@android.com9b039062009-02-11 15:09:58 +00001318
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 ITER_END
1320}
1321
1322void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1323 const SkPaint* paint) {
1324 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001325
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 if (reject_bitmap(bitmap)) {
1327 return;
1328 }
reed@google.com4b226022011-01-11 18:32:13 +00001329
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 SkPaint tmp;
1331 if (NULL == paint) {
1332 paint = &tmp;
1333 }
reed@google.com4b226022011-01-11 18:32:13 +00001334
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001336
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 while (iter.next()) {
1338 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1339 *paint);
1340 }
1341 ITER_END
1342}
1343
1344void SkCanvas::drawText(const void* text, size_t byteLength,
1345 SkScalar x, SkScalar y, const SkPaint& paint) {
1346 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1347
1348 while (iter.next()) {
1349 iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
1350 }
1351
1352 ITER_END
1353}
1354
1355void SkCanvas::drawPosText(const void* text, size_t byteLength,
1356 const SkPoint pos[], const SkPaint& paint) {
1357 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001358
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359 while (iter.next()) {
1360 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
1361 paint);
1362 }
reed@google.com4b226022011-01-11 18:32:13 +00001363
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364 ITER_END
1365}
1366
1367void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1368 const SkScalar xpos[], SkScalar constY,
1369 const SkPaint& paint) {
1370 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001371
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 while (iter.next()) {
1373 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
1374 paint);
1375 }
reed@google.com4b226022011-01-11 18:32:13 +00001376
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 ITER_END
1378}
1379
1380void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1381 const SkPath& path, const SkMatrix* matrix,
1382 const SkPaint& paint) {
1383 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1384
1385 while (iter.next()) {
1386 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1387 matrix, paint);
1388 }
1389
1390 ITER_END
1391}
1392
1393void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1394 const SkPoint verts[], const SkPoint texs[],
1395 const SkColor colors[], SkXfermode* xmode,
1396 const uint16_t indices[], int indexCount,
1397 const SkPaint& paint) {
1398 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001399
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 while (iter.next()) {
1401 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1402 colors, xmode, indices, indexCount, paint);
1403 }
reed@google.com4b226022011-01-11 18:32:13 +00001404
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 ITER_END
1406}
1407
reed@android.comcb608442009-12-04 21:32:27 +00001408void SkCanvas::drawData(const void* data, size_t length) {
1409 // do nothing. Subclasses may do something with the data
1410}
1411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412//////////////////////////////////////////////////////////////////////////////
1413// These methods are NOT virtual, and therefore must call back into virtual
1414// methods, rather than actually drawing themselves.
1415//////////////////////////////////////////////////////////////////////////////
1416
1417void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001418 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 SkPaint paint;
1420
1421 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001422 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001423 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 }
1425 this->drawPaint(paint);
1426}
1427
reed@android.com845fdac2009-06-23 03:01:32 +00001428void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 SkPaint paint;
1430
1431 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001432 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001433 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434 }
1435 this->drawPaint(paint);
1436}
1437
1438void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1439 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001440
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 pt.set(x, y);
1442 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1443}
1444
1445void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1446 SkPoint pt;
1447 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001448
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 pt.set(x, y);
1450 paint.setColor(color);
1451 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1452}
1453
1454void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1455 const SkPaint& paint) {
1456 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 pts[0].set(x0, y0);
1459 pts[1].set(x1, y1);
1460 this->drawPoints(kLines_PointMode, 2, pts, paint);
1461}
1462
1463void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1464 SkScalar right, SkScalar bottom,
1465 const SkPaint& paint) {
1466 SkRect r;
1467
1468 r.set(left, top, right, bottom);
1469 this->drawRect(r, paint);
1470}
1471
1472void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1473 const SkPaint& paint) {
1474 if (radius < 0) {
1475 radius = 0;
1476 }
1477
1478 SkRect r;
1479 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001480
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481 if (paint.canComputeFastBounds()) {
1482 SkRect storage;
1483 if (this->quickReject(paint.computeFastBounds(r, &storage),
1484 paint2EdgeType(&paint))) {
1485 return;
1486 }
1487 }
reed@google.com4b226022011-01-11 18:32:13 +00001488
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 SkPath path;
1490 path.addOval(r);
1491 this->drawPath(path, paint);
1492}
1493
1494void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1495 const SkPaint& paint) {
1496 if (rx > 0 && ry > 0) {
1497 if (paint.canComputeFastBounds()) {
1498 SkRect storage;
1499 if (this->quickReject(paint.computeFastBounds(r, &storage),
1500 paint2EdgeType(&paint))) {
1501 return;
1502 }
1503 }
1504
1505 SkPath path;
1506 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1507 this->drawPath(path, paint);
1508 } else {
1509 this->drawRect(r, paint);
1510 }
1511}
1512
1513void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1514 if (paint.canComputeFastBounds()) {
1515 SkRect storage;
1516 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1517 paint2EdgeType(&paint))) {
1518 return;
1519 }
1520 }
1521
1522 SkPath path;
1523 path.addOval(oval);
1524 this->drawPath(path, paint);
1525}
1526
1527void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1528 SkScalar sweepAngle, bool useCenter,
1529 const SkPaint& paint) {
1530 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1531 this->drawOval(oval, paint);
1532 } else {
1533 SkPath path;
1534 if (useCenter) {
1535 path.moveTo(oval.centerX(), oval.centerY());
1536 }
1537 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1538 if (useCenter) {
1539 path.close();
1540 }
1541 this->drawPath(path, paint);
1542 }
1543}
1544
1545void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1546 const SkPath& path, SkScalar hOffset,
1547 SkScalar vOffset, const SkPaint& paint) {
1548 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001549
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550 matrix.setTranslate(hOffset, vOffset);
1551 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1552}
1553
reed@android.comf76bacf2009-05-13 14:00:33 +00001554///////////////////////////////////////////////////////////////////////////////
1555
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556void SkCanvas::drawPicture(SkPicture& picture) {
1557 int saveCount = save();
1558 picture.draw(this);
1559 restoreToCount(saveCount);
1560}
1561
reed@android.comf76bacf2009-05-13 14:00:33 +00001562void SkCanvas::drawShape(SkShape* shape) {
1563 // shape baseclass takes care of save/restore
1564 shape->draw(this);
1565}
1566
reed@android.com8a1c16f2008-12-17 15:59:43 +00001567///////////////////////////////////////////////////////////////////////////////
1568///////////////////////////////////////////////////////////////////////////////
1569
1570SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001571 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001572
1573 SkASSERT(canvas);
1574
1575 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1576 fDone = !fImpl->next();
1577}
1578
1579SkCanvas::LayerIter::~LayerIter() {
1580 fImpl->~SkDrawIter();
1581}
1582
1583void SkCanvas::LayerIter::next() {
1584 fDone = !fImpl->next();
1585}
1586
1587SkDevice* SkCanvas::LayerIter::device() const {
1588 return fImpl->getDevice();
1589}
1590
1591const SkMatrix& SkCanvas::LayerIter::matrix() const {
1592 return fImpl->getMatrix();
1593}
1594
1595const SkPaint& SkCanvas::LayerIter::paint() const {
1596 const SkPaint* paint = fImpl->getPaint();
1597 if (NULL == paint) {
1598 paint = &fDefaultPaint;
1599 }
1600 return *paint;
1601}
1602
1603const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1604int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1605int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1606