blob: 0e36c91a4b8036356b0ca069939eff0900541902 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
2 * Copyright (C) 2006-2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkCanvas.h"
18#include "SkBounder.h"
19#include "SkDevice.h"
20#include "SkDraw.h"
21#include "SkDrawFilter.h"
22#include "SkDrawLooper.h"
23#include "SkPicture.h"
24#include "SkScalarCompare.h"
reed@android.comf76bacf2009-05-13 14:00:33 +000025#include "SkShape.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkTemplates.h"
27#include "SkUtils.h"
28#include <new>
29
30//#define SK_TRACE_SAVERESTORE
31
32#ifdef SK_TRACE_SAVERESTORE
33 static int gLayerCounter;
34 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
35 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
36
37 static int gRecCounter;
38 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
39 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
40
41 static int gCanvasCounter;
42 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
43 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
44#else
45 #define inc_layer()
46 #define dec_layer()
47 #define inc_rec()
48 #define dec_rec()
49 #define inc_canvas()
50 #define dec_canvas()
51#endif
52
53///////////////////////////////////////////////////////////////////////////////
54// Helpers for computing fast bounds for quickReject tests
55
56static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
57 return paint != NULL && paint->isAntiAlias() ?
58 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
59}
60
61///////////////////////////////////////////////////////////////////////////////
62
63/* This is the record we keep for each SkDevice that the user installs.
64 The clip/matrix/proc are fields that reflect the top of the save/restore
65 stack. Whenever the canvas changes, it marks a dirty flag, and then before
66 these are used (assuming we're not on a layer) we rebuild these cache
67 values: they reflect the top of the save stack, but translated and clipped
68 by the device's XY offset and bitmap-bounds.
69*/
70struct DeviceCM {
71 DeviceCM* fNext;
72 SkDevice* fDevice;
73 SkRegion fClip;
74 const SkMatrix* fMatrix;
75 SkPaint* fPaint; // may be null (in the future)
76 int16_t fX, fY; // relative to base matrix/clip
reed@android.comf2b98d62010-12-20 18:26:13 +000077 // optional, related to canvas' external matrix
78 const SkMatrix* fMVMatrix;
79 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
81 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
82 : fNext(NULL) {
83 if (NULL != device) {
84 device->ref();
85 device->lockPixels();
86 }
87 fDevice = device;
88 fX = SkToS16(x);
89 fY = SkToS16(y);
90 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
91 }
92
93 ~DeviceCM() {
94 if (NULL != fDevice) {
95 fDevice->unlockPixels();
96 fDevice->unref();
97 }
98 SkDELETE(fPaint);
99 }
100
101 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
102 SkRegion* updateClip) {
103 int x = fX;
104 int y = fY;
105 int width = fDevice->width();
106 int height = fDevice->height();
107
108 if ((x | y) == 0) {
109 fMatrix = &totalMatrix;
110 fClip = totalClip;
111 } else {
112 fMatrixStorage = totalMatrix;
113 fMatrixStorage.postTranslate(SkIntToScalar(-x),
114 SkIntToScalar(-y));
115 fMatrix = &fMatrixStorage;
116
117 totalClip.translate(-x, -y, &fClip);
118 }
119
120 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
121
122 // intersect clip, but don't translate it (yet)
123
124 if (updateClip) {
125 updateClip->op(x, y, x + width, y + height,
126 SkRegion::kDifference_Op);
127 }
128
129 fDevice->setMatrixClip(*fMatrix, fClip);
130
131#ifdef SK_DEBUG
132 if (!fClip.isEmpty()) {
133 SkIRect deviceR;
134 deviceR.set(0, 0, width, height);
135 SkASSERT(deviceR.contains(fClip.getBounds()));
136 }
137#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000138 // default is to assume no external matrix
139 fMVMatrix = NULL;
140 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000142
143 // can only be called after calling updateMC()
144 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
145 fMVMatrixStorage.setConcat(extI, *fMatrix);
146 fMVMatrix = &fMVMatrixStorage;
147 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
148 }
149
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 void translateClip() {
151 if (fX | fY) {
152 fClip.translate(fX, fY);
153 }
154 }
155
156private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000157 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158};
159
160/* This is the record we keep for each save/restore level in the stack.
161 Since a level optionally copies the matrix and/or stack, we have pointers
162 for these fields. If the value is copied for this level, the copy is
163 stored in the ...Storage field, and the pointer points to that. If the
164 value is not copied for this level, we ignore ...Storage, and just point
165 at the corresponding value in the previous level in the stack.
166*/
167class SkCanvas::MCRec {
168public:
169 MCRec* fNext;
170 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
171 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
172 SkDrawFilter* fFilter; // the current filter (or null)
173
174 DeviceCM* fLayer;
175 /* If there are any layers in the stack, this points to the top-most
176 one that is at or below this level in the stack (so we know what
177 bitmap/device to draw into from this level. This value is NOT
178 reference counted, since the real owner is either our fLayer field,
179 or a previous one in a lower level.)
180 */
181 DeviceCM* fTopLayer;
182
183 MCRec(const MCRec* prev, int flags) {
184 if (NULL != prev) {
185 if (flags & SkCanvas::kMatrix_SaveFlag) {
186 fMatrixStorage = *prev->fMatrix;
187 fMatrix = &fMatrixStorage;
188 } else {
189 fMatrix = prev->fMatrix;
190 }
191
192 if (flags & SkCanvas::kClip_SaveFlag) {
193 fRegionStorage = *prev->fRegion;
194 fRegion = &fRegionStorage;
195 } else {
196 fRegion = prev->fRegion;
197 }
198
199 fFilter = prev->fFilter;
200 fFilter->safeRef();
201
202 fTopLayer = prev->fTopLayer;
203 } else { // no prev
204 fMatrixStorage.reset();
205
206 fMatrix = &fMatrixStorage;
207 fRegion = &fRegionStorage;
208 fFilter = NULL;
209 fTopLayer = NULL;
210 }
211 fLayer = NULL;
212
213 // don't bother initializing fNext
214 inc_rec();
215 }
216 ~MCRec() {
217 fFilter->safeUnref();
218 SkDELETE(fLayer);
219 dec_rec();
220 }
221
222private:
223 SkMatrix fMatrixStorage;
224 SkRegion fRegionStorage;
225};
226
227class SkDrawIter : public SkDraw {
228public:
229 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
230 fCanvas = canvas;
231 canvas->updateDeviceCMCache();
232
233 fBounder = canvas->getBounder();
234 fCurrLayer = canvas->fMCRec->fTopLayer;
235 fSkipEmptyClips = skipEmptyClips;
236 }
237
238 bool next() {
239 // skip over recs with empty clips
240 if (fSkipEmptyClips) {
241 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
242 fCurrLayer = fCurrLayer->fNext;
243 }
244 }
245
246 if (NULL != fCurrLayer) {
247 const DeviceCM* rec = fCurrLayer;
248
249 fMatrix = rec->fMatrix;
250 fClip = &rec->fClip;
251 fDevice = rec->fDevice;
252 fBitmap = &fDevice->accessBitmap(true);
253 fLayerX = rec->fX;
254 fLayerY = rec->fY;
255 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000256 fMVMatrix = rec->fMVMatrix;
257 fExtMatrix = rec->fExtMatrix;
258 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259
260 fCurrLayer = rec->fNext;
261 if (fBounder) {
262 fBounder->setClip(fClip);
263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000265
reed@android.comf2b98d62010-12-20 18:26:13 +0000266 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 return true;
268 }
269 return false;
270 }
271
272 int getX() const { return fLayerX; }
273 int getY() const { return fLayerY; }
274 SkDevice* getDevice() const { return fDevice; }
275 const SkMatrix& getMatrix() const { return *fMatrix; }
276 const SkRegion& getClip() const { return *fClip; }
277 const SkPaint* getPaint() const { return fPaint; }
278private:
279 SkCanvas* fCanvas;
280 const DeviceCM* fCurrLayer;
281 const SkPaint* fPaint; // May be null.
282 int fLayerX;
283 int fLayerY;
284 SkBool8 fSkipEmptyClips;
285
286 typedef SkDraw INHERITED;
287};
288
289/////////////////////////////////////////////////////////////////////////////
290
291class AutoDrawLooper {
292public:
293 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, SkDrawFilter::Type t)
294 : fCanvas(canvas), fPaint((SkPaint*)&paint), fType(t) {
295 if ((fLooper = paint.getLooper()) != NULL) {
296 fLooper->init(canvas, (SkPaint*)&paint);
297 } else {
298 fOnce = true;
299 }
300 fFilter = canvas->getDrawFilter();
301 fNeedFilterRestore = false;
302 }
303
304 ~AutoDrawLooper() {
305 if (fNeedFilterRestore) {
306 SkASSERT(fFilter);
307 fFilter->restore(fCanvas, fPaint, fType);
308 }
309 if (NULL != fLooper) {
310 fLooper->restore();
311 }
312 }
313
314 bool next() {
315 SkDrawFilter* filter = fFilter;
316
317 // if we drew earlier with a filter, then we need to restore first
318 if (fNeedFilterRestore) {
319 SkASSERT(filter);
320 filter->restore(fCanvas, fPaint, fType);
321 fNeedFilterRestore = false;
322 }
323
324 bool result;
325
326 if (NULL != fLooper) {
327 result = fLooper->next();
328 } else {
329 result = fOnce;
330 fOnce = false;
331 }
332
333 // if we're gonna draw, give the filter a chance to do its work
334 if (result && NULL != filter) {
335 fNeedFilterRestore = result = filter->filter(fCanvas, fPaint,
336 fType);
337 }
338 return result;
339 }
340
341private:
342 SkDrawLooper* fLooper;
343 SkDrawFilter* fFilter;
344 SkCanvas* fCanvas;
345 SkPaint* fPaint;
346 SkDrawFilter::Type fType;
347 bool fOnce;
348 bool fNeedFilterRestore;
349
350};
351
352/* Stack helper for managing a SkBounder. In the destructor, if we were
353 given a bounder, we call its commit() method, signifying that we are
354 done accumulating bounds for that draw.
355*/
356class SkAutoBounderCommit {
357public:
358 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
359 ~SkAutoBounderCommit() {
360 if (NULL != fBounder) {
361 fBounder->commit();
362 }
363 }
364private:
365 SkBounder* fBounder;
366};
367
368#include "SkColorPriv.h"
369
370class AutoValidator {
371public:
372 AutoValidator(SkDevice* device) : fDevice(device) {}
373 ~AutoValidator() {
374#ifdef SK_DEBUG
375 const SkBitmap& bm = fDevice->accessBitmap(false);
376 if (bm.config() == SkBitmap::kARGB_4444_Config) {
377 for (int y = 0; y < bm.height(); y++) {
378 const SkPMColor16* p = bm.getAddr16(0, y);
379 for (int x = 0; x < bm.width(); x++) {
380 SkPMColor16 c = p[x];
381 SkPMColor16Assert(c);
382 }
383 }
384 }
385#endif
386 }
387private:
388 SkDevice* fDevice;
389};
390
391////////// macros to place around the internal draw calls //////////////////
392
393#define ITER_BEGIN(paint, type) \
394/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
395 AutoDrawLooper looper(this, paint, type); \
396 while (looper.next()) { \
397 SkAutoBounderCommit ac(fBounder); \
398 SkDrawIter iter(this);
399
400#define ITER_END }
401
402////////////////////////////////////////////////////////////////////////////
403
404SkDevice* SkCanvas::init(SkDevice* device) {
405 fBounder = NULL;
406 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000407 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000408 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000409 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410
411 fMCRec = (MCRec*)fMCStack.push_back();
412 new (fMCRec) MCRec(NULL, 0);
413
414 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
415 fMCRec->fTopLayer = fMCRec->fLayer;
416 fMCRec->fNext = NULL;
417
reed@android.comf2b98d62010-12-20 18:26:13 +0000418 fUseExternalMatrix = false;
419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 return this->setDevice(device);
421}
422
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000423SkCanvas::SkCanvas(SkDeviceFactory* factory)
424 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
425 fDeviceFactory(factory) {
426 inc_canvas();
427
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000428 if (!factory)
429 fDeviceFactory = SkNEW(SkRasterDeviceFactory);
430
431 this->init(NULL);
432}
433
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434SkCanvas::SkCanvas(SkDevice* device)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000435 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
436 fDeviceFactory(device->getDeviceFactory()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 inc_canvas();
438
439 this->init(device);
440}
441
442SkCanvas::SkCanvas(const SkBitmap& bitmap)
443 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
444 inc_canvas();
445
reed@android.comf2b98d62010-12-20 18:26:13 +0000446 SkDevice* device = SkNEW_ARGS(SkDevice, (this, bitmap, false));
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000447 fDeviceFactory = device->getDeviceFactory();
448 this->init(device)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449}
450
451SkCanvas::~SkCanvas() {
452 // free up the contents of our deque
453 this->restoreToCount(1); // restore everything but the last
454 this->internalRestore(); // restore the last, since we're going away
455
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000456 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000457 SkDELETE(fDeviceFactory);
458
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459 dec_canvas();
460}
461
462SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
463 SkRefCnt_SafeAssign(fBounder, bounder);
464 return bounder;
465}
466
467SkDrawFilter* SkCanvas::getDrawFilter() const {
468 return fMCRec->fFilter;
469}
470
471SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
472 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
473 return filter;
474}
475
476///////////////////////////////////////////////////////////////////////////////
477
478SkDevice* SkCanvas::getDevice() const {
479 // return root device
480 SkDeque::Iter iter(fMCStack);
481 MCRec* rec = (MCRec*)iter.next();
482 SkASSERT(rec && rec->fLayer);
483 return rec->fLayer->fDevice;
484}
485
486SkDevice* SkCanvas::setDevice(SkDevice* device) {
487 // return root device
488 SkDeque::Iter iter(fMCStack);
489 MCRec* rec = (MCRec*)iter.next();
490 SkASSERT(rec && rec->fLayer);
491 SkDevice* rootDevice = rec->fLayer->fDevice;
492
493 if (rootDevice == device) {
494 return device;
495 }
496
497 /* Notify the devices that they are going in/out of scope, so they can do
498 things like lock/unlock their pixels, etc.
499 */
500 if (device) {
501 device->lockPixels();
502 }
503 if (rootDevice) {
504 rootDevice->unlockPixels();
505 }
506
507 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
508 rootDevice = device;
509
510 fDeviceCMDirty = true;
511
512 /* Now we update our initial region to have the bounds of the new device,
513 and then intersect all of the clips in our stack with these bounds,
514 to ensure that we can't draw outside of the device's bounds (and trash
515 memory).
516
517 NOTE: this is only a partial-fix, since if the new device is larger than
518 the previous one, we don't know how to "enlarge" the clips in our stack,
519 so drawing may be artificially restricted. Without keeping a history of
520 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
521 reconstruct the correct clips, so this approximation will have to do.
522 The caller really needs to restore() back to the base if they want to
523 accurately take advantage of the new device bounds.
524 */
525
526 if (NULL == device) {
527 rec->fRegion->setEmpty();
528 while ((rec = (MCRec*)iter.next()) != NULL) {
529 (void)rec->fRegion->setEmpty();
530 }
531 } else {
532 // compute our total bounds for all devices
533 SkIRect bounds;
534
535 bounds.set(0, 0, device->width(), device->height());
536
537 // now jam our 1st clip to be bounds, and intersect the rest with that
538 rec->fRegion->setRect(bounds);
539 while ((rec = (MCRec*)iter.next()) != NULL) {
540 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
541 }
542 }
543 return device;
544}
545
reed@android.comf2b98d62010-12-20 18:26:13 +0000546SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap, bool forLayer) {
547 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (this, bitmap, forLayer)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 device->unref();
549 return device;
550}
551
552//////////////////////////////////////////////////////////////////////////////
553
554bool SkCanvas::getViewport(SkIPoint* size) const {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000555 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000556 return false;
557 if (size)
558 size->set(getDevice()->width(), getDevice()->height());
559 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560}
561
562bool SkCanvas::setViewport(int width, int height) {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000563 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000564 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000565
566 this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height,
567 false, false))->unref();
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000568 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000569}
570
571void SkCanvas::updateDeviceCMCache() {
572 if (fDeviceCMDirty) {
573 const SkMatrix& totalMatrix = this->getTotalMatrix();
574 const SkRegion& totalClip = this->getTotalClip();
575 DeviceCM* layer = fMCRec->fTopLayer;
576
577 if (NULL == layer->fNext) { // only one layer
578 layer->updateMC(totalMatrix, totalClip, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000579 if (fUseExternalMatrix) {
580 layer->updateExternalMatrix(fExternalMatrix,
581 fExternalInverse);
582 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 } else {
584 SkRegion clip;
585 clip = totalClip; // make a copy
586 do {
587 layer->updateMC(totalMatrix, clip, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000588 if (fUseExternalMatrix) {
589 layer->updateExternalMatrix(fExternalMatrix,
590 fExternalInverse);
591 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 } while ((layer = layer->fNext) != NULL);
593 }
594 fDeviceCMDirty = false;
595 }
596}
597
reed@android.comf2b98d62010-12-20 18:26:13 +0000598void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
599 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000601 if (fLastDeviceToGainFocus != device) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000602 device->gainFocus(this, matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000603 fLastDeviceToGainFocus = device;
604 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605}
606
607///////////////////////////////////////////////////////////////////////////////
608
609int SkCanvas::internalSave(SaveFlags flags) {
610 int saveCount = this->getSaveCount(); // record this before the actual save
611
612 MCRec* newTop = (MCRec*)fMCStack.push_back();
613 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
614
615 newTop->fNext = fMCRec;
616 fMCRec = newTop;
617
618 return saveCount;
619}
620
621int SkCanvas::save(SaveFlags flags) {
622 // call shared impl
623 return this->internalSave(flags);
624}
625
626#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
627#define C16MASK (1 << SkBitmap::kRGB_565_Config)
628#define C8MASK (1 << SkBitmap::kA8_Config)
629
630static SkBitmap::Config resolve_config(SkCanvas* canvas,
631 const SkIRect& bounds,
632 SkCanvas::SaveFlags flags,
633 bool* isOpaque) {
634 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
635
636#if 0
637 // loop through and union all the configs we may draw into
638 uint32_t configMask = 0;
639 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
640 {
641 SkDevice* device = canvas->getLayerDevice(i);
642 if (device->intersects(bounds))
643 configMask |= 1 << device->config();
644 }
645
646 // if the caller wants alpha or fullcolor, we can't return 565
647 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
648 SkCanvas::kHasAlphaLayer_SaveFlag))
649 configMask &= ~C16MASK;
650
651 switch (configMask) {
652 case C8MASK: // if we only have A8, return that
653 return SkBitmap::kA8_Config;
654
655 case C16MASK: // if we only have 565, return that
656 return SkBitmap::kRGB_565_Config;
657
658 default:
659 return SkBitmap::kARGB_8888_Config; // default answer
660 }
661#else
662 return SkBitmap::kARGB_8888_Config; // default answer
663#endif
664}
665
666static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
667 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
668}
669
670int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
671 SaveFlags flags) {
672 // do this before we create the layer. We don't call the public save() since
673 // that would invoke a possibly overridden virtual
674 int count = this->internalSave(flags);
675
676 fDeviceCMDirty = true;
677
678 SkIRect ir;
679 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000680 if (clipBounds.isEmpty()) {
681 return count;
682 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683
684 if (NULL != bounds) {
685 SkRect r;
686
687 this->getTotalMatrix().mapRect(&r, *bounds);
688 r.roundOut(&ir);
689 // early exit if the layer's bounds are clipped out
690 if (!ir.intersect(clipBounds)) {
691 if (bounds_affects_clip(flags))
692 fMCRec->fRegion->setEmpty();
693 return count;
694 }
695 } else { // no user bounds, so just use the clip
696 ir = clipBounds;
697 }
698
699 // early exit if the clip is now empty
700 if (bounds_affects_clip(flags) &&
701 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
702 return count;
703 }
704
705 bool isOpaque;
706 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
707
708 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
709 isOpaque, true);
710 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
711 device->unref();
712
713 layer->fNext = fMCRec->fTopLayer;
714 fMCRec->fLayer = layer;
715 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
716
717 return count;
718}
719
720int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
721 SaveFlags flags) {
722 if (0xFF == alpha) {
723 return this->saveLayer(bounds, NULL, flags);
724 } else {
725 SkPaint tmpPaint;
726 tmpPaint.setAlpha(alpha);
727 return this->saveLayer(bounds, &tmpPaint, flags);
728 }
729}
730
731void SkCanvas::restore() {
732 // check for underflow
733 if (fMCStack.count() > 1) {
734 this->internalRestore();
735 }
736}
737
738void SkCanvas::internalRestore() {
739 SkASSERT(fMCStack.count() != 0);
740
741 fDeviceCMDirty = true;
742 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000743 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744
745 // reserve our layer (if any)
746 DeviceCM* layer = fMCRec->fLayer; // may be null
747 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
748 fMCRec->fLayer = NULL;
749
750 // now do the normal restore()
751 fMCRec->~MCRec(); // balanced in save()
752 fMCStack.pop_back();
753 fMCRec = (MCRec*)fMCStack.back();
754
755 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
756 since if we're being recorded, we don't want to record this (the
757 recorder will have already recorded the restore).
758 */
759 if (NULL != layer) {
760 if (layer->fNext) {
761 this->drawDevice(layer->fDevice, layer->fX, layer->fY,
762 layer->fPaint);
763 // reset this, since drawDevice will have set it to true
764 fDeviceCMDirty = true;
765 }
766 SkDELETE(layer);
767 }
768}
769
770int SkCanvas::getSaveCount() const {
771 return fMCStack.count();
772}
773
774void SkCanvas::restoreToCount(int count) {
775 // sanity check
776 if (count < 1) {
777 count = 1;
778 }
779 while (fMCStack.count() > count) {
780 this->restore();
781 }
782}
783
784/////////////////////////////////////////////////////////////////////////////
785
786// can't draw it if its empty, or its too big for a fixed-point width or height
787static bool reject_bitmap(const SkBitmap& bitmap) {
788 return bitmap.width() <= 0 || bitmap.height() <= 0 ||
789 bitmap.width() > 32767 || bitmap.height() > 32767;
790}
791
reed@android.comf2b98d62010-12-20 18:26:13 +0000792void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 const SkMatrix& matrix, const SkPaint* paint) {
794 if (reject_bitmap(bitmap)) {
795 return;
796 }
797
798 if (NULL == paint) {
799 SkPaint tmpPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000800 this->commonDrawBitmap(bitmap, srcRect, matrix, tmpPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 } else {
reed@android.comf2b98d62010-12-20 18:26:13 +0000802 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 }
804}
805
806void SkCanvas::drawDevice(SkDevice* device, int x, int y,
807 const SkPaint* paint) {
808 SkPaint tmp;
809 if (NULL == paint) {
810 tmp.setDither(true);
811 paint = &tmp;
812 }
813
814 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
815 while (iter.next()) {
816 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
817 *paint);
818 }
819 ITER_END
820}
821
822/////////////////////////////////////////////////////////////////////////////
823
824bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
825 fDeviceCMDirty = true;
826 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000827 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 return fMCRec->fMatrix->preTranslate(dx, dy);
829}
830
831bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
832 fDeviceCMDirty = true;
833 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000834 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 return fMCRec->fMatrix->preScale(sx, sy);
836}
837
838bool SkCanvas::rotate(SkScalar degrees) {
839 fDeviceCMDirty = true;
840 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000841 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 return fMCRec->fMatrix->preRotate(degrees);
843}
844
845bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
846 fDeviceCMDirty = true;
847 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000848 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 return fMCRec->fMatrix->preSkew(sx, sy);
850}
851
852bool SkCanvas::concat(const SkMatrix& matrix) {
853 fDeviceCMDirty = true;
854 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000855 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 return fMCRec->fMatrix->preConcat(matrix);
857}
858
859void SkCanvas::setMatrix(const SkMatrix& matrix) {
860 fDeviceCMDirty = true;
861 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000862 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 *fMCRec->fMatrix = matrix;
864}
865
866// this is not virtual, so it must call a virtual method so that subclasses
867// will see its action
868void SkCanvas::resetMatrix() {
869 SkMatrix matrix;
870
871 matrix.reset();
872 this->setMatrix(matrix);
873}
874
875//////////////////////////////////////////////////////////////////////////////
876
877bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
878 fDeviceCMDirty = true;
879 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000880 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881
882 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000883 // for these simpler matrices, we can stay a rect ever after applying
884 // the matrix. This means we don't have to a) make a path, and b) tell
885 // the region code to scan-convert the path, only to discover that it
886 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 SkRect r;
888 SkIRect ir;
889
890 fMCRec->fMatrix->mapRect(&r, rect);
891 r.round(&ir);
892 return fMCRec->fRegion->op(ir, op);
893 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000894 // since we're rotate or some such thing, we convert the rect to a path
895 // and clip against that, since it can handle any matrix. However, to
896 // avoid recursion in the case where we are subclassed (e.g. Pictures)
897 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 SkPath path;
899
900 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000901 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 }
903}
904
905bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
906 fDeviceCMDirty = true;
907 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000908 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909
910 SkPath devPath;
911 path.transform(*fMCRec->fMatrix, &devPath);
912
913 if (SkRegion::kIntersect_Op == op) {
914 return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
915 } else {
916 SkRegion base;
917 const SkBitmap& bm = this->getDevice()->accessBitmap(false);
918 base.setRect(0, 0, bm.width(), bm.height());
919
920 if (SkRegion::kReplace_Op == op) {
921 return fMCRec->fRegion->setPath(devPath, base);
922 } else {
923 SkRegion rgn;
924 rgn.setPath(devPath, base);
925 return fMCRec->fRegion->op(rgn, op);
926 }
927 }
928}
929
930bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
931 fDeviceCMDirty = true;
932 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000933 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934
935 return fMCRec->fRegion->op(rgn, op);
936}
937
reed@android.comba09de42010-02-05 20:46:05 +0000938void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +0000940 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
941 fLocalBoundsCompareTypeBW;
942
943 if (!this->getClipBounds(&r, et)) {
944 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945 } else {
reed@android.comba09de42010-02-05 20:46:05 +0000946 rCompare.set(SkScalarToCompareType(r.fLeft),
947 SkScalarToCompareType(r.fTop),
948 SkScalarToCompareType(r.fRight),
949 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 }
951}
952
reed@android.comd252db02009-04-01 18:31:44 +0000953/* current impl ignores edgetype, and relies on
954 getLocalClipBoundsCompareType(), which always returns a value assuming
955 antialiasing (worst case)
956 */
reed@android.comba09de42010-02-05 20:46:05 +0000957bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 if (fMCRec->fRegion->isEmpty()) {
959 return true;
960 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961
reed@android.coma380ae42009-07-21 01:17:02 +0000962 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
963 SkRect dst;
964 fMCRec->fMatrix->mapRect(&dst, rect);
965 SkIRect idst;
966 dst.roundOut(&idst);
967 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
968 } else {
reed@android.comba09de42010-02-05 20:46:05 +0000969 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +0000970
reed@android.coma380ae42009-07-21 01:17:02 +0000971 // for speed, do the most likely reject compares first
972 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
973 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
974 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
975 return true;
976 }
977 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
978 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
979 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
980 return true;
981 }
982 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984}
985
986bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +0000987 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988}
989
990bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
991 /* current impl ignores edgetype, and relies on
992 getLocalClipBoundsCompareType(), which always returns a value assuming
993 antialiasing (worst case)
994 */
995
996 if (fMCRec->fRegion->isEmpty()) {
997 return true;
998 }
999
reed@android.comaefd2bc2009-03-30 21:02:14 +00001000 SkScalarCompareType userT = SkScalarToCompareType(top);
1001 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002
1003 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001004 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005 if (userT >= userB) {
1006 return true;
1007 }
1008
1009 // check if we are above or below the local clip bounds
1010 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1011 return userT >= clipR.fBottom || userB <= clipR.fTop;
1012}
1013
1014bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1015 const SkRegion& clip = *fMCRec->fRegion;
1016 if (clip.isEmpty()) {
1017 if (bounds) {
1018 bounds->setEmpty();
1019 }
1020 return false;
1021 }
1022
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001023 SkMatrix inverse;
1024 // if we can't invert the CTM, we can't return local clip bounds
1025 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001026 if (bounds) {
1027 bounds->setEmpty();
1028 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001029 return false;
1030 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001032 if (NULL != bounds) {
1033 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 // get the clip's bounds
1035 const SkIRect& ibounds = clip.getBounds();
1036 // adjust it outwards if we are antialiasing
1037 int inset = (kAA_EdgeType == et);
1038 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1039 ibounds.fRight + inset, ibounds.fBottom + inset);
1040
1041 // invert into local coordinates
1042 inverse.mapRect(bounds, r);
1043 }
1044 return true;
1045}
1046
1047const SkMatrix& SkCanvas::getTotalMatrix() const {
1048 return *fMCRec->fMatrix;
1049}
1050
1051const SkRegion& SkCanvas::getTotalClip() const {
1052 return *fMCRec->fRegion;
1053}
1054
reed@android.comf2b98d62010-12-20 18:26:13 +00001055void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1056 if (NULL == matrix || matrix->isIdentity()) {
1057 if (fUseExternalMatrix) {
1058 fDeviceCMDirty = true;
1059 }
1060 fUseExternalMatrix = false;
1061 } else {
1062 fUseExternalMatrix = true;
1063 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
1064
1065 fExternalMatrix = *matrix;
1066 matrix->invert(&fExternalInverse);
1067 }
1068
1069 static bool gUseExt;
1070 if (gUseExt != fUseExternalMatrix && false) {
1071 gUseExt = fUseExternalMatrix;
1072 printf("---- fUseExternalMatrix = %d\n", fUseExternalMatrix);
1073 }
1074}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075
reed@android.comf2b98d62010-12-20 18:26:13 +00001076SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1077 bool isOpaque, bool forLayer) {
1078 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079}
1080
1081//////////////////////////////////////////////////////////////////////////////
1082// These are the virtual drawing methods
1083//////////////////////////////////////////////////////////////////////////////
1084
1085void SkCanvas::drawPaint(const SkPaint& paint) {
1086 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
1087
1088 while (iter.next()) {
1089 iter.fDevice->drawPaint(iter, paint);
1090 }
1091
1092 ITER_END
1093}
1094
1095void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1096 const SkPaint& paint) {
1097 if ((long)count <= 0) {
1098 return;
1099 }
1100
1101 SkASSERT(pts != NULL);
1102
1103 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
1104
1105 while (iter.next()) {
1106 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1107 }
1108
1109 ITER_END
1110}
1111
1112void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1113 if (paint.canComputeFastBounds()) {
1114 SkRect storage;
1115 if (this->quickReject(paint.computeFastBounds(r, &storage),
1116 paint2EdgeType(&paint))) {
1117 return;
1118 }
1119 }
1120
1121 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1122
1123 while (iter.next()) {
1124 iter.fDevice->drawRect(iter, r, paint);
1125 }
1126
1127 ITER_END
1128}
1129
1130void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1131 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001132 SkRect storage;
1133 const SkRect& bounds = path.getBounds();
1134 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 paint2EdgeType(&paint))) {
1136 return;
1137 }
1138 }
1139
1140 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1141
1142 while (iter.next()) {
1143 iter.fDevice->drawPath(iter, path, paint);
1144 }
1145
1146 ITER_END
1147}
1148
1149void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1150 const SkPaint* paint) {
1151 SkDEBUGCODE(bitmap.validate();)
1152
1153 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1154 SkRect fastBounds;
1155 fastBounds.set(x, y,
1156 x + SkIntToScalar(bitmap.width()),
1157 y + SkIntToScalar(bitmap.height()));
1158 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1159 return;
1160 }
1161 }
1162
1163 SkMatrix matrix;
1164 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001165 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001166}
1167
1168void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1169 const SkRect& dst, const SkPaint* paint) {
1170 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1171 return;
1172 }
1173
1174 // do this now, to avoid the cost of calling extract for RLE bitmaps
1175 if (this->quickReject(dst, paint2EdgeType(paint))) {
1176 return;
1177 }
1178
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 const SkBitmap* bitmapPtr = &bitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001182 SkRect tmpSrc;
1183 if (src) {
1184 tmpSrc.set(*src);
1185 // if the extract process clipped off the top or left of the
1186 // original, we adjust for that here to get the position right.
1187 if (tmpSrc.fLeft > 0) {
1188 tmpSrc.fRight -= tmpSrc.fLeft;
1189 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001190 }
reed@android.com87899992009-10-16 14:48:38 +00001191 if (tmpSrc.fTop > 0) {
1192 tmpSrc.fBottom -= tmpSrc.fTop;
1193 tmpSrc.fTop = 0;
1194 }
1195 } else {
1196 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1197 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198 }
reed@android.com87899992009-10-16 14:48:38 +00001199 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001200
1201 // ensure that src is "valid" before we pass it to our internal routines
1202 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1203 SkIRect tmpISrc;
1204 if (src) {
1205 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
1206 tmpISrc.intersect(*src);
1207 src = &tmpISrc;
1208 }
1209 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210}
1211
1212void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1213 const SkPaint* paint) {
1214 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001215 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216}
1217
reed@android.comf2b98d62010-12-20 18:26:13 +00001218void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1219 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001221
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001223
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224 while (iter.next()) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001225 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 }
reed@android.com9b039062009-02-11 15:09:58 +00001227
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 ITER_END
1229}
1230
1231void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1232 const SkPaint* paint) {
1233 SkDEBUGCODE(bitmap.validate();)
1234
1235 if (reject_bitmap(bitmap)) {
1236 return;
1237 }
1238
1239 SkPaint tmp;
1240 if (NULL == paint) {
1241 paint = &tmp;
1242 }
1243
1244 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
1245
1246 while (iter.next()) {
1247 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1248 *paint);
1249 }
1250 ITER_END
1251}
1252
1253void SkCanvas::drawText(const void* text, size_t byteLength,
1254 SkScalar x, SkScalar y, const SkPaint& paint) {
1255 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1256
1257 while (iter.next()) {
1258 iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
1259 }
1260
1261 ITER_END
1262}
1263
1264void SkCanvas::drawPosText(const void* text, size_t byteLength,
1265 const SkPoint pos[], const SkPaint& paint) {
1266 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1267
1268 while (iter.next()) {
1269 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
1270 paint);
1271 }
1272
1273 ITER_END
1274}
1275
1276void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1277 const SkScalar xpos[], SkScalar constY,
1278 const SkPaint& paint) {
1279 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1280
1281 while (iter.next()) {
1282 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
1283 paint);
1284 }
1285
1286 ITER_END
1287}
1288
1289void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1290 const SkPath& path, const SkMatrix* matrix,
1291 const SkPaint& paint) {
1292 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1293
1294 while (iter.next()) {
1295 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1296 matrix, paint);
1297 }
1298
1299 ITER_END
1300}
1301
1302void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1303 const SkPoint verts[], const SkPoint texs[],
1304 const SkColor colors[], SkXfermode* xmode,
1305 const uint16_t indices[], int indexCount,
1306 const SkPaint& paint) {
1307 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1308
1309 while (iter.next()) {
1310 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1311 colors, xmode, indices, indexCount, paint);
1312 }
1313
1314 ITER_END
1315}
1316
reed@android.comcb608442009-12-04 21:32:27 +00001317void SkCanvas::drawData(const void* data, size_t length) {
1318 // do nothing. Subclasses may do something with the data
1319}
1320
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321//////////////////////////////////////////////////////////////////////////////
1322// These methods are NOT virtual, and therefore must call back into virtual
1323// methods, rather than actually drawing themselves.
1324//////////////////////////////////////////////////////////////////////////////
1325
1326void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001327 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 SkPaint paint;
1329
1330 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001331 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001332 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 }
1334 this->drawPaint(paint);
1335}
1336
reed@android.com845fdac2009-06-23 03:01:32 +00001337void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 SkPaint paint;
1339
1340 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001341 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001342 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 }
1344 this->drawPaint(paint);
1345}
1346
1347void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1348 SkPoint pt;
1349
1350 pt.set(x, y);
1351 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1352}
1353
1354void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1355 SkPoint pt;
1356 SkPaint paint;
1357
1358 pt.set(x, y);
1359 paint.setColor(color);
1360 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1361}
1362
1363void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1364 const SkPaint& paint) {
1365 SkPoint pts[2];
1366
1367 pts[0].set(x0, y0);
1368 pts[1].set(x1, y1);
1369 this->drawPoints(kLines_PointMode, 2, pts, paint);
1370}
1371
1372void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1373 SkScalar right, SkScalar bottom,
1374 const SkPaint& paint) {
1375 SkRect r;
1376
1377 r.set(left, top, right, bottom);
1378 this->drawRect(r, paint);
1379}
1380
1381void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1382 const SkPaint& paint) {
1383 if (radius < 0) {
1384 radius = 0;
1385 }
1386
1387 SkRect r;
1388 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
1389
1390 if (paint.canComputeFastBounds()) {
1391 SkRect storage;
1392 if (this->quickReject(paint.computeFastBounds(r, &storage),
1393 paint2EdgeType(&paint))) {
1394 return;
1395 }
1396 }
1397
1398 SkPath path;
1399 path.addOval(r);
1400 this->drawPath(path, paint);
1401}
1402
1403void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1404 const SkPaint& paint) {
1405 if (rx > 0 && ry > 0) {
1406 if (paint.canComputeFastBounds()) {
1407 SkRect storage;
1408 if (this->quickReject(paint.computeFastBounds(r, &storage),
1409 paint2EdgeType(&paint))) {
1410 return;
1411 }
1412 }
1413
1414 SkPath path;
1415 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1416 this->drawPath(path, paint);
1417 } else {
1418 this->drawRect(r, paint);
1419 }
1420}
1421
1422void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1423 if (paint.canComputeFastBounds()) {
1424 SkRect storage;
1425 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1426 paint2EdgeType(&paint))) {
1427 return;
1428 }
1429 }
1430
1431 SkPath path;
1432 path.addOval(oval);
1433 this->drawPath(path, paint);
1434}
1435
1436void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1437 SkScalar sweepAngle, bool useCenter,
1438 const SkPaint& paint) {
1439 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1440 this->drawOval(oval, paint);
1441 } else {
1442 SkPath path;
1443 if (useCenter) {
1444 path.moveTo(oval.centerX(), oval.centerY());
1445 }
1446 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1447 if (useCenter) {
1448 path.close();
1449 }
1450 this->drawPath(path, paint);
1451 }
1452}
1453
1454void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1455 const SkPath& path, SkScalar hOffset,
1456 SkScalar vOffset, const SkPaint& paint) {
1457 SkMatrix matrix;
1458
1459 matrix.setTranslate(hOffset, vOffset);
1460 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1461}
1462
reed@android.comf76bacf2009-05-13 14:00:33 +00001463///////////////////////////////////////////////////////////////////////////////
1464
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465void SkCanvas::drawPicture(SkPicture& picture) {
1466 int saveCount = save();
1467 picture.draw(this);
1468 restoreToCount(saveCount);
1469}
1470
reed@android.comf76bacf2009-05-13 14:00:33 +00001471void SkCanvas::drawShape(SkShape* shape) {
1472 // shape baseclass takes care of save/restore
1473 shape->draw(this);
1474}
1475
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476///////////////////////////////////////////////////////////////////////////////
1477///////////////////////////////////////////////////////////////////////////////
1478
1479SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
1480 // need COMPILE_TIME_ASSERT
1481 SkASSERT(sizeof(fStorage) >= sizeof(SkDrawIter));
1482
1483 SkASSERT(canvas);
1484
1485 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1486 fDone = !fImpl->next();
1487}
1488
1489SkCanvas::LayerIter::~LayerIter() {
1490 fImpl->~SkDrawIter();
1491}
1492
1493void SkCanvas::LayerIter::next() {
1494 fDone = !fImpl->next();
1495}
1496
1497SkDevice* SkCanvas::LayerIter::device() const {
1498 return fImpl->getDevice();
1499}
1500
1501const SkMatrix& SkCanvas::LayerIter::matrix() const {
1502 return fImpl->getMatrix();
1503}
1504
1505const SkPaint& SkCanvas::LayerIter::paint() const {
1506 const SkPaint* paint = fImpl->getPaint();
1507 if (NULL == paint) {
1508 paint = &fDefaultPaint;
1509 }
1510 return *paint;
1511}
1512
1513const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1514int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1515int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1516