blob: 8f536210371e24a5828880bcf2d82a5f5648792b [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;
200 fFilter->safeRef();
201
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() {
217 fFilter->safeUnref();
218 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;
563}
564
565//////////////////////////////////////////////////////////////////////////////
566
reed@google.com51df9e32010-12-23 19:29:18 +0000567bool SkCanvas::readPixels(SkBitmap* bitmap) {
568 SkDevice* device = this->getDevice();
569 if (!device) {
570 return false;
571 }
572 SkIRect bounds;
573 bounds.set(0, 0, device->width(), device->height());
574 return this->readPixels(bounds, bitmap);
575}
576
577void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
578 SkDevice* device = this->getDevice();
579 if (device) {
580 device->writePixels(bitmap, x, y);
581 }
582}
583
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584//////////////////////////////////////////////////////////////////////////////
585
586bool SkCanvas::getViewport(SkIPoint* size) const {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000587 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000588 return false;
589 if (size)
590 size->set(getDevice()->width(), getDevice()->height());
591 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592}
593
594bool SkCanvas::setViewport(int width, int height) {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000595 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000596 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000597
598 this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height,
599 false, false))->unref();
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000600 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601}
602
603void SkCanvas::updateDeviceCMCache() {
604 if (fDeviceCMDirty) {
605 const SkMatrix& totalMatrix = this->getTotalMatrix();
606 const SkRegion& totalClip = this->getTotalClip();
607 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 if (NULL == layer->fNext) { // only one layer
610 layer->updateMC(totalMatrix, totalClip, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000611 if (fUseExternalMatrix) {
612 layer->updateExternalMatrix(fExternalMatrix,
613 fExternalInverse);
614 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 } else {
616 SkRegion clip;
617 clip = totalClip; // make a copy
618 do {
619 layer->updateMC(totalMatrix, clip, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000620 if (fUseExternalMatrix) {
621 layer->updateExternalMatrix(fExternalMatrix,
622 fExternalInverse);
623 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 } while ((layer = layer->fNext) != NULL);
625 }
626 fDeviceCMDirty = false;
627 }
628}
629
reed@android.comf2b98d62010-12-20 18:26:13 +0000630void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
631 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000633 if (fLastDeviceToGainFocus != device) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000634 device->gainFocus(this, matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000635 fLastDeviceToGainFocus = device;
636 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637}
638
639///////////////////////////////////////////////////////////////////////////////
640
641int SkCanvas::internalSave(SaveFlags flags) {
642 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000643
reed@android.com8a1c16f2008-12-17 15:59:43 +0000644 MCRec* newTop = (MCRec*)fMCStack.push_back();
645 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000646
reed@android.com8a1c16f2008-12-17 15:59:43 +0000647 newTop->fNext = fMCRec;
648 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000649
reed@android.com8a1c16f2008-12-17 15:59:43 +0000650 return saveCount;
651}
652
653int SkCanvas::save(SaveFlags flags) {
654 // call shared impl
655 return this->internalSave(flags);
656}
657
658#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
659#define C16MASK (1 << SkBitmap::kRGB_565_Config)
660#define C8MASK (1 << SkBitmap::kA8_Config)
661
662static SkBitmap::Config resolve_config(SkCanvas* canvas,
663 const SkIRect& bounds,
664 SkCanvas::SaveFlags flags,
665 bool* isOpaque) {
666 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
667
668#if 0
669 // loop through and union all the configs we may draw into
670 uint32_t configMask = 0;
671 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
672 {
673 SkDevice* device = canvas->getLayerDevice(i);
674 if (device->intersects(bounds))
675 configMask |= 1 << device->config();
676 }
677
678 // if the caller wants alpha or fullcolor, we can't return 565
679 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
680 SkCanvas::kHasAlphaLayer_SaveFlag))
681 configMask &= ~C16MASK;
682
683 switch (configMask) {
684 case C8MASK: // if we only have A8, return that
685 return SkBitmap::kA8_Config;
686
687 case C16MASK: // if we only have 565, return that
688 return SkBitmap::kRGB_565_Config;
689
690 default:
691 return SkBitmap::kARGB_8888_Config; // default answer
692 }
693#else
694 return SkBitmap::kARGB_8888_Config; // default answer
695#endif
696}
697
698static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
699 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
700}
701
702int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
703 SaveFlags flags) {
704 // do this before we create the layer. We don't call the public save() since
705 // that would invoke a possibly overridden virtual
706 int count = this->internalSave(flags);
707
708 fDeviceCMDirty = true;
709
710 SkIRect ir;
711 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000712 if (clipBounds.isEmpty()) {
713 return count;
714 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715
716 if (NULL != bounds) {
717 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000718
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 this->getTotalMatrix().mapRect(&r, *bounds);
720 r.roundOut(&ir);
721 // early exit if the layer's bounds are clipped out
722 if (!ir.intersect(clipBounds)) {
723 if (bounds_affects_clip(flags))
724 fMCRec->fRegion->setEmpty();
725 return count;
726 }
727 } else { // no user bounds, so just use the clip
728 ir = clipBounds;
729 }
730
731 // early exit if the clip is now empty
732 if (bounds_affects_clip(flags) &&
733 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
734 return count;
735 }
736
737 bool isOpaque;
738 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
739
740 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
741 isOpaque, true);
742 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
743 device->unref();
744
745 layer->fNext = fMCRec->fTopLayer;
746 fMCRec->fLayer = layer;
747 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
748
749 return count;
750}
751
752int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
753 SaveFlags flags) {
754 if (0xFF == alpha) {
755 return this->saveLayer(bounds, NULL, flags);
756 } else {
757 SkPaint tmpPaint;
758 tmpPaint.setAlpha(alpha);
759 return this->saveLayer(bounds, &tmpPaint, flags);
760 }
761}
762
763void SkCanvas::restore() {
764 // check for underflow
765 if (fMCStack.count() > 1) {
766 this->internalRestore();
767 }
768}
769
770void SkCanvas::internalRestore() {
771 SkASSERT(fMCStack.count() != 0);
772
773 fDeviceCMDirty = true;
774 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000775 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776
777 // reserve our layer (if any)
778 DeviceCM* layer = fMCRec->fLayer; // may be null
779 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
780 fMCRec->fLayer = NULL;
781
782 // now do the normal restore()
783 fMCRec->~MCRec(); // balanced in save()
784 fMCStack.pop_back();
785 fMCRec = (MCRec*)fMCStack.back();
786
787 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
788 since if we're being recorded, we don't want to record this (the
789 recorder will have already recorded the restore).
790 */
791 if (NULL != layer) {
792 if (layer->fNext) {
793 this->drawDevice(layer->fDevice, layer->fX, layer->fY,
794 layer->fPaint);
795 // reset this, since drawDevice will have set it to true
796 fDeviceCMDirty = true;
797 }
798 SkDELETE(layer);
799 }
800}
801
802int SkCanvas::getSaveCount() const {
803 return fMCStack.count();
804}
805
806void SkCanvas::restoreToCount(int count) {
807 // sanity check
808 if (count < 1) {
809 count = 1;
810 }
811 while (fMCStack.count() > count) {
812 this->restore();
813 }
814}
815
816/////////////////////////////////////////////////////////////////////////////
817
818// can't draw it if its empty, or its too big for a fixed-point width or height
819static bool reject_bitmap(const SkBitmap& bitmap) {
820 return bitmap.width() <= 0 || bitmap.height() <= 0 ||
821 bitmap.width() > 32767 || bitmap.height() > 32767;
822}
823
reed@android.comf2b98d62010-12-20 18:26:13 +0000824void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 const SkMatrix& matrix, const SkPaint* paint) {
826 if (reject_bitmap(bitmap)) {
827 return;
828 }
829
830 if (NULL == paint) {
831 SkPaint tmpPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000832 this->commonDrawBitmap(bitmap, srcRect, matrix, tmpPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833 } else {
reed@android.comf2b98d62010-12-20 18:26:13 +0000834 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 }
836}
837
838void SkCanvas::drawDevice(SkDevice* device, int x, int y,
839 const SkPaint* paint) {
840 SkPaint tmp;
841 if (NULL == paint) {
842 tmp.setDither(true);
843 paint = &tmp;
844 }
reed@google.com4b226022011-01-11 18:32:13 +0000845
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
847 while (iter.next()) {
848 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
849 *paint);
850 }
851 ITER_END
852}
853
854/////////////////////////////////////////////////////////////////////////////
855
856bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
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->preTranslate(dx, dy);
861}
862
863bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
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->preScale(sx, sy);
868}
869
870bool SkCanvas::rotate(SkScalar degrees) {
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->preRotate(degrees);
875}
876
877bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
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->preSkew(sx, sy);
882}
883
884bool SkCanvas::concat(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 return fMCRec->fMatrix->preConcat(matrix);
889}
890
891void SkCanvas::setMatrix(const SkMatrix& matrix) {
892 fDeviceCMDirty = true;
893 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000894 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 *fMCRec->fMatrix = matrix;
896}
897
898// this is not virtual, so it must call a virtual method so that subclasses
899// will see its action
900void SkCanvas::resetMatrix() {
901 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000902
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 matrix.reset();
904 this->setMatrix(matrix);
905}
906
907//////////////////////////////////////////////////////////////////////////////
908
909bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
910 fDeviceCMDirty = true;
911 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000912 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913
914 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000915 // for these simpler matrices, we can stay a rect ever after applying
916 // the matrix. This means we don't have to a) make a path, and b) tell
917 // the region code to scan-convert the path, only to discover that it
918 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919 SkRect r;
920 SkIRect ir;
921
922 fMCRec->fMatrix->mapRect(&r, rect);
923 r.round(&ir);
924 return fMCRec->fRegion->op(ir, op);
925 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000926 // since we're rotate or some such thing, we convert the rect to a path
927 // and clip against that, since it can handle any matrix. However, to
928 // avoid recursion in the case where we are subclassed (e.g. Pictures)
929 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 SkPath path;
931
932 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000933 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 }
935}
936
937bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
938 fDeviceCMDirty = true;
939 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000940 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941
942 SkPath devPath;
943 path.transform(*fMCRec->fMatrix, &devPath);
944
945 if (SkRegion::kIntersect_Op == op) {
946 return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
947 } else {
948 SkRegion base;
949 const SkBitmap& bm = this->getDevice()->accessBitmap(false);
950 base.setRect(0, 0, bm.width(), bm.height());
reed@google.com4b226022011-01-11 18:32:13 +0000951
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 if (SkRegion::kReplace_Op == op) {
953 return fMCRec->fRegion->setPath(devPath, base);
954 } else {
955 SkRegion rgn;
956 rgn.setPath(devPath, base);
957 return fMCRec->fRegion->op(rgn, op);
958 }
959 }
960}
961
962bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
963 fDeviceCMDirty = true;
964 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000965 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966
967 return fMCRec->fRegion->op(rgn, op);
968}
969
reed@android.comba09de42010-02-05 20:46:05 +0000970void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +0000972 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
973 fLocalBoundsCompareTypeBW;
974
975 if (!this->getClipBounds(&r, et)) {
976 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 } else {
reed@android.comba09de42010-02-05 20:46:05 +0000978 rCompare.set(SkScalarToCompareType(r.fLeft),
979 SkScalarToCompareType(r.fTop),
980 SkScalarToCompareType(r.fRight),
981 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982 }
983}
984
reed@android.comd252db02009-04-01 18:31:44 +0000985/* current impl ignores edgetype, and relies on
986 getLocalClipBoundsCompareType(), which always returns a value assuming
987 antialiasing (worst case)
988 */
reed@android.comba09de42010-02-05 20:46:05 +0000989bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 if (fMCRec->fRegion->isEmpty()) {
991 return true;
992 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993
reed@android.coma380ae42009-07-21 01:17:02 +0000994 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
995 SkRect dst;
996 fMCRec->fMatrix->mapRect(&dst, rect);
997 SkIRect idst;
998 dst.roundOut(&idst);
999 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1000 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001001 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001002
reed@android.coma380ae42009-07-21 01:17:02 +00001003 // for speed, do the most likely reject compares first
1004 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1005 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1006 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1007 return true;
1008 }
1009 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1010 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1011 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1012 return true;
1013 }
1014 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016}
1017
1018bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001019 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020}
1021
1022bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1023 /* current impl ignores edgetype, and relies on
1024 getLocalClipBoundsCompareType(), which always returns a value assuming
1025 antialiasing (worst case)
1026 */
1027
1028 if (fMCRec->fRegion->isEmpty()) {
1029 return true;
1030 }
reed@google.com4b226022011-01-11 18:32:13 +00001031
reed@android.comaefd2bc2009-03-30 21:02:14 +00001032 SkScalarCompareType userT = SkScalarToCompareType(top);
1033 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001034
reed@android.com8a1c16f2008-12-17 15:59:43 +00001035 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001036 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 if (userT >= userB) {
1038 return true;
1039 }
reed@google.com4b226022011-01-11 18:32:13 +00001040
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 // check if we are above or below the local clip bounds
1042 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1043 return userT >= clipR.fBottom || userB <= clipR.fTop;
1044}
1045
1046bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1047 const SkRegion& clip = *fMCRec->fRegion;
1048 if (clip.isEmpty()) {
1049 if (bounds) {
1050 bounds->setEmpty();
1051 }
1052 return false;
1053 }
1054
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001055 SkMatrix inverse;
1056 // if we can't invert the CTM, we can't return local clip bounds
1057 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001058 if (bounds) {
1059 bounds->setEmpty();
1060 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001061 return false;
1062 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001064 if (NULL != bounds) {
1065 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 // get the clip's bounds
1067 const SkIRect& ibounds = clip.getBounds();
1068 // adjust it outwards if we are antialiasing
1069 int inset = (kAA_EdgeType == et);
1070 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1071 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001072
reed@android.com8a1c16f2008-12-17 15:59:43 +00001073 // invert into local coordinates
1074 inverse.mapRect(bounds, r);
1075 }
1076 return true;
1077}
1078
1079const SkMatrix& SkCanvas::getTotalMatrix() const {
1080 return *fMCRec->fMatrix;
1081}
1082
1083const SkRegion& SkCanvas::getTotalClip() const {
1084 return *fMCRec->fRegion;
1085}
1086
reed@android.comf2b98d62010-12-20 18:26:13 +00001087void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1088 if (NULL == matrix || matrix->isIdentity()) {
1089 if (fUseExternalMatrix) {
1090 fDeviceCMDirty = true;
1091 }
1092 fUseExternalMatrix = false;
1093 } else {
1094 fUseExternalMatrix = true;
1095 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001096
reed@android.comf2b98d62010-12-20 18:26:13 +00001097 fExternalMatrix = *matrix;
1098 matrix->invert(&fExternalInverse);
1099 }
reed@google.com4b226022011-01-11 18:32:13 +00001100
reed@android.comf2b98d62010-12-20 18:26:13 +00001101 static bool gUseExt;
1102 if (gUseExt != fUseExternalMatrix && false) {
1103 gUseExt = fUseExternalMatrix;
1104 printf("---- fUseExternalMatrix = %d\n", fUseExternalMatrix);
1105 }
1106}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107
reed@android.comf2b98d62010-12-20 18:26:13 +00001108SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1109 bool isOpaque, bool forLayer) {
1110 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111}
1112
1113//////////////////////////////////////////////////////////////////////////////
1114// These are the virtual drawing methods
1115//////////////////////////////////////////////////////////////////////////////
1116
1117void SkCanvas::drawPaint(const SkPaint& paint) {
1118 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
1119
1120 while (iter.next()) {
1121 iter.fDevice->drawPaint(iter, paint);
1122 }
1123
1124 ITER_END
1125}
1126
1127void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1128 const SkPaint& paint) {
1129 if ((long)count <= 0) {
1130 return;
1131 }
1132
1133 SkASSERT(pts != NULL);
1134
1135 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001136
reed@android.com8a1c16f2008-12-17 15:59:43 +00001137 while (iter.next()) {
1138 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1139 }
reed@google.com4b226022011-01-11 18:32:13 +00001140
reed@android.com8a1c16f2008-12-17 15:59:43 +00001141 ITER_END
1142}
1143
1144void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1145 if (paint.canComputeFastBounds()) {
1146 SkRect storage;
1147 if (this->quickReject(paint.computeFastBounds(r, &storage),
1148 paint2EdgeType(&paint))) {
1149 return;
1150 }
1151 }
reed@google.com4b226022011-01-11 18:32:13 +00001152
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1154
1155 while (iter.next()) {
1156 iter.fDevice->drawRect(iter, r, paint);
1157 }
1158
1159 ITER_END
1160}
1161
1162void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1163 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001164 SkRect storage;
1165 const SkRect& bounds = path.getBounds();
1166 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167 paint2EdgeType(&paint))) {
1168 return;
1169 }
1170 }
1171
1172 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1173
1174 while (iter.next()) {
1175 iter.fDevice->drawPath(iter, path, paint);
1176 }
1177
1178 ITER_END
1179}
1180
1181void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1182 const SkPaint* paint) {
1183 SkDEBUGCODE(bitmap.validate();)
1184
1185 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1186 SkRect fastBounds;
1187 fastBounds.set(x, y,
1188 x + SkIntToScalar(bitmap.width()),
1189 y + SkIntToScalar(bitmap.height()));
1190 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1191 return;
1192 }
1193 }
reed@google.com4b226022011-01-11 18:32:13 +00001194
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 SkMatrix matrix;
1196 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001197 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198}
1199
1200void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1201 const SkRect& dst, const SkPaint* paint) {
1202 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1203 return;
1204 }
reed@google.com4b226022011-01-11 18:32:13 +00001205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 // do this now, to avoid the cost of calling extract for RLE bitmaps
1207 if (this->quickReject(dst, paint2EdgeType(paint))) {
1208 return;
1209 }
reed@google.com4b226022011-01-11 18:32:13 +00001210
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001212
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001214 SkRect tmpSrc;
1215 if (src) {
1216 tmpSrc.set(*src);
1217 // if the extract process clipped off the top or left of the
1218 // original, we adjust for that here to get the position right.
1219 if (tmpSrc.fLeft > 0) {
1220 tmpSrc.fRight -= tmpSrc.fLeft;
1221 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001222 }
reed@android.com87899992009-10-16 14:48:38 +00001223 if (tmpSrc.fTop > 0) {
1224 tmpSrc.fBottom -= tmpSrc.fTop;
1225 tmpSrc.fTop = 0;
1226 }
1227 } else {
1228 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1229 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 }
reed@android.com87899992009-10-16 14:48:38 +00001231 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001232
1233 // ensure that src is "valid" before we pass it to our internal routines
1234 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1235 SkIRect tmpISrc;
1236 if (src) {
1237 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
1238 tmpISrc.intersect(*src);
1239 src = &tmpISrc;
1240 }
1241 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242}
1243
1244void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1245 const SkPaint* paint) {
1246 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001247 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248}
1249
reed@android.comf2b98d62010-12-20 18:26:13 +00001250void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1251 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001253
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001255
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 while (iter.next()) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001257 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 }
reed@android.com9b039062009-02-11 15:09:58 +00001259
reed@android.com8a1c16f2008-12-17 15:59:43 +00001260 ITER_END
1261}
1262
1263void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1264 const SkPaint* paint) {
1265 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001266
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267 if (reject_bitmap(bitmap)) {
1268 return;
1269 }
reed@google.com4b226022011-01-11 18:32:13 +00001270
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 SkPaint tmp;
1272 if (NULL == paint) {
1273 paint = &tmp;
1274 }
reed@google.com4b226022011-01-11 18:32:13 +00001275
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 while (iter.next()) {
1279 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1280 *paint);
1281 }
1282 ITER_END
1283}
1284
1285void SkCanvas::drawText(const void* text, size_t byteLength,
1286 SkScalar x, SkScalar y, const SkPaint& paint) {
1287 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1288
1289 while (iter.next()) {
1290 iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
1291 }
1292
1293 ITER_END
1294}
1295
1296void SkCanvas::drawPosText(const void* text, size_t byteLength,
1297 const SkPoint pos[], const SkPaint& paint) {
1298 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001299
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 while (iter.next()) {
1301 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
1302 paint);
1303 }
reed@google.com4b226022011-01-11 18:32:13 +00001304
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 ITER_END
1306}
1307
1308void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1309 const SkScalar xpos[], SkScalar constY,
1310 const SkPaint& paint) {
1311 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001312
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 while (iter.next()) {
1314 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
1315 paint);
1316 }
reed@google.com4b226022011-01-11 18:32:13 +00001317
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 ITER_END
1319}
1320
1321void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1322 const SkPath& path, const SkMatrix* matrix,
1323 const SkPaint& paint) {
1324 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1325
1326 while (iter.next()) {
1327 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1328 matrix, paint);
1329 }
1330
1331 ITER_END
1332}
1333
1334void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1335 const SkPoint verts[], const SkPoint texs[],
1336 const SkColor colors[], SkXfermode* xmode,
1337 const uint16_t indices[], int indexCount,
1338 const SkPaint& paint) {
1339 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001340
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 while (iter.next()) {
1342 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1343 colors, xmode, indices, indexCount, paint);
1344 }
reed@google.com4b226022011-01-11 18:32:13 +00001345
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346 ITER_END
1347}
1348
reed@android.comcb608442009-12-04 21:32:27 +00001349void SkCanvas::drawData(const void* data, size_t length) {
1350 // do nothing. Subclasses may do something with the data
1351}
1352
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353//////////////////////////////////////////////////////////////////////////////
1354// These methods are NOT virtual, and therefore must call back into virtual
1355// methods, rather than actually drawing themselves.
1356//////////////////////////////////////////////////////////////////////////////
1357
1358void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001359 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 SkPaint paint;
1361
1362 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001363 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001364 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365 }
1366 this->drawPaint(paint);
1367}
1368
reed@android.com845fdac2009-06-23 03:01:32 +00001369void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 SkPaint paint;
1371
1372 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001373 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001374 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 }
1376 this->drawPaint(paint);
1377}
1378
1379void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1380 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001381
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382 pt.set(x, y);
1383 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1384}
1385
1386void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1387 SkPoint pt;
1388 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001389
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 pt.set(x, y);
1391 paint.setColor(color);
1392 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1393}
1394
1395void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1396 const SkPaint& paint) {
1397 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001398
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 pts[0].set(x0, y0);
1400 pts[1].set(x1, y1);
1401 this->drawPoints(kLines_PointMode, 2, pts, paint);
1402}
1403
1404void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1405 SkScalar right, SkScalar bottom,
1406 const SkPaint& paint) {
1407 SkRect r;
1408
1409 r.set(left, top, right, bottom);
1410 this->drawRect(r, paint);
1411}
1412
1413void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1414 const SkPaint& paint) {
1415 if (radius < 0) {
1416 radius = 0;
1417 }
1418
1419 SkRect r;
1420 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001421
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 if (paint.canComputeFastBounds()) {
1423 SkRect storage;
1424 if (this->quickReject(paint.computeFastBounds(r, &storage),
1425 paint2EdgeType(&paint))) {
1426 return;
1427 }
1428 }
reed@google.com4b226022011-01-11 18:32:13 +00001429
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 SkPath path;
1431 path.addOval(r);
1432 this->drawPath(path, paint);
1433}
1434
1435void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1436 const SkPaint& paint) {
1437 if (rx > 0 && ry > 0) {
1438 if (paint.canComputeFastBounds()) {
1439 SkRect storage;
1440 if (this->quickReject(paint.computeFastBounds(r, &storage),
1441 paint2EdgeType(&paint))) {
1442 return;
1443 }
1444 }
1445
1446 SkPath path;
1447 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1448 this->drawPath(path, paint);
1449 } else {
1450 this->drawRect(r, paint);
1451 }
1452}
1453
1454void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1455 if (paint.canComputeFastBounds()) {
1456 SkRect storage;
1457 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1458 paint2EdgeType(&paint))) {
1459 return;
1460 }
1461 }
1462
1463 SkPath path;
1464 path.addOval(oval);
1465 this->drawPath(path, paint);
1466}
1467
1468void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1469 SkScalar sweepAngle, bool useCenter,
1470 const SkPaint& paint) {
1471 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1472 this->drawOval(oval, paint);
1473 } else {
1474 SkPath path;
1475 if (useCenter) {
1476 path.moveTo(oval.centerX(), oval.centerY());
1477 }
1478 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1479 if (useCenter) {
1480 path.close();
1481 }
1482 this->drawPath(path, paint);
1483 }
1484}
1485
1486void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1487 const SkPath& path, SkScalar hOffset,
1488 SkScalar vOffset, const SkPaint& paint) {
1489 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001490
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491 matrix.setTranslate(hOffset, vOffset);
1492 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1493}
1494
reed@android.comf76bacf2009-05-13 14:00:33 +00001495///////////////////////////////////////////////////////////////////////////////
1496
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497void SkCanvas::drawPicture(SkPicture& picture) {
1498 int saveCount = save();
1499 picture.draw(this);
1500 restoreToCount(saveCount);
1501}
1502
reed@android.comf76bacf2009-05-13 14:00:33 +00001503void SkCanvas::drawShape(SkShape* shape) {
1504 // shape baseclass takes care of save/restore
1505 shape->draw(this);
1506}
1507
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508///////////////////////////////////////////////////////////////////////////////
1509///////////////////////////////////////////////////////////////////////////////
1510
1511SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001512 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513
1514 SkASSERT(canvas);
1515
1516 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1517 fDone = !fImpl->next();
1518}
1519
1520SkCanvas::LayerIter::~LayerIter() {
1521 fImpl->~SkDrawIter();
1522}
1523
1524void SkCanvas::LayerIter::next() {
1525 fDone = !fImpl->next();
1526}
1527
1528SkDevice* SkCanvas::LayerIter::device() const {
1529 return fImpl->getDevice();
1530}
1531
1532const SkMatrix& SkCanvas::LayerIter::matrix() const {
1533 return fImpl->getMatrix();
1534}
1535
1536const SkPaint& SkCanvas::LayerIter::paint() const {
1537 const SkPaint* paint = fImpl->getPaint();
1538 if (NULL == paint) {
1539 paint = &fDefaultPaint;
1540 }
1541 return *paint;
1542}
1543
1544const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1545int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1546int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1547