blob: b05d424cf9aea594657dd77f879fafa05fbc416f [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 }
reed@google.com4b226022011-01-11 18:32:13 +000087 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 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 }
reed@google.com4b226022011-01-11 18:32:13 +0000100
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 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();
reed@google.com4b226022011-01-11 18:32:13 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 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;
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 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)
reed@google.com4b226022011-01-11 18:32:13 +0000123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 if (updateClip) {
125 updateClip->op(x, y, x + width, y + height,
126 SkRegion::kDifference_Op);
127 }
reed@google.com4b226022011-01-11 18:32:13 +0000128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 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)
reed@google.com4b226022011-01-11 18:32:13 +0000173
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 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 }
reed@google.com4b226022011-01-11 18:32:13 +0000191
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 if (flags & SkCanvas::kClip_SaveFlag) {
193 fRegionStorage = *prev->fRegion;
194 fRegion = &fRegionStorage;
195 } else {
196 fRegion = prev->fRegion;
197 }
198
199 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000200 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201
202 fTopLayer = prev->fTopLayer;
203 } else { // no prev
204 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000205
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 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() {
reed@google.com82065d62011-02-07 15:30:46 +0000217 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 SkDELETE(fLayer);
219 dec_rec();
220 }
reed@google.com4b226022011-01-11 18:32:13 +0000221
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222private:
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 }
reed@google.com4b226022011-01-11 18:32:13 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 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 }
reed@google.com4b226022011-01-11 18:32:13 +0000271
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 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 }
reed@google.com4b226022011-01-11 18:32:13 +0000313
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 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 }
reed@google.com4b226022011-01-11 18:32:13 +0000323
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 bool result;
reed@google.com4b226022011-01-11 18:32:13 +0000325
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 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 }
reed@google.com4b226022011-01-11 18:32:13 +0000340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341private:
342 SkDrawLooper* fLooper;
343 SkDrawFilter* fFilter;
344 SkCanvas* fCanvas;
345 SkPaint* fPaint;
346 SkDrawFilter::Type fType;
347 bool fOnce;
348 bool fNeedFilterRestore;
reed@google.com4b226022011-01-11 18:32:13 +0000349
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350};
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);
reed@google.com4b226022011-01-11 18:32:13 +0000399
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400#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);
reed@google.com4b226022011-01-11 18:32:13 +0000481 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 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);
reed@google.com4b226022011-01-11 18:32:13 +0000489 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 SkASSERT(rec && rec->fLayer);
491 SkDevice* rootDevice = rec->fLayer->fDevice;
492
493 if (rootDevice == device) {
494 return device;
495 }
reed@google.com4b226022011-01-11 18:32:13 +0000496
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 /* 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;
reed@google.com4b226022011-01-11 18:32:13 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 /* 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).
reed@google.com4b226022011-01-11 18:32:13 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 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,
reed@google.com4b226022011-01-11 18:32:13 +0000519 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 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;
reed@google.com4b226022011-01-11 18:32:13 +0000534
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 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
reed@google.com4b226022011-01-11 18:32:13 +0000560SkDeviceFactory* SkCanvas::setDeviceFactory(SkDeviceFactory* factory) {
561 SkDELETE(fDeviceFactory);
562 fDeviceFactory = factory;
reed@google.com9b2135a2011-01-11 19:45:38 +0000563 return factory;
reed@google.com4b226022011-01-11 18:32:13 +0000564}
565
566//////////////////////////////////////////////////////////////////////////////
567
reed@google.com51df9e32010-12-23 19:29:18 +0000568bool SkCanvas::readPixels(SkBitmap* bitmap) {
569 SkDevice* device = this->getDevice();
570 if (!device) {
571 return false;
572 }
573 SkIRect bounds;
574 bounds.set(0, 0, device->width(), device->height());
575 return this->readPixels(bounds, bitmap);
576}
577
578void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
579 SkDevice* device = this->getDevice();
580 if (device) {
581 device->writePixels(bitmap, x, y);
582 }
583}
584
reed@android.com8a1c16f2008-12-17 15:59:43 +0000585//////////////////////////////////////////////////////////////////////////////
586
587bool SkCanvas::getViewport(SkIPoint* size) const {
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;
590 if (size)
591 size->set(getDevice()->width(), getDevice()->height());
592 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593}
594
595bool SkCanvas::setViewport(int width, int height) {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000596 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000597 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000598
599 this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height,
600 false, false))->unref();
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000601 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602}
603
604void SkCanvas::updateDeviceCMCache() {
605 if (fDeviceCMDirty) {
606 const SkMatrix& totalMatrix = this->getTotalMatrix();
607 const SkRegion& totalClip = this->getTotalClip();
608 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000609
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 if (NULL == layer->fNext) { // only one layer
611 layer->updateMC(totalMatrix, totalClip, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000612 if (fUseExternalMatrix) {
613 layer->updateExternalMatrix(fExternalMatrix,
614 fExternalInverse);
615 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 } else {
617 SkRegion clip;
618 clip = totalClip; // make a copy
619 do {
620 layer->updateMC(totalMatrix, clip, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000621 if (fUseExternalMatrix) {
622 layer->updateExternalMatrix(fExternalMatrix,
623 fExternalInverse);
624 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 } while ((layer = layer->fNext) != NULL);
626 }
627 fDeviceCMDirty = false;
628 }
629}
630
reed@android.comf2b98d62010-12-20 18:26:13 +0000631void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
632 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000634 if (fLastDeviceToGainFocus != device) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000635 device->gainFocus(this, matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000636 fLastDeviceToGainFocus = device;
637 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638}
639
640///////////////////////////////////////////////////////////////////////////////
641
642int SkCanvas::internalSave(SaveFlags flags) {
643 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000644
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 MCRec* newTop = (MCRec*)fMCStack.push_back();
646 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000647
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 newTop->fNext = fMCRec;
649 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 return saveCount;
652}
653
654int SkCanvas::save(SaveFlags flags) {
655 // call shared impl
656 return this->internalSave(flags);
657}
658
659#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
660#define C16MASK (1 << SkBitmap::kRGB_565_Config)
661#define C8MASK (1 << SkBitmap::kA8_Config)
662
663static SkBitmap::Config resolve_config(SkCanvas* canvas,
664 const SkIRect& bounds,
665 SkCanvas::SaveFlags flags,
666 bool* isOpaque) {
667 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
668
669#if 0
670 // loop through and union all the configs we may draw into
671 uint32_t configMask = 0;
672 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
673 {
674 SkDevice* device = canvas->getLayerDevice(i);
675 if (device->intersects(bounds))
676 configMask |= 1 << device->config();
677 }
678
679 // if the caller wants alpha or fullcolor, we can't return 565
680 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
681 SkCanvas::kHasAlphaLayer_SaveFlag))
682 configMask &= ~C16MASK;
683
684 switch (configMask) {
685 case C8MASK: // if we only have A8, return that
686 return SkBitmap::kA8_Config;
687
688 case C16MASK: // if we only have 565, return that
689 return SkBitmap::kRGB_565_Config;
690
691 default:
692 return SkBitmap::kARGB_8888_Config; // default answer
693 }
694#else
695 return SkBitmap::kARGB_8888_Config; // default answer
696#endif
697}
698
699static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
700 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
701}
702
703int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
704 SaveFlags flags) {
705 // do this before we create the layer. We don't call the public save() since
706 // that would invoke a possibly overridden virtual
707 int count = this->internalSave(flags);
708
709 fDeviceCMDirty = true;
710
711 SkIRect ir;
712 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000713 if (clipBounds.isEmpty()) {
714 return count;
715 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716
717 if (NULL != bounds) {
718 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000719
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 this->getTotalMatrix().mapRect(&r, *bounds);
721 r.roundOut(&ir);
722 // early exit if the layer's bounds are clipped out
723 if (!ir.intersect(clipBounds)) {
724 if (bounds_affects_clip(flags))
725 fMCRec->fRegion->setEmpty();
726 return count;
727 }
728 } else { // no user bounds, so just use the clip
729 ir = clipBounds;
730 }
731
732 // early exit if the clip is now empty
733 if (bounds_affects_clip(flags) &&
734 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
735 return count;
736 }
737
738 bool isOpaque;
739 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
740
741 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
742 isOpaque, true);
743 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
744 device->unref();
745
746 layer->fNext = fMCRec->fTopLayer;
747 fMCRec->fLayer = layer;
748 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
749
750 return count;
751}
752
753int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
754 SaveFlags flags) {
755 if (0xFF == alpha) {
756 return this->saveLayer(bounds, NULL, flags);
757 } else {
758 SkPaint tmpPaint;
759 tmpPaint.setAlpha(alpha);
760 return this->saveLayer(bounds, &tmpPaint, flags);
761 }
762}
763
764void SkCanvas::restore() {
765 // check for underflow
766 if (fMCStack.count() > 1) {
767 this->internalRestore();
768 }
769}
770
771void SkCanvas::internalRestore() {
772 SkASSERT(fMCStack.count() != 0);
773
774 fDeviceCMDirty = true;
775 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000776 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777
778 // reserve our layer (if any)
779 DeviceCM* layer = fMCRec->fLayer; // may be null
780 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
781 fMCRec->fLayer = NULL;
782
783 // now do the normal restore()
784 fMCRec->~MCRec(); // balanced in save()
785 fMCStack.pop_back();
786 fMCRec = (MCRec*)fMCStack.back();
787
788 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
789 since if we're being recorded, we don't want to record this (the
790 recorder will have already recorded the restore).
791 */
792 if (NULL != layer) {
793 if (layer->fNext) {
794 this->drawDevice(layer->fDevice, layer->fX, layer->fY,
795 layer->fPaint);
796 // reset this, since drawDevice will have set it to true
797 fDeviceCMDirty = true;
798 }
799 SkDELETE(layer);
800 }
801}
802
803int SkCanvas::getSaveCount() const {
804 return fMCStack.count();
805}
806
807void SkCanvas::restoreToCount(int count) {
808 // sanity check
809 if (count < 1) {
810 count = 1;
811 }
812 while (fMCStack.count() > count) {
813 this->restore();
814 }
815}
816
817/////////////////////////////////////////////////////////////////////////////
818
819// can't draw it if its empty, or its too big for a fixed-point width or height
820static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000821 return bitmap.width() <= 0 || bitmap.height() <= 0
822#ifndef SK_ALLOW_OVER_32K_BITMAPS
823 || bitmap.width() > 32767 || bitmap.height() > 32767
824#endif
825 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826}
827
reed@android.comf2b98d62010-12-20 18:26:13 +0000828void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 const SkMatrix& matrix, const SkPaint* paint) {
830 if (reject_bitmap(bitmap)) {
831 return;
832 }
833
834 if (NULL == paint) {
835 SkPaint tmpPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000836 this->commonDrawBitmap(bitmap, srcRect, matrix, tmpPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 } else {
reed@android.comf2b98d62010-12-20 18:26:13 +0000838 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 }
840}
841
842void SkCanvas::drawDevice(SkDevice* device, int x, int y,
843 const SkPaint* paint) {
844 SkPaint tmp;
845 if (NULL == paint) {
846 tmp.setDither(true);
847 paint = &tmp;
848 }
reed@google.com4b226022011-01-11 18:32:13 +0000849
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
851 while (iter.next()) {
852 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
853 *paint);
854 }
855 ITER_END
856}
857
858/////////////////////////////////////////////////////////////////////////////
859
860bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
861 fDeviceCMDirty = true;
862 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000863 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864 return fMCRec->fMatrix->preTranslate(dx, dy);
865}
866
867bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
868 fDeviceCMDirty = true;
869 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000870 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871 return fMCRec->fMatrix->preScale(sx, sy);
872}
873
874bool SkCanvas::rotate(SkScalar degrees) {
875 fDeviceCMDirty = true;
876 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000877 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878 return fMCRec->fMatrix->preRotate(degrees);
879}
880
881bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
882 fDeviceCMDirty = true;
883 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000884 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000885 return fMCRec->fMatrix->preSkew(sx, sy);
886}
887
888bool SkCanvas::concat(const SkMatrix& matrix) {
889 fDeviceCMDirty = true;
890 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000891 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892 return fMCRec->fMatrix->preConcat(matrix);
893}
894
895void SkCanvas::setMatrix(const SkMatrix& matrix) {
896 fDeviceCMDirty = true;
897 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000898 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 *fMCRec->fMatrix = matrix;
900}
901
902// this is not virtual, so it must call a virtual method so that subclasses
903// will see its action
904void SkCanvas::resetMatrix() {
905 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000906
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907 matrix.reset();
908 this->setMatrix(matrix);
909}
910
911//////////////////////////////////////////////////////////////////////////////
912
913bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
914 fDeviceCMDirty = true;
915 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000916 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917
918 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000919 // for these simpler matrices, we can stay a rect ever after applying
920 // the matrix. This means we don't have to a) make a path, and b) tell
921 // the region code to scan-convert the path, only to discover that it
922 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 SkRect r;
924 SkIRect ir;
925
926 fMCRec->fMatrix->mapRect(&r, rect);
927 r.round(&ir);
928 return fMCRec->fRegion->op(ir, op);
929 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000930 // since we're rotate or some such thing, we convert the rect to a path
931 // and clip against that, since it can handle any matrix. However, to
932 // avoid recursion in the case where we are subclassed (e.g. Pictures)
933 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 SkPath path;
935
936 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000937 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 }
939}
940
941bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
942 fDeviceCMDirty = true;
943 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000944 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945
946 SkPath devPath;
947 path.transform(*fMCRec->fMatrix, &devPath);
948
949 if (SkRegion::kIntersect_Op == op) {
950 return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
951 } else {
952 SkRegion base;
953 const SkBitmap& bm = this->getDevice()->accessBitmap(false);
954 base.setRect(0, 0, bm.width(), bm.height());
reed@google.com4b226022011-01-11 18:32:13 +0000955
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 if (SkRegion::kReplace_Op == op) {
957 return fMCRec->fRegion->setPath(devPath, base);
958 } else {
959 SkRegion rgn;
960 rgn.setPath(devPath, base);
961 return fMCRec->fRegion->op(rgn, op);
962 }
963 }
964}
965
966bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
967 fDeviceCMDirty = true;
968 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000969 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970
971 return fMCRec->fRegion->op(rgn, op);
972}
973
reed@android.comba09de42010-02-05 20:46:05 +0000974void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +0000976 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
977 fLocalBoundsCompareTypeBW;
978
979 if (!this->getClipBounds(&r, et)) {
980 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 } else {
reed@android.comba09de42010-02-05 20:46:05 +0000982 rCompare.set(SkScalarToCompareType(r.fLeft),
983 SkScalarToCompareType(r.fTop),
984 SkScalarToCompareType(r.fRight),
985 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 }
987}
988
reed@android.comd252db02009-04-01 18:31:44 +0000989/* current impl ignores edgetype, and relies on
990 getLocalClipBoundsCompareType(), which always returns a value assuming
991 antialiasing (worst case)
992 */
reed@android.comba09de42010-02-05 20:46:05 +0000993bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +0000994
995 if (!rect.hasValidCoordinates())
996 return true;
997
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 if (fMCRec->fRegion->isEmpty()) {
999 return true;
1000 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001
reed@android.coma380ae42009-07-21 01:17:02 +00001002 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1003 SkRect dst;
1004 fMCRec->fMatrix->mapRect(&dst, rect);
1005 SkIRect idst;
1006 dst.roundOut(&idst);
1007 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1008 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001009 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001010
reed@android.coma380ae42009-07-21 01:17:02 +00001011 // for speed, do the most likely reject compares first
1012 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1013 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1014 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1015 return true;
1016 }
1017 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1018 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1019 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1020 return true;
1021 }
1022 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024}
1025
1026bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001027 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028}
1029
1030bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1031 /* current impl ignores edgetype, and relies on
1032 getLocalClipBoundsCompareType(), which always returns a value assuming
1033 antialiasing (worst case)
1034 */
1035
1036 if (fMCRec->fRegion->isEmpty()) {
1037 return true;
1038 }
reed@google.com4b226022011-01-11 18:32:13 +00001039
reed@android.comaefd2bc2009-03-30 21:02:14 +00001040 SkScalarCompareType userT = SkScalarToCompareType(top);
1041 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001042
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001044 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 if (userT >= userB) {
1046 return true;
1047 }
reed@google.com4b226022011-01-11 18:32:13 +00001048
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 // check if we are above or below the local clip bounds
1050 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1051 return userT >= clipR.fBottom || userB <= clipR.fTop;
1052}
1053
1054bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1055 const SkRegion& clip = *fMCRec->fRegion;
1056 if (clip.isEmpty()) {
1057 if (bounds) {
1058 bounds->setEmpty();
1059 }
1060 return false;
1061 }
1062
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001063 SkMatrix inverse;
1064 // if we can't invert the CTM, we can't return local clip bounds
1065 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001066 if (bounds) {
1067 bounds->setEmpty();
1068 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001069 return false;
1070 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001072 if (NULL != bounds) {
1073 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001074 // get the clip's bounds
1075 const SkIRect& ibounds = clip.getBounds();
1076 // adjust it outwards if we are antialiasing
1077 int inset = (kAA_EdgeType == et);
1078 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1079 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001080
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081 // invert into local coordinates
1082 inverse.mapRect(bounds, r);
1083 }
1084 return true;
1085}
1086
1087const SkMatrix& SkCanvas::getTotalMatrix() const {
1088 return *fMCRec->fMatrix;
1089}
1090
1091const SkRegion& SkCanvas::getTotalClip() const {
1092 return *fMCRec->fRegion;
1093}
1094
reed@android.comf2b98d62010-12-20 18:26:13 +00001095void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1096 if (NULL == matrix || matrix->isIdentity()) {
1097 if (fUseExternalMatrix) {
1098 fDeviceCMDirty = true;
1099 }
1100 fUseExternalMatrix = false;
1101 } else {
1102 fUseExternalMatrix = true;
1103 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001104
reed@android.comf2b98d62010-12-20 18:26:13 +00001105 fExternalMatrix = *matrix;
1106 matrix->invert(&fExternalInverse);
1107 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001108}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109
reed@android.comf2b98d62010-12-20 18:26:13 +00001110SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1111 bool isOpaque, bool forLayer) {
1112 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113}
1114
1115//////////////////////////////////////////////////////////////////////////////
1116// These are the virtual drawing methods
1117//////////////////////////////////////////////////////////////////////////////
1118
1119void SkCanvas::drawPaint(const SkPaint& paint) {
1120 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
1121
1122 while (iter.next()) {
1123 iter.fDevice->drawPaint(iter, paint);
1124 }
1125
1126 ITER_END
1127}
1128
1129void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1130 const SkPaint& paint) {
1131 if ((long)count <= 0) {
1132 return;
1133 }
1134
1135 SkASSERT(pts != NULL);
1136
1137 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001138
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 while (iter.next()) {
1140 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1141 }
reed@google.com4b226022011-01-11 18:32:13 +00001142
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 ITER_END
1144}
1145
1146void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1147 if (paint.canComputeFastBounds()) {
1148 SkRect storage;
1149 if (this->quickReject(paint.computeFastBounds(r, &storage),
1150 paint2EdgeType(&paint))) {
1151 return;
1152 }
1153 }
reed@google.com4b226022011-01-11 18:32:13 +00001154
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1156
1157 while (iter.next()) {
1158 iter.fDevice->drawRect(iter, r, paint);
1159 }
1160
1161 ITER_END
1162}
1163
1164void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1165 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001166 SkRect storage;
1167 const SkRect& bounds = path.getBounds();
1168 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169 paint2EdgeType(&paint))) {
1170 return;
1171 }
1172 }
1173
1174 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1175
1176 while (iter.next()) {
1177 iter.fDevice->drawPath(iter, path, paint);
1178 }
1179
1180 ITER_END
1181}
1182
1183void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1184 const SkPaint* paint) {
1185 SkDEBUGCODE(bitmap.validate();)
1186
1187 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1188 SkRect fastBounds;
1189 fastBounds.set(x, y,
1190 x + SkIntToScalar(bitmap.width()),
1191 y + SkIntToScalar(bitmap.height()));
1192 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1193 return;
1194 }
1195 }
reed@google.com4b226022011-01-11 18:32:13 +00001196
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197 SkMatrix matrix;
1198 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001199 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200}
1201
1202void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1203 const SkRect& dst, const SkPaint* paint) {
1204 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1205 return;
1206 }
reed@google.com4b226022011-01-11 18:32:13 +00001207
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208 // do this now, to avoid the cost of calling extract for RLE bitmaps
1209 if (this->quickReject(dst, paint2EdgeType(paint))) {
1210 return;
1211 }
reed@google.com4b226022011-01-11 18:32:13 +00001212
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001214
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001216 SkRect tmpSrc;
1217 if (src) {
1218 tmpSrc.set(*src);
1219 // if the extract process clipped off the top or left of the
1220 // original, we adjust for that here to get the position right.
1221 if (tmpSrc.fLeft > 0) {
1222 tmpSrc.fRight -= tmpSrc.fLeft;
1223 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001224 }
reed@android.com87899992009-10-16 14:48:38 +00001225 if (tmpSrc.fTop > 0) {
1226 tmpSrc.fBottom -= tmpSrc.fTop;
1227 tmpSrc.fTop = 0;
1228 }
1229 } else {
1230 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1231 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 }
reed@android.com87899992009-10-16 14:48:38 +00001233 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001234
1235 // ensure that src is "valid" before we pass it to our internal routines
1236 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1237 SkIRect tmpISrc;
1238 if (src) {
1239 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
1240 tmpISrc.intersect(*src);
1241 src = &tmpISrc;
1242 }
1243 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244}
1245
1246void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1247 const SkPaint* paint) {
1248 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001249 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250}
1251
reed@android.comf2b98d62010-12-20 18:26:13 +00001252void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1253 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001255
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001257
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 while (iter.next()) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001259 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 }
reed@android.com9b039062009-02-11 15:09:58 +00001261
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262 ITER_END
1263}
1264
1265void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1266 const SkPaint* paint) {
1267 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 if (reject_bitmap(bitmap)) {
1270 return;
1271 }
reed@google.com4b226022011-01-11 18:32:13 +00001272
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273 SkPaint tmp;
1274 if (NULL == paint) {
1275 paint = &tmp;
1276 }
reed@google.com4b226022011-01-11 18:32:13 +00001277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 while (iter.next()) {
1281 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1282 *paint);
1283 }
1284 ITER_END
1285}
1286
1287void SkCanvas::drawText(const void* text, size_t byteLength,
1288 SkScalar x, SkScalar y, const SkPaint& paint) {
1289 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1290
1291 while (iter.next()) {
1292 iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
1293 }
1294
1295 ITER_END
1296}
1297
1298void SkCanvas::drawPosText(const void* text, size_t byteLength,
1299 const SkPoint pos[], const SkPaint& paint) {
1300 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001301
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 while (iter.next()) {
1303 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
1304 paint);
1305 }
reed@google.com4b226022011-01-11 18:32:13 +00001306
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 ITER_END
1308}
1309
1310void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1311 const SkScalar xpos[], SkScalar constY,
1312 const SkPaint& paint) {
1313 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001314
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 while (iter.next()) {
1316 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
1317 paint);
1318 }
reed@google.com4b226022011-01-11 18:32:13 +00001319
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 ITER_END
1321}
1322
1323void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1324 const SkPath& path, const SkMatrix* matrix,
1325 const SkPaint& paint) {
1326 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1327
1328 while (iter.next()) {
1329 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1330 matrix, paint);
1331 }
1332
1333 ITER_END
1334}
1335
1336void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1337 const SkPoint verts[], const SkPoint texs[],
1338 const SkColor colors[], SkXfermode* xmode,
1339 const uint16_t indices[], int indexCount,
1340 const SkPaint& paint) {
1341 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001342
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 while (iter.next()) {
1344 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1345 colors, xmode, indices, indexCount, paint);
1346 }
reed@google.com4b226022011-01-11 18:32:13 +00001347
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 ITER_END
1349}
1350
reed@android.comcb608442009-12-04 21:32:27 +00001351void SkCanvas::drawData(const void* data, size_t length) {
1352 // do nothing. Subclasses may do something with the data
1353}
1354
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355//////////////////////////////////////////////////////////////////////////////
1356// These methods are NOT virtual, and therefore must call back into virtual
1357// methods, rather than actually drawing themselves.
1358//////////////////////////////////////////////////////////////////////////////
1359
1360void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001361 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362 SkPaint paint;
1363
1364 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001365 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001366 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 }
1368 this->drawPaint(paint);
1369}
1370
reed@android.com845fdac2009-06-23 03:01:32 +00001371void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 SkPaint paint;
1373
1374 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001375 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001376 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 }
1378 this->drawPaint(paint);
1379}
1380
1381void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1382 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001383
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384 pt.set(x, y);
1385 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1386}
1387
1388void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1389 SkPoint pt;
1390 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001391
reed@android.com8a1c16f2008-12-17 15:59:43 +00001392 pt.set(x, y);
1393 paint.setColor(color);
1394 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1395}
1396
1397void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1398 const SkPaint& paint) {
1399 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001400
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 pts[0].set(x0, y0);
1402 pts[1].set(x1, y1);
1403 this->drawPoints(kLines_PointMode, 2, pts, paint);
1404}
1405
1406void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1407 SkScalar right, SkScalar bottom,
1408 const SkPaint& paint) {
1409 SkRect r;
1410
1411 r.set(left, top, right, bottom);
1412 this->drawRect(r, paint);
1413}
1414
1415void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1416 const SkPaint& paint) {
1417 if (radius < 0) {
1418 radius = 0;
1419 }
1420
1421 SkRect r;
1422 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001423
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 if (paint.canComputeFastBounds()) {
1425 SkRect storage;
1426 if (this->quickReject(paint.computeFastBounds(r, &storage),
1427 paint2EdgeType(&paint))) {
1428 return;
1429 }
1430 }
reed@google.com4b226022011-01-11 18:32:13 +00001431
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 SkPath path;
1433 path.addOval(r);
1434 this->drawPath(path, paint);
1435}
1436
1437void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1438 const SkPaint& paint) {
1439 if (rx > 0 && ry > 0) {
1440 if (paint.canComputeFastBounds()) {
1441 SkRect storage;
1442 if (this->quickReject(paint.computeFastBounds(r, &storage),
1443 paint2EdgeType(&paint))) {
1444 return;
1445 }
1446 }
1447
1448 SkPath path;
1449 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1450 this->drawPath(path, paint);
1451 } else {
1452 this->drawRect(r, paint);
1453 }
1454}
1455
1456void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1457 if (paint.canComputeFastBounds()) {
1458 SkRect storage;
1459 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1460 paint2EdgeType(&paint))) {
1461 return;
1462 }
1463 }
1464
1465 SkPath path;
1466 path.addOval(oval);
1467 this->drawPath(path, paint);
1468}
1469
1470void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1471 SkScalar sweepAngle, bool useCenter,
1472 const SkPaint& paint) {
1473 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1474 this->drawOval(oval, paint);
1475 } else {
1476 SkPath path;
1477 if (useCenter) {
1478 path.moveTo(oval.centerX(), oval.centerY());
1479 }
1480 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1481 if (useCenter) {
1482 path.close();
1483 }
1484 this->drawPath(path, paint);
1485 }
1486}
1487
1488void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1489 const SkPath& path, SkScalar hOffset,
1490 SkScalar vOffset, const SkPaint& paint) {
1491 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001492
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493 matrix.setTranslate(hOffset, vOffset);
1494 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1495}
1496
reed@android.comf76bacf2009-05-13 14:00:33 +00001497///////////////////////////////////////////////////////////////////////////////
1498
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499void SkCanvas::drawPicture(SkPicture& picture) {
1500 int saveCount = save();
1501 picture.draw(this);
1502 restoreToCount(saveCount);
1503}
1504
reed@android.comf76bacf2009-05-13 14:00:33 +00001505void SkCanvas::drawShape(SkShape* shape) {
1506 // shape baseclass takes care of save/restore
1507 shape->draw(this);
1508}
1509
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510///////////////////////////////////////////////////////////////////////////////
1511///////////////////////////////////////////////////////////////////////////////
1512
1513SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001514 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515
1516 SkASSERT(canvas);
1517
1518 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1519 fDone = !fImpl->next();
1520}
1521
1522SkCanvas::LayerIter::~LayerIter() {
1523 fImpl->~SkDrawIter();
1524}
1525
1526void SkCanvas::LayerIter::next() {
1527 fDone = !fImpl->next();
1528}
1529
1530SkDevice* SkCanvas::LayerIter::device() const {
1531 return fImpl->getDevice();
1532}
1533
1534const SkMatrix& SkCanvas::LayerIter::matrix() const {
1535 return fImpl->getMatrix();
1536}
1537
1538const SkPaint& SkCanvas::LayerIter::paint() const {
1539 const SkPaint* paint = fImpl->getPaint();
1540 if (NULL == paint) {
1541 paint = &fDefaultPaint;
1542 }
1543 return *paint;
1544}
1545
1546const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1547int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1548int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1549