blob: 23031c534ac75aa1af973f04229c37df968212f3 [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
reed@android.com199f1082009-06-10 02:12:47 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 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;
reed@android.com199f1082009-06-10 02:12:47 +0000393 fLastDeviceToGainFocus = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394
395 fMCRec = (MCRec*)fMCStack.push_back();
396 new (fMCRec) MCRec(NULL, 0);
397
398 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
399 fMCRec->fTopLayer = fMCRec->fLayer;
400 fMCRec->fNext = NULL;
401
402 return this->setDevice(device);
403}
404
405SkCanvas::SkCanvas(SkDevice* device)
406 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
407 inc_canvas();
408
409 this->init(device);
410}
411
412SkCanvas::SkCanvas(const SkBitmap& bitmap)
413 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
414 inc_canvas();
415
416 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
417}
418
419SkCanvas::~SkCanvas() {
420 // free up the contents of our deque
421 this->restoreToCount(1); // restore everything but the last
422 this->internalRestore(); // restore the last, since we're going away
423
424 fBounder->safeUnref();
425
426 dec_canvas();
427}
428
429SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
430 SkRefCnt_SafeAssign(fBounder, bounder);
431 return bounder;
432}
433
434SkDrawFilter* SkCanvas::getDrawFilter() const {
435 return fMCRec->fFilter;
436}
437
438SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
439 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
440 return filter;
441}
442
443///////////////////////////////////////////////////////////////////////////////
444
445SkDevice* SkCanvas::getDevice() const {
446 // return root device
447 SkDeque::Iter iter(fMCStack);
448 MCRec* rec = (MCRec*)iter.next();
449 SkASSERT(rec && rec->fLayer);
450 return rec->fLayer->fDevice;
451}
452
453SkDevice* SkCanvas::setDevice(SkDevice* device) {
454 // return root device
455 SkDeque::Iter iter(fMCStack);
456 MCRec* rec = (MCRec*)iter.next();
457 SkASSERT(rec && rec->fLayer);
458 SkDevice* rootDevice = rec->fLayer->fDevice;
459
460 if (rootDevice == device) {
461 return device;
462 }
463
464 /* Notify the devices that they are going in/out of scope, so they can do
465 things like lock/unlock their pixels, etc.
466 */
467 if (device) {
468 device->lockPixels();
469 }
470 if (rootDevice) {
471 rootDevice->unlockPixels();
472 }
473
474 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
475 rootDevice = device;
476
477 fDeviceCMDirty = true;
478
479 /* Now we update our initial region to have the bounds of the new device,
480 and then intersect all of the clips in our stack with these bounds,
481 to ensure that we can't draw outside of the device's bounds (and trash
482 memory).
483
484 NOTE: this is only a partial-fix, since if the new device is larger than
485 the previous one, we don't know how to "enlarge" the clips in our stack,
486 so drawing may be artificially restricted. Without keeping a history of
487 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
488 reconstruct the correct clips, so this approximation will have to do.
489 The caller really needs to restore() back to the base if they want to
490 accurately take advantage of the new device bounds.
491 */
492
493 if (NULL == device) {
494 rec->fRegion->setEmpty();
495 while ((rec = (MCRec*)iter.next()) != NULL) {
496 (void)rec->fRegion->setEmpty();
497 }
498 } else {
499 // compute our total bounds for all devices
500 SkIRect bounds;
501
502 bounds.set(0, 0, device->width(), device->height());
503
504 // now jam our 1st clip to be bounds, and intersect the rest with that
505 rec->fRegion->setRect(bounds);
506 while ((rec = (MCRec*)iter.next()) != NULL) {
507 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
508 }
509 }
510 return device;
511}
512
513SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
514 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
515 device->unref();
516 return device;
517}
518
519//////////////////////////////////////////////////////////////////////////////
520
521bool SkCanvas::getViewport(SkIPoint* size) const {
522 return false;
523}
524
525bool SkCanvas::setViewport(int width, int height) {
526 return false;
527}
528
529void SkCanvas::updateDeviceCMCache() {
530 if (fDeviceCMDirty) {
531 const SkMatrix& totalMatrix = this->getTotalMatrix();
532 const SkRegion& totalClip = this->getTotalClip();
533 DeviceCM* layer = fMCRec->fTopLayer;
534
535 if (NULL == layer->fNext) { // only one layer
536 layer->updateMC(totalMatrix, totalClip, NULL);
537 } else {
538 SkRegion clip;
539 clip = totalClip; // make a copy
540 do {
541 layer->updateMC(totalMatrix, clip, &clip);
542 } while ((layer = layer->fNext) != NULL);
543 }
544 fDeviceCMDirty = false;
545 }
546}
547
548void SkCanvas::prepareForDeviceDraw(SkDevice* device) {
549 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000550 if (fLastDeviceToGainFocus != device) {
551 device->gainFocus(this);
552 fLastDeviceToGainFocus = device;
553 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554}
555
556///////////////////////////////////////////////////////////////////////////////
557
558int SkCanvas::internalSave(SaveFlags flags) {
559 int saveCount = this->getSaveCount(); // record this before the actual save
560
561 MCRec* newTop = (MCRec*)fMCStack.push_back();
562 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
563
564 newTop->fNext = fMCRec;
565 fMCRec = newTop;
566
567 return saveCount;
568}
569
570int SkCanvas::save(SaveFlags flags) {
571 // call shared impl
572 return this->internalSave(flags);
573}
574
575#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
576#define C16MASK (1 << SkBitmap::kRGB_565_Config)
577#define C8MASK (1 << SkBitmap::kA8_Config)
578
579static SkBitmap::Config resolve_config(SkCanvas* canvas,
580 const SkIRect& bounds,
581 SkCanvas::SaveFlags flags,
582 bool* isOpaque) {
583 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
584
585#if 0
586 // loop through and union all the configs we may draw into
587 uint32_t configMask = 0;
588 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
589 {
590 SkDevice* device = canvas->getLayerDevice(i);
591 if (device->intersects(bounds))
592 configMask |= 1 << device->config();
593 }
594
595 // if the caller wants alpha or fullcolor, we can't return 565
596 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
597 SkCanvas::kHasAlphaLayer_SaveFlag))
598 configMask &= ~C16MASK;
599
600 switch (configMask) {
601 case C8MASK: // if we only have A8, return that
602 return SkBitmap::kA8_Config;
603
604 case C16MASK: // if we only have 565, return that
605 return SkBitmap::kRGB_565_Config;
606
607 default:
608 return SkBitmap::kARGB_8888_Config; // default answer
609 }
610#else
611 return SkBitmap::kARGB_8888_Config; // default answer
612#endif
613}
614
615static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
616 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
617}
618
619int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
620 SaveFlags flags) {
621 // do this before we create the layer. We don't call the public save() since
622 // that would invoke a possibly overridden virtual
623 int count = this->internalSave(flags);
624
625 fDeviceCMDirty = true;
626
627 SkIRect ir;
628 const SkIRect& clipBounds = this->getTotalClip().getBounds();
629
630 if (NULL != bounds) {
631 SkRect r;
632
633 this->getTotalMatrix().mapRect(&r, *bounds);
634 r.roundOut(&ir);
635 // early exit if the layer's bounds are clipped out
636 if (!ir.intersect(clipBounds)) {
637 if (bounds_affects_clip(flags))
638 fMCRec->fRegion->setEmpty();
639 return count;
640 }
641 } else { // no user bounds, so just use the clip
642 ir = clipBounds;
643 }
644
645 // early exit if the clip is now empty
646 if (bounds_affects_clip(flags) &&
647 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
648 return count;
649 }
650
651 bool isOpaque;
652 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
653
654 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
655 isOpaque, true);
656 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
657 device->unref();
658
659 layer->fNext = fMCRec->fTopLayer;
660 fMCRec->fLayer = layer;
661 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
662
663 return count;
664}
665
666int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
667 SaveFlags flags) {
668 if (0xFF == alpha) {
669 return this->saveLayer(bounds, NULL, flags);
670 } else {
671 SkPaint tmpPaint;
672 tmpPaint.setAlpha(alpha);
673 return this->saveLayer(bounds, &tmpPaint, flags);
674 }
675}
676
677void SkCanvas::restore() {
678 // check for underflow
679 if (fMCStack.count() > 1) {
680 this->internalRestore();
681 }
682}
683
684void SkCanvas::internalRestore() {
685 SkASSERT(fMCStack.count() != 0);
686
687 fDeviceCMDirty = true;
688 fLocalBoundsCompareTypeDirty = true;
689
690 // reserve our layer (if any)
691 DeviceCM* layer = fMCRec->fLayer; // may be null
692 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
693 fMCRec->fLayer = NULL;
694
695 // now do the normal restore()
696 fMCRec->~MCRec(); // balanced in save()
697 fMCStack.pop_back();
698 fMCRec = (MCRec*)fMCStack.back();
699
700 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
701 since if we're being recorded, we don't want to record this (the
702 recorder will have already recorded the restore).
703 */
704 if (NULL != layer) {
705 if (layer->fNext) {
706 this->drawDevice(layer->fDevice, layer->fX, layer->fY,
707 layer->fPaint);
708 // reset this, since drawDevice will have set it to true
709 fDeviceCMDirty = true;
710 }
711 SkDELETE(layer);
712 }
713}
714
715int SkCanvas::getSaveCount() const {
716 return fMCStack.count();
717}
718
719void SkCanvas::restoreToCount(int count) {
720 // sanity check
721 if (count < 1) {
722 count = 1;
723 }
724 while (fMCStack.count() > count) {
725 this->restore();
726 }
727}
728
729/////////////////////////////////////////////////////////////////////////////
730
731// can't draw it if its empty, or its too big for a fixed-point width or height
732static bool reject_bitmap(const SkBitmap& bitmap) {
733 return bitmap.width() <= 0 || bitmap.height() <= 0 ||
734 bitmap.width() > 32767 || bitmap.height() > 32767;
735}
736
737void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
738 const SkMatrix& matrix, const SkPaint* paint) {
739 if (reject_bitmap(bitmap)) {
740 return;
741 }
742
743 if (NULL == paint) {
744 SkPaint tmpPaint;
745 this->commonDrawBitmap(bitmap, matrix, tmpPaint);
746 } else {
747 this->commonDrawBitmap(bitmap, matrix, *paint);
748 }
749}
750
751void SkCanvas::drawDevice(SkDevice* device, int x, int y,
752 const SkPaint* paint) {
753 SkPaint tmp;
754 if (NULL == paint) {
755 tmp.setDither(true);
756 paint = &tmp;
757 }
758
759 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
760 while (iter.next()) {
761 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
762 *paint);
763 }
764 ITER_END
765}
766
767/////////////////////////////////////////////////////////////////////////////
768
769bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
770 fDeviceCMDirty = true;
771 fLocalBoundsCompareTypeDirty = true;
772 return fMCRec->fMatrix->preTranslate(dx, dy);
773}
774
775bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
776 fDeviceCMDirty = true;
777 fLocalBoundsCompareTypeDirty = true;
778 return fMCRec->fMatrix->preScale(sx, sy);
779}
780
781bool SkCanvas::rotate(SkScalar degrees) {
782 fDeviceCMDirty = true;
783 fLocalBoundsCompareTypeDirty = true;
784 return fMCRec->fMatrix->preRotate(degrees);
785}
786
787bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
788 fDeviceCMDirty = true;
789 fLocalBoundsCompareTypeDirty = true;
790 return fMCRec->fMatrix->preSkew(sx, sy);
791}
792
793bool SkCanvas::concat(const SkMatrix& matrix) {
794 fDeviceCMDirty = true;
795 fLocalBoundsCompareTypeDirty = true;
796 return fMCRec->fMatrix->preConcat(matrix);
797}
798
799void SkCanvas::setMatrix(const SkMatrix& matrix) {
800 fDeviceCMDirty = true;
801 fLocalBoundsCompareTypeDirty = true;
802 *fMCRec->fMatrix = matrix;
803}
804
805// this is not virtual, so it must call a virtual method so that subclasses
806// will see its action
807void SkCanvas::resetMatrix() {
808 SkMatrix matrix;
809
810 matrix.reset();
811 this->setMatrix(matrix);
812}
813
814//////////////////////////////////////////////////////////////////////////////
815
816bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
817 fDeviceCMDirty = true;
818 fLocalBoundsCompareTypeDirty = true;
819
820 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000821 // for these simpler matrices, we can stay a rect ever after applying
822 // the matrix. This means we don't have to a) make a path, and b) tell
823 // the region code to scan-convert the path, only to discover that it
824 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 SkRect r;
826 SkIRect ir;
827
828 fMCRec->fMatrix->mapRect(&r, rect);
829 r.round(&ir);
830 return fMCRec->fRegion->op(ir, op);
831 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000832 // since we're rotate or some such thing, we convert the rect to a path
833 // and clip against that, since it can handle any matrix. However, to
834 // avoid recursion in the case where we are subclassed (e.g. Pictures)
835 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 SkPath path;
837
838 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000839 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 }
841}
842
843bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
844 fDeviceCMDirty = true;
845 fLocalBoundsCompareTypeDirty = true;
846
847 SkPath devPath;
848 path.transform(*fMCRec->fMatrix, &devPath);
849
850 if (SkRegion::kIntersect_Op == op) {
851 return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
852 } else {
853 SkRegion base;
854 const SkBitmap& bm = this->getDevice()->accessBitmap(false);
855 base.setRect(0, 0, bm.width(), bm.height());
856
857 if (SkRegion::kReplace_Op == op) {
858 return fMCRec->fRegion->setPath(devPath, base);
859 } else {
860 SkRegion rgn;
861 rgn.setPath(devPath, base);
862 return fMCRec->fRegion->op(rgn, op);
863 }
864 }
865}
866
867bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
868 fDeviceCMDirty = true;
869 fLocalBoundsCompareTypeDirty = true;
870
871 return fMCRec->fRegion->op(rgn, op);
872}
873
874void SkCanvas::computeLocalClipBoundsCompareType() const {
875 SkRect r;
876
877 if (!this->getClipBounds(&r, kAA_EdgeType)) {
878 fLocalBoundsCompareType.setEmpty();
879 } else {
880 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
881 SkScalarToCompareType(r.fTop),
882 SkScalarToCompareType(r.fRight),
883 SkScalarToCompareType(r.fBottom));
884 }
885}
886
reed@android.comd252db02009-04-01 18:31:44 +0000887/* current impl ignores edgetype, and relies on
888 getLocalClipBoundsCompareType(), which always returns a value assuming
889 antialiasing (worst case)
890 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891bool SkCanvas::quickReject(const SkRect& rect, EdgeType) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 if (fMCRec->fRegion->isEmpty()) {
893 return true;
894 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895
reed@android.coma380ae42009-07-21 01:17:02 +0000896 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
897 SkRect dst;
898 fMCRec->fMatrix->mapRect(&dst, rect);
899 SkIRect idst;
900 dst.roundOut(&idst);
901 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
902 } else {
903 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +0000904
reed@android.coma380ae42009-07-21 01:17:02 +0000905 // for speed, do the most likely reject compares first
906 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
907 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
908 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
909 return true;
910 }
911 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
912 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
913 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
914 return true;
915 }
916 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918}
919
920bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +0000921 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922}
923
924bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
925 /* current impl ignores edgetype, and relies on
926 getLocalClipBoundsCompareType(), which always returns a value assuming
927 antialiasing (worst case)
928 */
929
930 if (fMCRec->fRegion->isEmpty()) {
931 return true;
932 }
933
reed@android.comaefd2bc2009-03-30 21:02:14 +0000934 SkScalarCompareType userT = SkScalarToCompareType(top);
935 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936
937 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +0000938 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 if (userT >= userB) {
940 return true;
941 }
942
943 // check if we are above or below the local clip bounds
944 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
945 return userT >= clipR.fBottom || userB <= clipR.fTop;
946}
947
948bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
949 const SkRegion& clip = *fMCRec->fRegion;
950 if (clip.isEmpty()) {
951 if (bounds) {
952 bounds->setEmpty();
953 }
954 return false;
955 }
956
reed@android.comd9c0f0b2009-02-06 22:39:37 +0000957 SkMatrix inverse;
958 // if we can't invert the CTM, we can't return local clip bounds
959 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +0000960 if (bounds) {
961 bounds->setEmpty();
962 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +0000963 return false;
964 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000965
reed@android.comd9c0f0b2009-02-06 22:39:37 +0000966 if (NULL != bounds) {
967 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 // get the clip's bounds
969 const SkIRect& ibounds = clip.getBounds();
970 // adjust it outwards if we are antialiasing
971 int inset = (kAA_EdgeType == et);
972 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
973 ibounds.fRight + inset, ibounds.fBottom + inset);
974
975 // invert into local coordinates
976 inverse.mapRect(bounds, r);
977 }
978 return true;
979}
980
981const SkMatrix& SkCanvas::getTotalMatrix() const {
982 return *fMCRec->fMatrix;
983}
984
985const SkRegion& SkCanvas::getTotalClip() const {
986 return *fMCRec->fRegion;
987}
988
989///////////////////////////////////////////////////////////////////////////////
990
991SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width,
992 int height, bool isOpaque, bool isForLayer) {
993 SkBitmap bitmap;
994
995 bitmap.setConfig(config, width, height);
996 bitmap.setIsOpaque(isOpaque);
997
998 // should this happen in the device subclass?
999 bitmap.allocPixels();
1000 if (!bitmap.isOpaque()) {
1001 bitmap.eraseARGB(0, 0, 0, 0);
1002 }
1003
1004 return SkNEW_ARGS(SkDevice, (bitmap));
1005}
1006
1007//////////////////////////////////////////////////////////////////////////////
1008// These are the virtual drawing methods
1009//////////////////////////////////////////////////////////////////////////////
1010
1011void SkCanvas::drawPaint(const SkPaint& paint) {
1012 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
1013
1014 while (iter.next()) {
1015 iter.fDevice->drawPaint(iter, paint);
1016 }
1017
1018 ITER_END
1019}
1020
1021void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1022 const SkPaint& paint) {
1023 if ((long)count <= 0) {
1024 return;
1025 }
1026
1027 SkASSERT(pts != NULL);
1028
1029 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
1030
1031 while (iter.next()) {
1032 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1033 }
1034
1035 ITER_END
1036}
1037
1038void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1039 if (paint.canComputeFastBounds()) {
1040 SkRect storage;
1041 if (this->quickReject(paint.computeFastBounds(r, &storage),
1042 paint2EdgeType(&paint))) {
1043 return;
1044 }
1045 }
1046
1047 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1048
1049 while (iter.next()) {
1050 iter.fDevice->drawRect(iter, r, paint);
1051 }
1052
1053 ITER_END
1054}
1055
1056void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1057 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001058 SkRect storage;
1059 const SkRect& bounds = path.getBounds();
1060 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 paint2EdgeType(&paint))) {
1062 return;
1063 }
1064 }
1065
1066 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1067
1068 while (iter.next()) {
1069 iter.fDevice->drawPath(iter, path, paint);
1070 }
1071
1072 ITER_END
1073}
1074
1075void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1076 const SkPaint* paint) {
1077 SkDEBUGCODE(bitmap.validate();)
1078
1079 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1080 SkRect fastBounds;
1081 fastBounds.set(x, y,
1082 x + SkIntToScalar(bitmap.width()),
1083 y + SkIntToScalar(bitmap.height()));
1084 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1085 return;
1086 }
1087 }
1088
1089 SkMatrix matrix;
1090 matrix.setTranslate(x, y);
1091 this->internalDrawBitmap(bitmap, matrix, paint);
1092}
1093
1094void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1095 const SkRect& dst, const SkPaint* paint) {
1096 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1097 return;
1098 }
1099
1100 // do this now, to avoid the cost of calling extract for RLE bitmaps
1101 if (this->quickReject(dst, paint2EdgeType(paint))) {
1102 return;
1103 }
1104
1105 SkBitmap tmp; // storage if we need a subset of bitmap
1106 const SkBitmap* bitmapPtr = &bitmap;
1107
1108 if (NULL != src) {
1109 if (!bitmap.extractSubset(&tmp, *src)) {
1110 return; // extraction failed
1111 }
1112 bitmapPtr = &tmp;
1113 }
1114
1115 SkScalar width = SkIntToScalar(bitmapPtr->width());
1116 SkScalar height = SkIntToScalar(bitmapPtr->height());
1117 SkMatrix matrix;
1118
1119 if (dst.width() == width && dst.height() == height) {
1120 matrix.setTranslate(dst.fLeft, dst.fTop);
1121 } else {
1122 SkRect tmpSrc;
1123 tmpSrc.set(0, 0, width, height);
1124 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
1125 }
1126 this->internalDrawBitmap(*bitmapPtr, matrix, paint);
1127}
1128
1129void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1130 const SkPaint* paint) {
1131 SkDEBUGCODE(bitmap.validate();)
1132 this->internalDrawBitmap(bitmap, matrix, paint);
1133}
1134
1135void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
1136 const SkPaint& paint) {
1137 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001138
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001140
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 while (iter.next()) {
1142 iter.fDevice->drawBitmap(iter, bitmap, matrix, paint);
1143 }
reed@android.com9b039062009-02-11 15:09:58 +00001144
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 ITER_END
1146}
1147
1148void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1149 const SkPaint* paint) {
1150 SkDEBUGCODE(bitmap.validate();)
1151
1152 if (reject_bitmap(bitmap)) {
1153 return;
1154 }
1155
1156 SkPaint tmp;
1157 if (NULL == paint) {
1158 paint = &tmp;
1159 }
1160
1161 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
1162
1163 while (iter.next()) {
1164 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1165 *paint);
1166 }
1167 ITER_END
1168}
1169
1170void SkCanvas::drawText(const void* text, size_t byteLength,
1171 SkScalar x, SkScalar y, const SkPaint& paint) {
1172 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1173
1174 while (iter.next()) {
1175 iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
1176 }
1177
1178 ITER_END
1179}
1180
1181void SkCanvas::drawPosText(const void* text, size_t byteLength,
1182 const SkPoint pos[], const SkPaint& paint) {
1183 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1184
1185 while (iter.next()) {
1186 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
1187 paint);
1188 }
1189
1190 ITER_END
1191}
1192
1193void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1194 const SkScalar xpos[], SkScalar constY,
1195 const SkPaint& paint) {
1196 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1197
1198 while (iter.next()) {
1199 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
1200 paint);
1201 }
1202
1203 ITER_END
1204}
1205
1206void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1207 const SkPath& path, const SkMatrix* matrix,
1208 const SkPaint& paint) {
1209 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1210
1211 while (iter.next()) {
1212 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1213 matrix, paint);
1214 }
1215
1216 ITER_END
1217}
1218
1219void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1220 const SkPoint verts[], const SkPoint texs[],
1221 const SkColor colors[], SkXfermode* xmode,
1222 const uint16_t indices[], int indexCount,
1223 const SkPaint& paint) {
1224 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1225
1226 while (iter.next()) {
1227 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1228 colors, xmode, indices, indexCount, paint);
1229 }
1230
1231 ITER_END
1232}
1233
1234//////////////////////////////////////////////////////////////////////////////
1235// These methods are NOT virtual, and therefore must call back into virtual
1236// methods, rather than actually drawing themselves.
1237//////////////////////////////////////////////////////////////////////////////
1238
1239void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001240 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 SkPaint paint;
1242
1243 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001244 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001245 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246 }
1247 this->drawPaint(paint);
1248}
1249
reed@android.com845fdac2009-06-23 03:01:32 +00001250void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 SkPaint paint;
1252
1253 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001254 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001255 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 }
1257 this->drawPaint(paint);
1258}
1259
1260void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1261 SkPoint pt;
1262
1263 pt.set(x, y);
1264 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1265}
1266
1267void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1268 SkPoint pt;
1269 SkPaint paint;
1270
1271 pt.set(x, y);
1272 paint.setColor(color);
1273 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1274}
1275
1276void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1277 const SkPaint& paint) {
1278 SkPoint pts[2];
1279
1280 pts[0].set(x0, y0);
1281 pts[1].set(x1, y1);
1282 this->drawPoints(kLines_PointMode, 2, pts, paint);
1283}
1284
1285void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1286 SkScalar right, SkScalar bottom,
1287 const SkPaint& paint) {
1288 SkRect r;
1289
1290 r.set(left, top, right, bottom);
1291 this->drawRect(r, paint);
1292}
1293
1294void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1295 const SkPaint& paint) {
1296 if (radius < 0) {
1297 radius = 0;
1298 }
1299
1300 SkRect r;
1301 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
1302
1303 if (paint.canComputeFastBounds()) {
1304 SkRect storage;
1305 if (this->quickReject(paint.computeFastBounds(r, &storage),
1306 paint2EdgeType(&paint))) {
1307 return;
1308 }
1309 }
1310
1311 SkPath path;
1312 path.addOval(r);
1313 this->drawPath(path, paint);
1314}
1315
1316void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1317 const SkPaint& paint) {
1318 if (rx > 0 && ry > 0) {
1319 if (paint.canComputeFastBounds()) {
1320 SkRect storage;
1321 if (this->quickReject(paint.computeFastBounds(r, &storage),
1322 paint2EdgeType(&paint))) {
1323 return;
1324 }
1325 }
1326
1327 SkPath path;
1328 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1329 this->drawPath(path, paint);
1330 } else {
1331 this->drawRect(r, paint);
1332 }
1333}
1334
1335void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1336 if (paint.canComputeFastBounds()) {
1337 SkRect storage;
1338 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1339 paint2EdgeType(&paint))) {
1340 return;
1341 }
1342 }
1343
1344 SkPath path;
1345 path.addOval(oval);
1346 this->drawPath(path, paint);
1347}
1348
1349void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1350 SkScalar sweepAngle, bool useCenter,
1351 const SkPaint& paint) {
1352 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1353 this->drawOval(oval, paint);
1354 } else {
1355 SkPath path;
1356 if (useCenter) {
1357 path.moveTo(oval.centerX(), oval.centerY());
1358 }
1359 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1360 if (useCenter) {
1361 path.close();
1362 }
1363 this->drawPath(path, paint);
1364 }
1365}
1366
1367void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1368 const SkPath& path, SkScalar hOffset,
1369 SkScalar vOffset, const SkPaint& paint) {
1370 SkMatrix matrix;
1371
1372 matrix.setTranslate(hOffset, vOffset);
1373 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1374}
1375
reed@android.comf76bacf2009-05-13 14:00:33 +00001376///////////////////////////////////////////////////////////////////////////////
1377
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378void SkCanvas::drawPicture(SkPicture& picture) {
1379 int saveCount = save();
1380 picture.draw(this);
1381 restoreToCount(saveCount);
1382}
1383
reed@android.comf76bacf2009-05-13 14:00:33 +00001384void SkCanvas::drawShape(SkShape* shape) {
1385 // shape baseclass takes care of save/restore
1386 shape->draw(this);
1387}
1388
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389///////////////////////////////////////////////////////////////////////////////
1390///////////////////////////////////////////////////////////////////////////////
1391
1392SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
1393 // need COMPILE_TIME_ASSERT
1394 SkASSERT(sizeof(fStorage) >= sizeof(SkDrawIter));
1395
1396 SkASSERT(canvas);
1397
1398 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1399 fDone = !fImpl->next();
1400}
1401
1402SkCanvas::LayerIter::~LayerIter() {
1403 fImpl->~SkDrawIter();
1404}
1405
1406void SkCanvas::LayerIter::next() {
1407 fDone = !fImpl->next();
1408}
1409
1410SkDevice* SkCanvas::LayerIter::device() const {
1411 return fImpl->getDevice();
1412}
1413
1414const SkMatrix& SkCanvas::LayerIter::matrix() const {
1415 return fImpl->getMatrix();
1416}
1417
1418const SkPaint& SkCanvas::LayerIter::paint() const {
1419 const SkPaint* paint = fImpl->getPaint();
1420 if (NULL == paint) {
1421 paint = &fDefaultPaint;
1422 }
1423 return *paint;
1424}
1425
1426const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1427int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1428int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1429