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