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