blob: 076164147d5ed5a9074627bb84d909d3a7f4b624 [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
reed@google.com51df9e32010-12-23 19:29:18 +0000552bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
553 SkDevice* device = this->getDevice();
554 if (!device) {
555 return false;
556 }
557 return device->readPixels(srcRect, bitmap);
558}
559
560bool SkCanvas::readPixels(SkBitmap* bitmap) {
561 SkDevice* device = this->getDevice();
562 if (!device) {
563 return false;
564 }
565 SkIRect bounds;
566 bounds.set(0, 0, device->width(), device->height());
567 return this->readPixels(bounds, bitmap);
568}
569
570void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
571 SkDevice* device = this->getDevice();
572 if (device) {
573 device->writePixels(bitmap, x, y);
574 }
575}
576
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577//////////////////////////////////////////////////////////////////////////////
578
579bool SkCanvas::getViewport(SkIPoint* size) const {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000580 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000581 return false;
582 if (size)
583 size->set(getDevice()->width(), getDevice()->height());
584 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585}
586
587bool SkCanvas::setViewport(int width, int height) {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000588 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000589 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000590
591 this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height,
592 false, false))->unref();
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000593 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594}
595
596void SkCanvas::updateDeviceCMCache() {
597 if (fDeviceCMDirty) {
598 const SkMatrix& totalMatrix = this->getTotalMatrix();
599 const SkRegion& totalClip = this->getTotalClip();
600 DeviceCM* layer = fMCRec->fTopLayer;
601
602 if (NULL == layer->fNext) { // only one layer
603 layer->updateMC(totalMatrix, totalClip, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000604 if (fUseExternalMatrix) {
605 layer->updateExternalMatrix(fExternalMatrix,
606 fExternalInverse);
607 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 } else {
609 SkRegion clip;
610 clip = totalClip; // make a copy
611 do {
612 layer->updateMC(totalMatrix, clip, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000613 if (fUseExternalMatrix) {
614 layer->updateExternalMatrix(fExternalMatrix,
615 fExternalInverse);
616 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 } while ((layer = layer->fNext) != NULL);
618 }
619 fDeviceCMDirty = false;
620 }
621}
622
reed@android.comf2b98d62010-12-20 18:26:13 +0000623void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
624 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000626 if (fLastDeviceToGainFocus != device) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000627 device->gainFocus(this, matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000628 fLastDeviceToGainFocus = device;
629 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630}
631
632///////////////////////////////////////////////////////////////////////////////
633
634int SkCanvas::internalSave(SaveFlags flags) {
635 int saveCount = this->getSaveCount(); // record this before the actual save
636
637 MCRec* newTop = (MCRec*)fMCStack.push_back();
638 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
639
640 newTop->fNext = fMCRec;
641 fMCRec = newTop;
642
643 return saveCount;
644}
645
646int SkCanvas::save(SaveFlags flags) {
647 // call shared impl
648 return this->internalSave(flags);
649}
650
651#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
652#define C16MASK (1 << SkBitmap::kRGB_565_Config)
653#define C8MASK (1 << SkBitmap::kA8_Config)
654
655static SkBitmap::Config resolve_config(SkCanvas* canvas,
656 const SkIRect& bounds,
657 SkCanvas::SaveFlags flags,
658 bool* isOpaque) {
659 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
660
661#if 0
662 // loop through and union all the configs we may draw into
663 uint32_t configMask = 0;
664 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
665 {
666 SkDevice* device = canvas->getLayerDevice(i);
667 if (device->intersects(bounds))
668 configMask |= 1 << device->config();
669 }
670
671 // if the caller wants alpha or fullcolor, we can't return 565
672 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
673 SkCanvas::kHasAlphaLayer_SaveFlag))
674 configMask &= ~C16MASK;
675
676 switch (configMask) {
677 case C8MASK: // if we only have A8, return that
678 return SkBitmap::kA8_Config;
679
680 case C16MASK: // if we only have 565, return that
681 return SkBitmap::kRGB_565_Config;
682
683 default:
684 return SkBitmap::kARGB_8888_Config; // default answer
685 }
686#else
687 return SkBitmap::kARGB_8888_Config; // default answer
688#endif
689}
690
691static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
692 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
693}
694
695int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
696 SaveFlags flags) {
697 // do this before we create the layer. We don't call the public save() since
698 // that would invoke a possibly overridden virtual
699 int count = this->internalSave(flags);
700
701 fDeviceCMDirty = true;
702
703 SkIRect ir;
704 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000705 if (clipBounds.isEmpty()) {
706 return count;
707 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708
709 if (NULL != bounds) {
710 SkRect r;
711
712 this->getTotalMatrix().mapRect(&r, *bounds);
713 r.roundOut(&ir);
714 // early exit if the layer's bounds are clipped out
715 if (!ir.intersect(clipBounds)) {
716 if (bounds_affects_clip(flags))
717 fMCRec->fRegion->setEmpty();
718 return count;
719 }
720 } else { // no user bounds, so just use the clip
721 ir = clipBounds;
722 }
723
724 // early exit if the clip is now empty
725 if (bounds_affects_clip(flags) &&
726 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
727 return count;
728 }
729
730 bool isOpaque;
731 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
732
733 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
734 isOpaque, true);
735 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
736 device->unref();
737
738 layer->fNext = fMCRec->fTopLayer;
739 fMCRec->fLayer = layer;
740 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
741
742 return count;
743}
744
745int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
746 SaveFlags flags) {
747 if (0xFF == alpha) {
748 return this->saveLayer(bounds, NULL, flags);
749 } else {
750 SkPaint tmpPaint;
751 tmpPaint.setAlpha(alpha);
752 return this->saveLayer(bounds, &tmpPaint, flags);
753 }
754}
755
756void SkCanvas::restore() {
757 // check for underflow
758 if (fMCStack.count() > 1) {
759 this->internalRestore();
760 }
761}
762
763void SkCanvas::internalRestore() {
764 SkASSERT(fMCStack.count() != 0);
765
766 fDeviceCMDirty = true;
767 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000768 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769
770 // reserve our layer (if any)
771 DeviceCM* layer = fMCRec->fLayer; // may be null
772 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
773 fMCRec->fLayer = NULL;
774
775 // now do the normal restore()
776 fMCRec->~MCRec(); // balanced in save()
777 fMCStack.pop_back();
778 fMCRec = (MCRec*)fMCStack.back();
779
780 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
781 since if we're being recorded, we don't want to record this (the
782 recorder will have already recorded the restore).
783 */
784 if (NULL != layer) {
785 if (layer->fNext) {
786 this->drawDevice(layer->fDevice, layer->fX, layer->fY,
787 layer->fPaint);
788 // reset this, since drawDevice will have set it to true
789 fDeviceCMDirty = true;
790 }
791 SkDELETE(layer);
792 }
793}
794
795int SkCanvas::getSaveCount() const {
796 return fMCStack.count();
797}
798
799void SkCanvas::restoreToCount(int count) {
800 // sanity check
801 if (count < 1) {
802 count = 1;
803 }
804 while (fMCStack.count() > count) {
805 this->restore();
806 }
807}
808
809/////////////////////////////////////////////////////////////////////////////
810
811// can't draw it if its empty, or its too big for a fixed-point width or height
812static bool reject_bitmap(const SkBitmap& bitmap) {
813 return bitmap.width() <= 0 || bitmap.height() <= 0 ||
814 bitmap.width() > 32767 || bitmap.height() > 32767;
815}
816
reed@android.comf2b98d62010-12-20 18:26:13 +0000817void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 const SkMatrix& matrix, const SkPaint* paint) {
819 if (reject_bitmap(bitmap)) {
820 return;
821 }
822
823 if (NULL == paint) {
824 SkPaint tmpPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000825 this->commonDrawBitmap(bitmap, srcRect, matrix, tmpPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 } else {
reed@android.comf2b98d62010-12-20 18:26:13 +0000827 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 }
829}
830
831void SkCanvas::drawDevice(SkDevice* device, int x, int y,
832 const SkPaint* paint) {
833 SkPaint tmp;
834 if (NULL == paint) {
835 tmp.setDither(true);
836 paint = &tmp;
837 }
838
839 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
840 while (iter.next()) {
841 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
842 *paint);
843 }
844 ITER_END
845}
846
847/////////////////////////////////////////////////////////////////////////////
848
849bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
850 fDeviceCMDirty = true;
851 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000852 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 return fMCRec->fMatrix->preTranslate(dx, dy);
854}
855
856bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
857 fDeviceCMDirty = true;
858 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000859 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 return fMCRec->fMatrix->preScale(sx, sy);
861}
862
863bool SkCanvas::rotate(SkScalar degrees) {
864 fDeviceCMDirty = true;
865 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000866 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 return fMCRec->fMatrix->preRotate(degrees);
868}
869
870bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
871 fDeviceCMDirty = true;
872 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000873 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 return fMCRec->fMatrix->preSkew(sx, sy);
875}
876
877bool SkCanvas::concat(const SkMatrix& matrix) {
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 return fMCRec->fMatrix->preConcat(matrix);
882}
883
884void SkCanvas::setMatrix(const SkMatrix& matrix) {
885 fDeviceCMDirty = true;
886 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000887 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888 *fMCRec->fMatrix = matrix;
889}
890
891// this is not virtual, so it must call a virtual method so that subclasses
892// will see its action
893void SkCanvas::resetMatrix() {
894 SkMatrix matrix;
895
896 matrix.reset();
897 this->setMatrix(matrix);
898}
899
900//////////////////////////////////////////////////////////////////////////////
901
902bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
903 fDeviceCMDirty = true;
904 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000905 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906
907 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000908 // for these simpler matrices, we can stay a rect ever after applying
909 // the matrix. This means we don't have to a) make a path, and b) tell
910 // the region code to scan-convert the path, only to discover that it
911 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 SkRect r;
913 SkIRect ir;
914
915 fMCRec->fMatrix->mapRect(&r, rect);
916 r.round(&ir);
917 return fMCRec->fRegion->op(ir, op);
918 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000919 // since we're rotate or some such thing, we convert the rect to a path
920 // and clip against that, since it can handle any matrix. However, to
921 // avoid recursion in the case where we are subclassed (e.g. Pictures)
922 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 SkPath path;
924
925 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000926 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 }
928}
929
930bool SkCanvas::clipPath(const SkPath& path, 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 SkPath devPath;
936 path.transform(*fMCRec->fMatrix, &devPath);
937
938 if (SkRegion::kIntersect_Op == op) {
939 return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
940 } else {
941 SkRegion base;
942 const SkBitmap& bm = this->getDevice()->accessBitmap(false);
943 base.setRect(0, 0, bm.width(), bm.height());
944
945 if (SkRegion::kReplace_Op == op) {
946 return fMCRec->fRegion->setPath(devPath, base);
947 } else {
948 SkRegion rgn;
949 rgn.setPath(devPath, base);
950 return fMCRec->fRegion->op(rgn, op);
951 }
952 }
953}
954
955bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
956 fDeviceCMDirty = true;
957 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000958 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959
960 return fMCRec->fRegion->op(rgn, op);
961}
962
reed@android.comba09de42010-02-05 20:46:05 +0000963void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +0000965 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
966 fLocalBoundsCompareTypeBW;
967
968 if (!this->getClipBounds(&r, et)) {
969 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 } else {
reed@android.comba09de42010-02-05 20:46:05 +0000971 rCompare.set(SkScalarToCompareType(r.fLeft),
972 SkScalarToCompareType(r.fTop),
973 SkScalarToCompareType(r.fRight),
974 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 }
976}
977
reed@android.comd252db02009-04-01 18:31:44 +0000978/* current impl ignores edgetype, and relies on
979 getLocalClipBoundsCompareType(), which always returns a value assuming
980 antialiasing (worst case)
981 */
reed@android.comba09de42010-02-05 20:46:05 +0000982bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 if (fMCRec->fRegion->isEmpty()) {
984 return true;
985 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986
reed@android.coma380ae42009-07-21 01:17:02 +0000987 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
988 SkRect dst;
989 fMCRec->fMatrix->mapRect(&dst, rect);
990 SkIRect idst;
991 dst.roundOut(&idst);
992 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
993 } else {
reed@android.comba09de42010-02-05 20:46:05 +0000994 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +0000995
reed@android.coma380ae42009-07-21 01:17:02 +0000996 // for speed, do the most likely reject compares first
997 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
998 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
999 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1000 return true;
1001 }
1002 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1003 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1004 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1005 return true;
1006 }
1007 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001008 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009}
1010
1011bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001012 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013}
1014
1015bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1016 /* current impl ignores edgetype, and relies on
1017 getLocalClipBoundsCompareType(), which always returns a value assuming
1018 antialiasing (worst case)
1019 */
1020
1021 if (fMCRec->fRegion->isEmpty()) {
1022 return true;
1023 }
1024
reed@android.comaefd2bc2009-03-30 21:02:14 +00001025 SkScalarCompareType userT = SkScalarToCompareType(top);
1026 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027
1028 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001029 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 if (userT >= userB) {
1031 return true;
1032 }
1033
1034 // check if we are above or below the local clip bounds
1035 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1036 return userT >= clipR.fBottom || userB <= clipR.fTop;
1037}
1038
1039bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1040 const SkRegion& clip = *fMCRec->fRegion;
1041 if (clip.isEmpty()) {
1042 if (bounds) {
1043 bounds->setEmpty();
1044 }
1045 return false;
1046 }
1047
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001048 SkMatrix inverse;
1049 // if we can't invert the CTM, we can't return local clip bounds
1050 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001051 if (bounds) {
1052 bounds->setEmpty();
1053 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001054 return false;
1055 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001057 if (NULL != bounds) {
1058 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 // get the clip's bounds
1060 const SkIRect& ibounds = clip.getBounds();
1061 // adjust it outwards if we are antialiasing
1062 int inset = (kAA_EdgeType == et);
1063 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1064 ibounds.fRight + inset, ibounds.fBottom + inset);
1065
1066 // invert into local coordinates
1067 inverse.mapRect(bounds, r);
1068 }
1069 return true;
1070}
1071
1072const SkMatrix& SkCanvas::getTotalMatrix() const {
1073 return *fMCRec->fMatrix;
1074}
1075
1076const SkRegion& SkCanvas::getTotalClip() const {
1077 return *fMCRec->fRegion;
1078}
1079
reed@android.comf2b98d62010-12-20 18:26:13 +00001080void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1081 if (NULL == matrix || matrix->isIdentity()) {
1082 if (fUseExternalMatrix) {
1083 fDeviceCMDirty = true;
1084 }
1085 fUseExternalMatrix = false;
1086 } else {
1087 fUseExternalMatrix = true;
1088 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
1089
1090 fExternalMatrix = *matrix;
1091 matrix->invert(&fExternalInverse);
1092 }
1093
1094 static bool gUseExt;
1095 if (gUseExt != fUseExternalMatrix && false) {
1096 gUseExt = fUseExternalMatrix;
1097 printf("---- fUseExternalMatrix = %d\n", fUseExternalMatrix);
1098 }
1099}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100
reed@android.comf2b98d62010-12-20 18:26:13 +00001101SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1102 bool isOpaque, bool forLayer) {
1103 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104}
1105
1106//////////////////////////////////////////////////////////////////////////////
1107// These are the virtual drawing methods
1108//////////////////////////////////////////////////////////////////////////////
1109
1110void SkCanvas::drawPaint(const SkPaint& paint) {
1111 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
1112
1113 while (iter.next()) {
1114 iter.fDevice->drawPaint(iter, paint);
1115 }
1116
1117 ITER_END
1118}
1119
1120void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1121 const SkPaint& paint) {
1122 if ((long)count <= 0) {
1123 return;
1124 }
1125
1126 SkASSERT(pts != NULL);
1127
1128 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
1129
1130 while (iter.next()) {
1131 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1132 }
1133
1134 ITER_END
1135}
1136
1137void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1138 if (paint.canComputeFastBounds()) {
1139 SkRect storage;
1140 if (this->quickReject(paint.computeFastBounds(r, &storage),
1141 paint2EdgeType(&paint))) {
1142 return;
1143 }
1144 }
1145
1146 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1147
1148 while (iter.next()) {
1149 iter.fDevice->drawRect(iter, r, paint);
1150 }
1151
1152 ITER_END
1153}
1154
1155void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1156 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001157 SkRect storage;
1158 const SkRect& bounds = path.getBounds();
1159 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160 paint2EdgeType(&paint))) {
1161 return;
1162 }
1163 }
1164
1165 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1166
1167 while (iter.next()) {
1168 iter.fDevice->drawPath(iter, path, paint);
1169 }
1170
1171 ITER_END
1172}
1173
1174void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1175 const SkPaint* paint) {
1176 SkDEBUGCODE(bitmap.validate();)
1177
1178 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1179 SkRect fastBounds;
1180 fastBounds.set(x, y,
1181 x + SkIntToScalar(bitmap.width()),
1182 y + SkIntToScalar(bitmap.height()));
1183 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1184 return;
1185 }
1186 }
1187
1188 SkMatrix matrix;
1189 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001190 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191}
1192
1193void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1194 const SkRect& dst, const SkPaint* paint) {
1195 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1196 return;
1197 }
1198
1199 // do this now, to avoid the cost of calling extract for RLE bitmaps
1200 if (this->quickReject(dst, paint2EdgeType(paint))) {
1201 return;
1202 }
1203
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 const SkBitmap* bitmapPtr = &bitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001207 SkRect tmpSrc;
1208 if (src) {
1209 tmpSrc.set(*src);
1210 // if the extract process clipped off the top or left of the
1211 // original, we adjust for that here to get the position right.
1212 if (tmpSrc.fLeft > 0) {
1213 tmpSrc.fRight -= tmpSrc.fLeft;
1214 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001215 }
reed@android.com87899992009-10-16 14:48:38 +00001216 if (tmpSrc.fTop > 0) {
1217 tmpSrc.fBottom -= tmpSrc.fTop;
1218 tmpSrc.fTop = 0;
1219 }
1220 } else {
1221 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1222 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 }
reed@android.com87899992009-10-16 14:48:38 +00001224 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001225
1226 // ensure that src is "valid" before we pass it to our internal routines
1227 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1228 SkIRect tmpISrc;
1229 if (src) {
1230 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
1231 tmpISrc.intersect(*src);
1232 src = &tmpISrc;
1233 }
1234 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235}
1236
1237void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1238 const SkPaint* paint) {
1239 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001240 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241}
1242
reed@android.comf2b98d62010-12-20 18:26:13 +00001243void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1244 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001246
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001248
reed@android.com8a1c16f2008-12-17 15:59:43 +00001249 while (iter.next()) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001250 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 }
reed@android.com9b039062009-02-11 15:09:58 +00001252
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253 ITER_END
1254}
1255
1256void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1257 const SkPaint* paint) {
1258 SkDEBUGCODE(bitmap.validate();)
1259
1260 if (reject_bitmap(bitmap)) {
1261 return;
1262 }
1263
1264 SkPaint tmp;
1265 if (NULL == paint) {
1266 paint = &tmp;
1267 }
1268
1269 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
1270
1271 while (iter.next()) {
1272 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1273 *paint);
1274 }
1275 ITER_END
1276}
1277
1278void SkCanvas::drawText(const void* text, size_t byteLength,
1279 SkScalar x, SkScalar y, const SkPaint& paint) {
1280 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1281
1282 while (iter.next()) {
1283 iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
1284 }
1285
1286 ITER_END
1287}
1288
1289void SkCanvas::drawPosText(const void* text, size_t byteLength,
1290 const SkPoint pos[], const SkPaint& paint) {
1291 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1292
1293 while (iter.next()) {
1294 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
1295 paint);
1296 }
1297
1298 ITER_END
1299}
1300
1301void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1302 const SkScalar xpos[], SkScalar constY,
1303 const SkPaint& paint) {
1304 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1305
1306 while (iter.next()) {
1307 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
1308 paint);
1309 }
1310
1311 ITER_END
1312}
1313
1314void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1315 const SkPath& path, const SkMatrix* matrix,
1316 const SkPaint& paint) {
1317 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1318
1319 while (iter.next()) {
1320 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1321 matrix, paint);
1322 }
1323
1324 ITER_END
1325}
1326
1327void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1328 const SkPoint verts[], const SkPoint texs[],
1329 const SkColor colors[], SkXfermode* xmode,
1330 const uint16_t indices[], int indexCount,
1331 const SkPaint& paint) {
1332 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1333
1334 while (iter.next()) {
1335 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1336 colors, xmode, indices, indexCount, paint);
1337 }
1338
1339 ITER_END
1340}
1341
reed@android.comcb608442009-12-04 21:32:27 +00001342void SkCanvas::drawData(const void* data, size_t length) {
1343 // do nothing. Subclasses may do something with the data
1344}
1345
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346//////////////////////////////////////////////////////////////////////////////
1347// These methods are NOT virtual, and therefore must call back into virtual
1348// methods, rather than actually drawing themselves.
1349//////////////////////////////////////////////////////////////////////////////
1350
1351void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001352 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 SkPaint paint;
1354
1355 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001356 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001357 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 }
1359 this->drawPaint(paint);
1360}
1361
reed@android.com845fdac2009-06-23 03:01:32 +00001362void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 SkPaint paint;
1364
1365 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001366 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001367 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 }
1369 this->drawPaint(paint);
1370}
1371
1372void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1373 SkPoint pt;
1374
1375 pt.set(x, y);
1376 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1377}
1378
1379void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1380 SkPoint pt;
1381 SkPaint paint;
1382
1383 pt.set(x, y);
1384 paint.setColor(color);
1385 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1386}
1387
1388void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1389 const SkPaint& paint) {
1390 SkPoint pts[2];
1391
1392 pts[0].set(x0, y0);
1393 pts[1].set(x1, y1);
1394 this->drawPoints(kLines_PointMode, 2, pts, paint);
1395}
1396
1397void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1398 SkScalar right, SkScalar bottom,
1399 const SkPaint& paint) {
1400 SkRect r;
1401
1402 r.set(left, top, right, bottom);
1403 this->drawRect(r, paint);
1404}
1405
1406void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1407 const SkPaint& paint) {
1408 if (radius < 0) {
1409 radius = 0;
1410 }
1411
1412 SkRect r;
1413 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
1414
1415 if (paint.canComputeFastBounds()) {
1416 SkRect storage;
1417 if (this->quickReject(paint.computeFastBounds(r, &storage),
1418 paint2EdgeType(&paint))) {
1419 return;
1420 }
1421 }
1422
1423 SkPath path;
1424 path.addOval(r);
1425 this->drawPath(path, paint);
1426}
1427
1428void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1429 const SkPaint& paint) {
1430 if (rx > 0 && ry > 0) {
1431 if (paint.canComputeFastBounds()) {
1432 SkRect storage;
1433 if (this->quickReject(paint.computeFastBounds(r, &storage),
1434 paint2EdgeType(&paint))) {
1435 return;
1436 }
1437 }
1438
1439 SkPath path;
1440 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1441 this->drawPath(path, paint);
1442 } else {
1443 this->drawRect(r, paint);
1444 }
1445}
1446
1447void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1448 if (paint.canComputeFastBounds()) {
1449 SkRect storage;
1450 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1451 paint2EdgeType(&paint))) {
1452 return;
1453 }
1454 }
1455
1456 SkPath path;
1457 path.addOval(oval);
1458 this->drawPath(path, paint);
1459}
1460
1461void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1462 SkScalar sweepAngle, bool useCenter,
1463 const SkPaint& paint) {
1464 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1465 this->drawOval(oval, paint);
1466 } else {
1467 SkPath path;
1468 if (useCenter) {
1469 path.moveTo(oval.centerX(), oval.centerY());
1470 }
1471 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1472 if (useCenter) {
1473 path.close();
1474 }
1475 this->drawPath(path, paint);
1476 }
1477}
1478
1479void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1480 const SkPath& path, SkScalar hOffset,
1481 SkScalar vOffset, const SkPaint& paint) {
1482 SkMatrix matrix;
1483
1484 matrix.setTranslate(hOffset, vOffset);
1485 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1486}
1487
reed@android.comf76bacf2009-05-13 14:00:33 +00001488///////////////////////////////////////////////////////////////////////////////
1489
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490void SkCanvas::drawPicture(SkPicture& picture) {
1491 int saveCount = save();
1492 picture.draw(this);
1493 restoreToCount(saveCount);
1494}
1495
reed@android.comf76bacf2009-05-13 14:00:33 +00001496void SkCanvas::drawShape(SkShape* shape) {
1497 // shape baseclass takes care of save/restore
1498 shape->draw(this);
1499}
1500
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501///////////////////////////////////////////////////////////////////////////////
1502///////////////////////////////////////////////////////////////////////////////
1503
1504SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001505 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506
1507 SkASSERT(canvas);
1508
1509 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1510 fDone = !fImpl->next();
1511}
1512
1513SkCanvas::LayerIter::~LayerIter() {
1514 fImpl->~SkDrawIter();
1515}
1516
1517void SkCanvas::LayerIter::next() {
1518 fDone = !fImpl->next();
1519}
1520
1521SkDevice* SkCanvas::LayerIter::device() const {
1522 return fImpl->getDevice();
1523}
1524
1525const SkMatrix& SkCanvas::LayerIter::matrix() const {
1526 return fImpl->getMatrix();
1527}
1528
1529const SkPaint& SkCanvas::LayerIter::paint() const {
1530 const SkPaint* paint = fImpl->getPaint();
1531 if (NULL == paint) {
1532 paint = &fDefaultPaint;
1533 }
1534 return *paint;
1535}
1536
1537const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1538int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1539int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1540