blob: 57b4f7b340ede5798ee8a39431a4ac27bc6d6ee3 [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
77
78 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
79 : fNext(NULL) {
80 if (NULL != device) {
81 device->ref();
82 device->lockPixels();
83 }
84 fDevice = device;
85 fX = SkToS16(x);
86 fY = SkToS16(y);
87 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 }
97
98 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
99 SkRegion* updateClip) {
100 int x = fX;
101 int y = fY;
102 int width = fDevice->width();
103 int height = fDevice->height();
104
105 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;
113
114 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)
120
121 if (updateClip) {
122 updateClip->op(x, y, x + width, y + height,
123 SkRegion::kDifference_Op);
124 }
125
126 fDevice->setMatrixClip(*fMatrix, fClip);
127
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
135 }
136
137 void translateClip() {
138 if (fX | fY) {
139 fClip.translate(fX, fY);
140 }
141 }
142
143private:
144 SkMatrix fMatrixStorage;
145};
146
147/* This is the record we keep for each save/restore level in the stack.
148 Since a level optionally copies the matrix and/or stack, we have pointers
149 for these fields. If the value is copied for this level, the copy is
150 stored in the ...Storage field, and the pointer points to that. If the
151 value is not copied for this level, we ignore ...Storage, and just point
152 at the corresponding value in the previous level in the stack.
153*/
154class SkCanvas::MCRec {
155public:
156 MCRec* fNext;
157 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
158 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
159 SkDrawFilter* fFilter; // the current filter (or null)
160
161 DeviceCM* fLayer;
162 /* If there are any layers in the stack, this points to the top-most
163 one that is at or below this level in the stack (so we know what
164 bitmap/device to draw into from this level. This value is NOT
165 reference counted, since the real owner is either our fLayer field,
166 or a previous one in a lower level.)
167 */
168 DeviceCM* fTopLayer;
169
170 MCRec(const MCRec* prev, int flags) {
171 if (NULL != prev) {
172 if (flags & SkCanvas::kMatrix_SaveFlag) {
173 fMatrixStorage = *prev->fMatrix;
174 fMatrix = &fMatrixStorage;
175 } else {
176 fMatrix = prev->fMatrix;
177 }
178
179 if (flags & SkCanvas::kClip_SaveFlag) {
180 fRegionStorage = *prev->fRegion;
181 fRegion = &fRegionStorage;
182 } else {
183 fRegion = prev->fRegion;
184 }
185
186 fFilter = prev->fFilter;
187 fFilter->safeRef();
188
189 fTopLayer = prev->fTopLayer;
190 } else { // no prev
191 fMatrixStorage.reset();
192
193 fMatrix = &fMatrixStorage;
194 fRegion = &fRegionStorage;
195 fFilter = NULL;
196 fTopLayer = NULL;
197 }
198 fLayer = NULL;
199
200 // don't bother initializing fNext
201 inc_rec();
202 }
203 ~MCRec() {
204 fFilter->safeUnref();
205 SkDELETE(fLayer);
206 dec_rec();
207 }
208
209private:
210 SkMatrix fMatrixStorage;
211 SkRegion fRegionStorage;
212};
213
214class SkDrawIter : public SkDraw {
215public:
216 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
217 fCanvas = canvas;
218 canvas->updateDeviceCMCache();
219
220 fBounder = canvas->getBounder();
221 fCurrLayer = canvas->fMCRec->fTopLayer;
222 fSkipEmptyClips = skipEmptyClips;
223 }
224
225 bool next() {
226 // skip over recs with empty clips
227 if (fSkipEmptyClips) {
228 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
229 fCurrLayer = fCurrLayer->fNext;
230 }
231 }
232
233 if (NULL != fCurrLayer) {
234 const DeviceCM* rec = fCurrLayer;
235
236 fMatrix = rec->fMatrix;
237 fClip = &rec->fClip;
238 fDevice = rec->fDevice;
239 fBitmap = &fDevice->accessBitmap(true);
240 fLayerX = rec->fX;
241 fLayerY = rec->fY;
242 fPaint = rec->fPaint;
243 SkDEBUGCODE(this->validate();)
244
245 fCurrLayer = rec->fNext;
246 if (fBounder) {
247 fBounder->setClip(fClip);
248 }
249
250 // fCurrLayer may be NULL now
251
252 fCanvas->prepareForDeviceDraw(fDevice);
253 return true;
254 }
255 return false;
256 }
257
258 int getX() const { return fLayerX; }
259 int getY() const { return fLayerY; }
260 SkDevice* getDevice() const { return fDevice; }
261 const SkMatrix& getMatrix() const { return *fMatrix; }
262 const SkRegion& getClip() const { return *fClip; }
263 const SkPaint* getPaint() const { return fPaint; }
264private:
265 SkCanvas* fCanvas;
266 const DeviceCM* fCurrLayer;
267 const SkPaint* fPaint; // May be null.
268 int fLayerX;
269 int fLayerY;
270 SkBool8 fSkipEmptyClips;
271
272 typedef SkDraw INHERITED;
273};
274
275/////////////////////////////////////////////////////////////////////////////
276
277class AutoDrawLooper {
278public:
279 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, SkDrawFilter::Type t)
280 : fCanvas(canvas), fPaint((SkPaint*)&paint), fType(t) {
281 if ((fLooper = paint.getLooper()) != NULL) {
282 fLooper->init(canvas, (SkPaint*)&paint);
283 } else {
284 fOnce = true;
285 }
286 fFilter = canvas->getDrawFilter();
287 fNeedFilterRestore = false;
288 }
289
290 ~AutoDrawLooper() {
291 if (fNeedFilterRestore) {
292 SkASSERT(fFilter);
293 fFilter->restore(fCanvas, fPaint, fType);
294 }
295 if (NULL != fLooper) {
296 fLooper->restore();
297 }
298 }
299
300 bool next() {
301 SkDrawFilter* filter = fFilter;
302
303 // if we drew earlier with a filter, then we need to restore first
304 if (fNeedFilterRestore) {
305 SkASSERT(filter);
306 filter->restore(fCanvas, fPaint, fType);
307 fNeedFilterRestore = false;
308 }
309
310 bool result;
311
312 if (NULL != fLooper) {
313 result = fLooper->next();
314 } else {
315 result = fOnce;
316 fOnce = false;
317 }
318
319 // if we're gonna draw, give the filter a chance to do its work
320 if (result && NULL != filter) {
321 fNeedFilterRestore = result = filter->filter(fCanvas, fPaint,
322 fType);
323 }
324 return result;
325 }
326
327private:
328 SkDrawLooper* fLooper;
329 SkDrawFilter* fFilter;
330 SkCanvas* fCanvas;
331 SkPaint* fPaint;
332 SkDrawFilter::Type fType;
333 bool fOnce;
334 bool fNeedFilterRestore;
335
336};
337
338/* Stack helper for managing a SkBounder. In the destructor, if we were
339 given a bounder, we call its commit() method, signifying that we are
340 done accumulating bounds for that draw.
341*/
342class SkAutoBounderCommit {
343public:
344 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
345 ~SkAutoBounderCommit() {
346 if (NULL != fBounder) {
347 fBounder->commit();
348 }
349 }
350private:
351 SkBounder* fBounder;
352};
353
354#include "SkColorPriv.h"
355
356class AutoValidator {
357public:
358 AutoValidator(SkDevice* device) : fDevice(device) {}
359 ~AutoValidator() {
360#ifdef SK_DEBUG
361 const SkBitmap& bm = fDevice->accessBitmap(false);
362 if (bm.config() == SkBitmap::kARGB_4444_Config) {
363 for (int y = 0; y < bm.height(); y++) {
364 const SkPMColor16* p = bm.getAddr16(0, y);
365 for (int x = 0; x < bm.width(); x++) {
366 SkPMColor16 c = p[x];
367 SkPMColor16Assert(c);
368 }
369 }
370 }
371#endif
372 }
373private:
374 SkDevice* fDevice;
375};
376
377////////// macros to place around the internal draw calls //////////////////
378
379#define ITER_BEGIN(paint, type) \
380/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
381 AutoDrawLooper looper(this, paint, type); \
382 while (looper.next()) { \
383 SkAutoBounderCommit ac(fBounder); \
384 SkDrawIter iter(this);
385
386#define ITER_END }
387
388////////////////////////////////////////////////////////////////////////////
389
390SkDevice* SkCanvas::init(SkDevice* device) {
391 fBounder = NULL;
392 fLocalBoundsCompareTypeDirty = true;
393
394 fMCRec = (MCRec*)fMCStack.push_back();
395 new (fMCRec) MCRec(NULL, 0);
396
397 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
398 fMCRec->fTopLayer = fMCRec->fLayer;
399 fMCRec->fNext = NULL;
400
401 return this->setDevice(device);
402}
403
404SkCanvas::SkCanvas(SkDevice* device)
405 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
406 inc_canvas();
407
408 this->init(device);
409}
410
411SkCanvas::SkCanvas(const SkBitmap& bitmap)
412 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
413 inc_canvas();
414
415 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
416}
417
418SkCanvas::~SkCanvas() {
419 // free up the contents of our deque
420 this->restoreToCount(1); // restore everything but the last
421 this->internalRestore(); // restore the last, since we're going away
422
423 fBounder->safeUnref();
424
425 dec_canvas();
426}
427
428SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
429 SkRefCnt_SafeAssign(fBounder, bounder);
430 return bounder;
431}
432
433SkDrawFilter* SkCanvas::getDrawFilter() const {
434 return fMCRec->fFilter;
435}
436
437SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
438 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
439 return filter;
440}
441
442///////////////////////////////////////////////////////////////////////////////
443
444SkDevice* SkCanvas::getDevice() const {
445 // return root device
446 SkDeque::Iter iter(fMCStack);
447 MCRec* rec = (MCRec*)iter.next();
448 SkASSERT(rec && rec->fLayer);
449 return rec->fLayer->fDevice;
450}
451
452SkDevice* SkCanvas::setDevice(SkDevice* device) {
453 // return root device
454 SkDeque::Iter iter(fMCStack);
455 MCRec* rec = (MCRec*)iter.next();
456 SkASSERT(rec && rec->fLayer);
457 SkDevice* rootDevice = rec->fLayer->fDevice;
458
459 if (rootDevice == device) {
460 return device;
461 }
462
463 /* Notify the devices that they are going in/out of scope, so they can do
464 things like lock/unlock their pixels, etc.
465 */
466 if (device) {
467 device->lockPixels();
468 }
469 if (rootDevice) {
470 rootDevice->unlockPixels();
471 }
472
473 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
474 rootDevice = device;
475
476 fDeviceCMDirty = true;
477
478 /* Now we update our initial region to have the bounds of the new device,
479 and then intersect all of the clips in our stack with these bounds,
480 to ensure that we can't draw outside of the device's bounds (and trash
481 memory).
482
483 NOTE: this is only a partial-fix, since if the new device is larger than
484 the previous one, we don't know how to "enlarge" the clips in our stack,
485 so drawing may be artificially restricted. Without keeping a history of
486 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
487 reconstruct the correct clips, so this approximation will have to do.
488 The caller really needs to restore() back to the base if they want to
489 accurately take advantage of the new device bounds.
490 */
491
492 if (NULL == device) {
493 rec->fRegion->setEmpty();
494 while ((rec = (MCRec*)iter.next()) != NULL) {
495 (void)rec->fRegion->setEmpty();
496 }
497 } else {
498 // compute our total bounds for all devices
499 SkIRect bounds;
500
501 bounds.set(0, 0, device->width(), device->height());
502
503 // now jam our 1st clip to be bounds, and intersect the rest with that
504 rec->fRegion->setRect(bounds);
505 while ((rec = (MCRec*)iter.next()) != NULL) {
506 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
507 }
508 }
509 return device;
510}
511
512SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
513 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
514 device->unref();
515 return device;
516}
517
518//////////////////////////////////////////////////////////////////////////////
519
520bool SkCanvas::getViewport(SkIPoint* size) const {
521 return false;
522}
523
524bool SkCanvas::setViewport(int width, int height) {
525 return false;
526}
527
528void SkCanvas::updateDeviceCMCache() {
529 if (fDeviceCMDirty) {
530 const SkMatrix& totalMatrix = this->getTotalMatrix();
531 const SkRegion& totalClip = this->getTotalClip();
532 DeviceCM* layer = fMCRec->fTopLayer;
533
534 if (NULL == layer->fNext) { // only one layer
535 layer->updateMC(totalMatrix, totalClip, NULL);
536 } else {
537 SkRegion clip;
538 clip = totalClip; // make a copy
539 do {
540 layer->updateMC(totalMatrix, clip, &clip);
541 } while ((layer = layer->fNext) != NULL);
542 }
543 fDeviceCMDirty = false;
544 }
545}
546
547void SkCanvas::prepareForDeviceDraw(SkDevice* device) {
548 SkASSERT(device);
549 device->gainFocus(this);
550}
551
552///////////////////////////////////////////////////////////////////////////////
553
554int SkCanvas::internalSave(SaveFlags flags) {
555 int saveCount = this->getSaveCount(); // record this before the actual save
556
557 MCRec* newTop = (MCRec*)fMCStack.push_back();
558 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
559
560 newTop->fNext = fMCRec;
561 fMCRec = newTop;
562
563 return saveCount;
564}
565
566int SkCanvas::save(SaveFlags flags) {
567 // call shared impl
568 return this->internalSave(flags);
569}
570
571#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
572#define C16MASK (1 << SkBitmap::kRGB_565_Config)
573#define C8MASK (1 << SkBitmap::kA8_Config)
574
575static SkBitmap::Config resolve_config(SkCanvas* canvas,
576 const SkIRect& bounds,
577 SkCanvas::SaveFlags flags,
578 bool* isOpaque) {
579 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
580
581#if 0
582 // loop through and union all the configs we may draw into
583 uint32_t configMask = 0;
584 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
585 {
586 SkDevice* device = canvas->getLayerDevice(i);
587 if (device->intersects(bounds))
588 configMask |= 1 << device->config();
589 }
590
591 // if the caller wants alpha or fullcolor, we can't return 565
592 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
593 SkCanvas::kHasAlphaLayer_SaveFlag))
594 configMask &= ~C16MASK;
595
596 switch (configMask) {
597 case C8MASK: // if we only have A8, return that
598 return SkBitmap::kA8_Config;
599
600 case C16MASK: // if we only have 565, return that
601 return SkBitmap::kRGB_565_Config;
602
603 default:
604 return SkBitmap::kARGB_8888_Config; // default answer
605 }
606#else
607 return SkBitmap::kARGB_8888_Config; // default answer
608#endif
609}
610
611static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
612 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
613}
614
615int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
616 SaveFlags flags) {
617 // do this before we create the layer. We don't call the public save() since
618 // that would invoke a possibly overridden virtual
619 int count = this->internalSave(flags);
620
621 fDeviceCMDirty = true;
622
623 SkIRect ir;
624 const SkIRect& clipBounds = this->getTotalClip().getBounds();
625
626 if (NULL != bounds) {
627 SkRect r;
628
629 this->getTotalMatrix().mapRect(&r, *bounds);
630 r.roundOut(&ir);
631 // early exit if the layer's bounds are clipped out
632 if (!ir.intersect(clipBounds)) {
633 if (bounds_affects_clip(flags))
634 fMCRec->fRegion->setEmpty();
635 return count;
636 }
637 } else { // no user bounds, so just use the clip
638 ir = clipBounds;
639 }
640
641 // early exit if the clip is now empty
642 if (bounds_affects_clip(flags) &&
643 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
644 return count;
645 }
646
647 bool isOpaque;
648 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
649
650 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
651 isOpaque, true);
652 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
653 device->unref();
654
655 layer->fNext = fMCRec->fTopLayer;
656 fMCRec->fLayer = layer;
657 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
658
659 return count;
660}
661
662int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
663 SaveFlags flags) {
664 if (0xFF == alpha) {
665 return this->saveLayer(bounds, NULL, flags);
666 } else {
667 SkPaint tmpPaint;
668 tmpPaint.setAlpha(alpha);
669 return this->saveLayer(bounds, &tmpPaint, flags);
670 }
671}
672
673void SkCanvas::restore() {
674 // check for underflow
675 if (fMCStack.count() > 1) {
676 this->internalRestore();
677 }
678}
679
680void SkCanvas::internalRestore() {
681 SkASSERT(fMCStack.count() != 0);
682
683 fDeviceCMDirty = true;
684 fLocalBoundsCompareTypeDirty = true;
685
686 // reserve our layer (if any)
687 DeviceCM* layer = fMCRec->fLayer; // may be null
688 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
689 fMCRec->fLayer = NULL;
690
691 // now do the normal restore()
692 fMCRec->~MCRec(); // balanced in save()
693 fMCStack.pop_back();
694 fMCRec = (MCRec*)fMCStack.back();
695
696 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
697 since if we're being recorded, we don't want to record this (the
698 recorder will have already recorded the restore).
699 */
700 if (NULL != layer) {
701 if (layer->fNext) {
702 this->drawDevice(layer->fDevice, layer->fX, layer->fY,
703 layer->fPaint);
704 // reset this, since drawDevice will have set it to true
705 fDeviceCMDirty = true;
706 }
707 SkDELETE(layer);
708 }
709}
710
711int SkCanvas::getSaveCount() const {
712 return fMCStack.count();
713}
714
715void SkCanvas::restoreToCount(int count) {
716 // sanity check
717 if (count < 1) {
718 count = 1;
719 }
720 while (fMCStack.count() > count) {
721 this->restore();
722 }
723}
724
725/////////////////////////////////////////////////////////////////////////////
726
727// can't draw it if its empty, or its too big for a fixed-point width or height
728static bool reject_bitmap(const SkBitmap& bitmap) {
729 return bitmap.width() <= 0 || bitmap.height() <= 0 ||
730 bitmap.width() > 32767 || bitmap.height() > 32767;
731}
732
733void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
734 const SkMatrix& matrix, const SkPaint* paint) {
735 if (reject_bitmap(bitmap)) {
736 return;
737 }
738
739 if (NULL == paint) {
740 SkPaint tmpPaint;
741 this->commonDrawBitmap(bitmap, matrix, tmpPaint);
742 } else {
743 this->commonDrawBitmap(bitmap, matrix, *paint);
744 }
745}
746
747void SkCanvas::drawDevice(SkDevice* device, int x, int y,
748 const SkPaint* paint) {
749 SkPaint tmp;
750 if (NULL == paint) {
751 tmp.setDither(true);
752 paint = &tmp;
753 }
754
755 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
756 while (iter.next()) {
757 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
758 *paint);
759 }
760 ITER_END
761}
762
763/////////////////////////////////////////////////////////////////////////////
764
765bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
766 fDeviceCMDirty = true;
767 fLocalBoundsCompareTypeDirty = true;
768 return fMCRec->fMatrix->preTranslate(dx, dy);
769}
770
771bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
772 fDeviceCMDirty = true;
773 fLocalBoundsCompareTypeDirty = true;
774 return fMCRec->fMatrix->preScale(sx, sy);
775}
776
777bool SkCanvas::rotate(SkScalar degrees) {
778 fDeviceCMDirty = true;
779 fLocalBoundsCompareTypeDirty = true;
780 return fMCRec->fMatrix->preRotate(degrees);
781}
782
783bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
784 fDeviceCMDirty = true;
785 fLocalBoundsCompareTypeDirty = true;
786 return fMCRec->fMatrix->preSkew(sx, sy);
787}
788
789bool SkCanvas::concat(const SkMatrix& matrix) {
790 fDeviceCMDirty = true;
791 fLocalBoundsCompareTypeDirty = true;
792 return fMCRec->fMatrix->preConcat(matrix);
793}
794
795void SkCanvas::setMatrix(const SkMatrix& matrix) {
796 fDeviceCMDirty = true;
797 fLocalBoundsCompareTypeDirty = true;
798 *fMCRec->fMatrix = matrix;
799}
800
801// this is not virtual, so it must call a virtual method so that subclasses
802// will see its action
803void SkCanvas::resetMatrix() {
804 SkMatrix matrix;
805
806 matrix.reset();
807 this->setMatrix(matrix);
808}
809
810//////////////////////////////////////////////////////////////////////////////
811
812bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
813 fDeviceCMDirty = true;
814 fLocalBoundsCompareTypeDirty = true;
815
816 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000817 // for these simpler matrices, we can stay a rect ever after applying
818 // the matrix. This means we don't have to a) make a path, and b) tell
819 // the region code to scan-convert the path, only to discover that it
820 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000821 SkRect r;
822 SkIRect ir;
823
824 fMCRec->fMatrix->mapRect(&r, rect);
825 r.round(&ir);
826 return fMCRec->fRegion->op(ir, op);
827 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000828 // since we're rotate or some such thing, we convert the rect to a path
829 // and clip against that, since it can handle any matrix. However, to
830 // avoid recursion in the case where we are subclassed (e.g. Pictures)
831 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 SkPath path;
833
834 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000835 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 }
837}
838
839bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
840 fDeviceCMDirty = true;
841 fLocalBoundsCompareTypeDirty = true;
842
843 SkPath devPath;
844 path.transform(*fMCRec->fMatrix, &devPath);
845
846 if (SkRegion::kIntersect_Op == op) {
847 return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
848 } else {
849 SkRegion base;
850 const SkBitmap& bm = this->getDevice()->accessBitmap(false);
851 base.setRect(0, 0, bm.width(), bm.height());
852
853 if (SkRegion::kReplace_Op == op) {
854 return fMCRec->fRegion->setPath(devPath, base);
855 } else {
856 SkRegion rgn;
857 rgn.setPath(devPath, base);
858 return fMCRec->fRegion->op(rgn, op);
859 }
860 }
861}
862
863bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
864 fDeviceCMDirty = true;
865 fLocalBoundsCompareTypeDirty = true;
866
867 return fMCRec->fRegion->op(rgn, op);
868}
869
870void SkCanvas::computeLocalClipBoundsCompareType() const {
871 SkRect r;
872
873 if (!this->getClipBounds(&r, kAA_EdgeType)) {
874 fLocalBoundsCompareType.setEmpty();
875 } else {
876 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
877 SkScalarToCompareType(r.fTop),
878 SkScalarToCompareType(r.fRight),
879 SkScalarToCompareType(r.fBottom));
880 }
881}
882
reed@android.comd252db02009-04-01 18:31:44 +0000883/* current impl ignores edgetype, and relies on
884 getLocalClipBoundsCompareType(), which always returns a value assuming
885 antialiasing (worst case)
886 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887bool SkCanvas::quickReject(const SkRect& rect, EdgeType) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 if (fMCRec->fRegion->isEmpty()) {
889 return true;
890 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891
reed@android.comd252db02009-04-01 18:31:44 +0000892 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
893
894 // for speed, do the most likely reject compares first
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
896 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
reed@android.comd252db02009-04-01 18:31:44 +0000897 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 return true;
899 }
reed@android.comd252db02009-04-01 18:31:44 +0000900 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
901 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
902 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
903 return true;
904 }
905 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906}
907
908bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +0000909 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910}
911
912bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
913 /* current impl ignores edgetype, and relies on
914 getLocalClipBoundsCompareType(), which always returns a value assuming
915 antialiasing (worst case)
916 */
917
918 if (fMCRec->fRegion->isEmpty()) {
919 return true;
920 }
921
reed@android.comaefd2bc2009-03-30 21:02:14 +0000922 SkScalarCompareType userT = SkScalarToCompareType(top);
923 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924
925 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +0000926 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 if (userT >= userB) {
928 return true;
929 }
930
931 // check if we are above or below the local clip bounds
932 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
933 return userT >= clipR.fBottom || userB <= clipR.fTop;
934}
935
936bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
937 const SkRegion& clip = *fMCRec->fRegion;
938 if (clip.isEmpty()) {
939 if (bounds) {
940 bounds->setEmpty();
941 }
942 return false;
943 }
944
reed@android.comd9c0f0b2009-02-06 22:39:37 +0000945 SkMatrix inverse;
946 // if we can't invert the CTM, we can't return local clip bounds
947 if (!fMCRec->fMatrix->invert(&inverse)) {
948 return false;
949 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950
reed@android.comd9c0f0b2009-02-06 22:39:37 +0000951 if (NULL != bounds) {
952 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953 // get the clip's bounds
954 const SkIRect& ibounds = clip.getBounds();
955 // adjust it outwards if we are antialiasing
956 int inset = (kAA_EdgeType == et);
957 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
958 ibounds.fRight + inset, ibounds.fBottom + inset);
959
960 // invert into local coordinates
961 inverse.mapRect(bounds, r);
962 }
963 return true;
964}
965
966const SkMatrix& SkCanvas::getTotalMatrix() const {
967 return *fMCRec->fMatrix;
968}
969
970const SkRegion& SkCanvas::getTotalClip() const {
971 return *fMCRec->fRegion;
972}
973
974///////////////////////////////////////////////////////////////////////////////
975
976SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width,
977 int height, bool isOpaque, bool isForLayer) {
978 SkBitmap bitmap;
979
980 bitmap.setConfig(config, width, height);
981 bitmap.setIsOpaque(isOpaque);
982
983 // should this happen in the device subclass?
984 bitmap.allocPixels();
985 if (!bitmap.isOpaque()) {
986 bitmap.eraseARGB(0, 0, 0, 0);
987 }
988
989 return SkNEW_ARGS(SkDevice, (bitmap));
990}
991
992//////////////////////////////////////////////////////////////////////////////
993// These are the virtual drawing methods
994//////////////////////////////////////////////////////////////////////////////
995
996void SkCanvas::drawPaint(const SkPaint& paint) {
997 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
998
999 while (iter.next()) {
1000 iter.fDevice->drawPaint(iter, paint);
1001 }
1002
1003 ITER_END
1004}
1005
1006void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1007 const SkPaint& paint) {
1008 if ((long)count <= 0) {
1009 return;
1010 }
1011
1012 SkASSERT(pts != NULL);
1013
1014 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
1015
1016 while (iter.next()) {
1017 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1018 }
1019
1020 ITER_END
1021}
1022
1023void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1024 if (paint.canComputeFastBounds()) {
1025 SkRect storage;
1026 if (this->quickReject(paint.computeFastBounds(r, &storage),
1027 paint2EdgeType(&paint))) {
1028 return;
1029 }
1030 }
1031
1032 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1033
1034 while (iter.next()) {
1035 iter.fDevice->drawRect(iter, r, paint);
1036 }
1037
1038 ITER_END
1039}
1040
1041void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1042 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001043 SkRect storage;
1044 const SkRect& bounds = path.getBounds();
1045 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001046 paint2EdgeType(&paint))) {
1047 return;
1048 }
1049 }
1050
1051 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1052
1053 while (iter.next()) {
1054 iter.fDevice->drawPath(iter, path, paint);
1055 }
1056
1057 ITER_END
1058}
1059
1060void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1061 const SkPaint* paint) {
1062 SkDEBUGCODE(bitmap.validate();)
1063
1064 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1065 SkRect fastBounds;
1066 fastBounds.set(x, y,
1067 x + SkIntToScalar(bitmap.width()),
1068 y + SkIntToScalar(bitmap.height()));
1069 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1070 return;
1071 }
1072 }
1073
1074 SkMatrix matrix;
1075 matrix.setTranslate(x, y);
1076 this->internalDrawBitmap(bitmap, matrix, paint);
1077}
1078
1079void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1080 const SkRect& dst, const SkPaint* paint) {
1081 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1082 return;
1083 }
1084
1085 // do this now, to avoid the cost of calling extract for RLE bitmaps
1086 if (this->quickReject(dst, paint2EdgeType(paint))) {
1087 return;
1088 }
1089
1090 SkBitmap tmp; // storage if we need a subset of bitmap
1091 const SkBitmap* bitmapPtr = &bitmap;
1092
1093 if (NULL != src) {
1094 if (!bitmap.extractSubset(&tmp, *src)) {
1095 return; // extraction failed
1096 }
1097 bitmapPtr = &tmp;
1098 }
1099
1100 SkScalar width = SkIntToScalar(bitmapPtr->width());
1101 SkScalar height = SkIntToScalar(bitmapPtr->height());
1102 SkMatrix matrix;
1103
1104 if (dst.width() == width && dst.height() == height) {
1105 matrix.setTranslate(dst.fLeft, dst.fTop);
1106 } else {
1107 SkRect tmpSrc;
1108 tmpSrc.set(0, 0, width, height);
1109 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
1110 }
1111 this->internalDrawBitmap(*bitmapPtr, matrix, paint);
1112}
1113
1114void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1115 const SkPaint* paint) {
1116 SkDEBUGCODE(bitmap.validate();)
1117 this->internalDrawBitmap(bitmap, matrix, paint);
1118}
1119
1120void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
1121 const SkPaint& paint) {
1122 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001123
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001125
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 while (iter.next()) {
1127 iter.fDevice->drawBitmap(iter, bitmap, matrix, paint);
1128 }
reed@android.com9b039062009-02-11 15:09:58 +00001129
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 ITER_END
1131}
1132
1133void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1134 const SkPaint* paint) {
1135 SkDEBUGCODE(bitmap.validate();)
1136
1137 if (reject_bitmap(bitmap)) {
1138 return;
1139 }
1140
1141 SkPaint tmp;
1142 if (NULL == paint) {
1143 paint = &tmp;
1144 }
1145
1146 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
1147
1148 while (iter.next()) {
1149 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1150 *paint);
1151 }
1152 ITER_END
1153}
1154
1155void SkCanvas::drawText(const void* text, size_t byteLength,
1156 SkScalar x, SkScalar y, const SkPaint& paint) {
1157 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1158
1159 while (iter.next()) {
1160 iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
1161 }
1162
1163 ITER_END
1164}
1165
1166void SkCanvas::drawPosText(const void* text, size_t byteLength,
1167 const SkPoint pos[], const SkPaint& paint) {
1168 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1169
1170 while (iter.next()) {
1171 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
1172 paint);
1173 }
1174
1175 ITER_END
1176}
1177
1178void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1179 const SkScalar xpos[], SkScalar constY,
1180 const SkPaint& paint) {
1181 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1182
1183 while (iter.next()) {
1184 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
1185 paint);
1186 }
1187
1188 ITER_END
1189}
1190
1191void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1192 const SkPath& path, const SkMatrix* matrix,
1193 const SkPaint& paint) {
1194 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1195
1196 while (iter.next()) {
1197 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1198 matrix, paint);
1199 }
1200
1201 ITER_END
1202}
1203
1204void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1205 const SkPoint verts[], const SkPoint texs[],
1206 const SkColor colors[], SkXfermode* xmode,
1207 const uint16_t indices[], int indexCount,
1208 const SkPaint& paint) {
1209 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1210
1211 while (iter.next()) {
1212 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1213 colors, xmode, indices, indexCount, paint);
1214 }
1215
1216 ITER_END
1217}
1218
1219//////////////////////////////////////////////////////////////////////////////
1220// These methods are NOT virtual, and therefore must call back into virtual
1221// methods, rather than actually drawing themselves.
1222//////////////////////////////////////////////////////////////////////////////
1223
1224void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
1225 SkPorterDuff::Mode mode) {
1226 SkPaint paint;
1227
1228 paint.setARGB(a, r, g, b);
1229 if (SkPorterDuff::kSrcOver_Mode != mode) {
1230 paint.setPorterDuffXfermode(mode);
1231 }
1232 this->drawPaint(paint);
1233}
1234
1235void SkCanvas::drawColor(SkColor c, SkPorterDuff::Mode mode) {
1236 SkPaint paint;
1237
1238 paint.setColor(c);
1239 if (SkPorterDuff::kSrcOver_Mode != mode) {
1240 paint.setPorterDuffXfermode(mode);
1241 }
1242 this->drawPaint(paint);
1243}
1244
1245void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1246 SkPoint pt;
1247
1248 pt.set(x, y);
1249 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1250}
1251
1252void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1253 SkPoint pt;
1254 SkPaint paint;
1255
1256 pt.set(x, y);
1257 paint.setColor(color);
1258 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1259}
1260
1261void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1262 const SkPaint& paint) {
1263 SkPoint pts[2];
1264
1265 pts[0].set(x0, y0);
1266 pts[1].set(x1, y1);
1267 this->drawPoints(kLines_PointMode, 2, pts, paint);
1268}
1269
1270void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1271 SkScalar right, SkScalar bottom,
1272 const SkPaint& paint) {
1273 SkRect r;
1274
1275 r.set(left, top, right, bottom);
1276 this->drawRect(r, paint);
1277}
1278
1279void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1280 const SkPaint& paint) {
1281 if (radius < 0) {
1282 radius = 0;
1283 }
1284
1285 SkRect r;
1286 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
1287
1288 if (paint.canComputeFastBounds()) {
1289 SkRect storage;
1290 if (this->quickReject(paint.computeFastBounds(r, &storage),
1291 paint2EdgeType(&paint))) {
1292 return;
1293 }
1294 }
1295
1296 SkPath path;
1297 path.addOval(r);
1298 this->drawPath(path, paint);
1299}
1300
1301void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1302 const SkPaint& paint) {
1303 if (rx > 0 && ry > 0) {
1304 if (paint.canComputeFastBounds()) {
1305 SkRect storage;
1306 if (this->quickReject(paint.computeFastBounds(r, &storage),
1307 paint2EdgeType(&paint))) {
1308 return;
1309 }
1310 }
1311
1312 SkPath path;
1313 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1314 this->drawPath(path, paint);
1315 } else {
1316 this->drawRect(r, paint);
1317 }
1318}
1319
1320void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1321 if (paint.canComputeFastBounds()) {
1322 SkRect storage;
1323 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1324 paint2EdgeType(&paint))) {
1325 return;
1326 }
1327 }
1328
1329 SkPath path;
1330 path.addOval(oval);
1331 this->drawPath(path, paint);
1332}
1333
1334void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1335 SkScalar sweepAngle, bool useCenter,
1336 const SkPaint& paint) {
1337 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1338 this->drawOval(oval, paint);
1339 } else {
1340 SkPath path;
1341 if (useCenter) {
1342 path.moveTo(oval.centerX(), oval.centerY());
1343 }
1344 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1345 if (useCenter) {
1346 path.close();
1347 }
1348 this->drawPath(path, paint);
1349 }
1350}
1351
1352void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1353 const SkPath& path, SkScalar hOffset,
1354 SkScalar vOffset, const SkPaint& paint) {
1355 SkMatrix matrix;
1356
1357 matrix.setTranslate(hOffset, vOffset);
1358 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1359}
1360
reed@android.comf76bacf2009-05-13 14:00:33 +00001361///////////////////////////////////////////////////////////////////////////////
1362
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363void SkCanvas::drawPicture(SkPicture& picture) {
1364 int saveCount = save();
1365 picture.draw(this);
1366 restoreToCount(saveCount);
1367}
1368
reed@android.comf76bacf2009-05-13 14:00:33 +00001369void SkCanvas::drawShape(SkShape* shape) {
1370 // shape baseclass takes care of save/restore
1371 shape->draw(this);
1372}
1373
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374///////////////////////////////////////////////////////////////////////////////
1375///////////////////////////////////////////////////////////////////////////////
1376
1377SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
1378 // need COMPILE_TIME_ASSERT
1379 SkASSERT(sizeof(fStorage) >= sizeof(SkDrawIter));
1380
1381 SkASSERT(canvas);
1382
1383 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1384 fDone = !fImpl->next();
1385}
1386
1387SkCanvas::LayerIter::~LayerIter() {
1388 fImpl->~SkDrawIter();
1389}
1390
1391void SkCanvas::LayerIter::next() {
1392 fDone = !fImpl->next();
1393}
1394
1395SkDevice* SkCanvas::LayerIter::device() const {
1396 return fImpl->getDevice();
1397}
1398
1399const SkMatrix& SkCanvas::LayerIter::matrix() const {
1400 return fImpl->getMatrix();
1401}
1402
1403const SkPaint& SkCanvas::LayerIter::paint() const {
1404 const SkPaint* paint = fImpl->getPaint();
1405 if (NULL == paint) {
1406 paint = &fDefaultPaint;
1407 }
1408 return *paint;
1409}
1410
1411const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1412int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1413int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1414