blob: faf030c81ed9a1b5f93e560d7ef6b142863de69a [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 {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994 if (fMCRec->fRegion->isEmpty()) {
995 return true;
996 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997
reed@android.coma380ae42009-07-21 01:17:02 +0000998 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
999 SkRect dst;
1000 fMCRec->fMatrix->mapRect(&dst, rect);
1001 SkIRect idst;
1002 dst.roundOut(&idst);
1003 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1004 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001005 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001006
reed@android.coma380ae42009-07-21 01:17:02 +00001007 // for speed, do the most likely reject compares first
1008 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1009 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1010 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1011 return true;
1012 }
1013 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1014 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1015 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1016 return true;
1017 }
1018 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001020}
1021
1022bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001023 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001024}
1025
1026bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1027 /* current impl ignores edgetype, and relies on
1028 getLocalClipBoundsCompareType(), which always returns a value assuming
1029 antialiasing (worst case)
1030 */
1031
1032 if (fMCRec->fRegion->isEmpty()) {
1033 return true;
1034 }
reed@google.com4b226022011-01-11 18:32:13 +00001035
reed@android.comaefd2bc2009-03-30 21:02:14 +00001036 SkScalarCompareType userT = SkScalarToCompareType(top);
1037 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001038
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001040 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 if (userT >= userB) {
1042 return true;
1043 }
reed@google.com4b226022011-01-11 18:32:13 +00001044
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 // check if we are above or below the local clip bounds
1046 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1047 return userT >= clipR.fBottom || userB <= clipR.fTop;
1048}
1049
1050bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1051 const SkRegion& clip = *fMCRec->fRegion;
1052 if (clip.isEmpty()) {
1053 if (bounds) {
1054 bounds->setEmpty();
1055 }
1056 return false;
1057 }
1058
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001059 SkMatrix inverse;
1060 // if we can't invert the CTM, we can't return local clip bounds
1061 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001062 if (bounds) {
1063 bounds->setEmpty();
1064 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001065 return false;
1066 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001068 if (NULL != bounds) {
1069 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070 // get the clip's bounds
1071 const SkIRect& ibounds = clip.getBounds();
1072 // adjust it outwards if we are antialiasing
1073 int inset = (kAA_EdgeType == et);
1074 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1075 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001076
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 // invert into local coordinates
1078 inverse.mapRect(bounds, r);
1079 }
1080 return true;
1081}
1082
1083const SkMatrix& SkCanvas::getTotalMatrix() const {
1084 return *fMCRec->fMatrix;
1085}
1086
1087const SkRegion& SkCanvas::getTotalClip() const {
1088 return *fMCRec->fRegion;
1089}
1090
reed@android.comf2b98d62010-12-20 18:26:13 +00001091void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1092 if (NULL == matrix || matrix->isIdentity()) {
1093 if (fUseExternalMatrix) {
1094 fDeviceCMDirty = true;
1095 }
1096 fUseExternalMatrix = false;
1097 } else {
1098 fUseExternalMatrix = true;
1099 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001100
reed@android.comf2b98d62010-12-20 18:26:13 +00001101 fExternalMatrix = *matrix;
1102 matrix->invert(&fExternalInverse);
1103 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001104}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105
reed@android.comf2b98d62010-12-20 18:26:13 +00001106SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1107 bool isOpaque, bool forLayer) {
1108 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109}
1110
1111//////////////////////////////////////////////////////////////////////////////
1112// These are the virtual drawing methods
1113//////////////////////////////////////////////////////////////////////////////
1114
1115void SkCanvas::drawPaint(const SkPaint& paint) {
1116 ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
1117
1118 while (iter.next()) {
1119 iter.fDevice->drawPaint(iter, paint);
1120 }
1121
1122 ITER_END
1123}
1124
1125void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1126 const SkPaint& paint) {
1127 if ((long)count <= 0) {
1128 return;
1129 }
1130
1131 SkASSERT(pts != NULL);
1132
1133 ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001134
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 while (iter.next()) {
1136 iter.fDevice->drawPoints(iter, mode, count, pts, paint);
1137 }
reed@google.com4b226022011-01-11 18:32:13 +00001138
reed@android.com8a1c16f2008-12-17 15:59:43 +00001139 ITER_END
1140}
1141
1142void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1143 if (paint.canComputeFastBounds()) {
1144 SkRect storage;
1145 if (this->quickReject(paint.computeFastBounds(r, &storage),
1146 paint2EdgeType(&paint))) {
1147 return;
1148 }
1149 }
reed@google.com4b226022011-01-11 18:32:13 +00001150
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
1152
1153 while (iter.next()) {
1154 iter.fDevice->drawRect(iter, r, paint);
1155 }
1156
1157 ITER_END
1158}
1159
1160void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1161 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001162 SkRect storage;
1163 const SkRect& bounds = path.getBounds();
1164 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 paint2EdgeType(&paint))) {
1166 return;
1167 }
1168 }
1169
1170 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
1171
1172 while (iter.next()) {
1173 iter.fDevice->drawPath(iter, path, paint);
1174 }
1175
1176 ITER_END
1177}
1178
1179void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1180 const SkPaint* paint) {
1181 SkDEBUGCODE(bitmap.validate();)
1182
1183 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1184 SkRect fastBounds;
1185 fastBounds.set(x, y,
1186 x + SkIntToScalar(bitmap.width()),
1187 y + SkIntToScalar(bitmap.height()));
1188 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1189 return;
1190 }
1191 }
reed@google.com4b226022011-01-11 18:32:13 +00001192
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 SkMatrix matrix;
1194 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001195 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196}
1197
1198void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1199 const SkRect& dst, const SkPaint* paint) {
1200 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1201 return;
1202 }
reed@google.com4b226022011-01-11 18:32:13 +00001203
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 // do this now, to avoid the cost of calling extract for RLE bitmaps
1205 if (this->quickReject(dst, paint2EdgeType(paint))) {
1206 return;
1207 }
reed@google.com4b226022011-01-11 18:32:13 +00001208
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001210
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001212 SkRect tmpSrc;
1213 if (src) {
1214 tmpSrc.set(*src);
1215 // if the extract process clipped off the top or left of the
1216 // original, we adjust for that here to get the position right.
1217 if (tmpSrc.fLeft > 0) {
1218 tmpSrc.fRight -= tmpSrc.fLeft;
1219 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001220 }
reed@android.com87899992009-10-16 14:48:38 +00001221 if (tmpSrc.fTop > 0) {
1222 tmpSrc.fBottom -= tmpSrc.fTop;
1223 tmpSrc.fTop = 0;
1224 }
1225 } else {
1226 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1227 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 }
reed@android.com87899992009-10-16 14:48:38 +00001229 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001230
1231 // ensure that src is "valid" before we pass it to our internal routines
1232 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1233 SkIRect tmpISrc;
1234 if (src) {
1235 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
1236 tmpISrc.intersect(*src);
1237 src = &tmpISrc;
1238 }
1239 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240}
1241
1242void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1243 const SkPaint* paint) {
1244 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001245 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246}
1247
reed@android.comf2b98d62010-12-20 18:26:13 +00001248void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1249 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001251
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252 ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001253
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 while (iter.next()) {
reed@android.comf2b98d62010-12-20 18:26:13 +00001255 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 }
reed@android.com9b039062009-02-11 15:09:58 +00001257
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258 ITER_END
1259}
1260
1261void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1262 const SkPaint* paint) {
1263 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001264
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265 if (reject_bitmap(bitmap)) {
1266 return;
1267 }
reed@google.com4b226022011-01-11 18:32:13 +00001268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 SkPaint tmp;
1270 if (NULL == paint) {
1271 paint = &tmp;
1272 }
reed@google.com4b226022011-01-11 18:32:13 +00001273
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001275
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276 while (iter.next()) {
1277 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
1278 *paint);
1279 }
1280 ITER_END
1281}
1282
1283void SkCanvas::drawText(const void* text, size_t byteLength,
1284 SkScalar x, SkScalar y, const SkPaint& paint) {
1285 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1286
1287 while (iter.next()) {
1288 iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
1289 }
1290
1291 ITER_END
1292}
1293
1294void SkCanvas::drawPosText(const void* text, size_t byteLength,
1295 const SkPoint pos[], const SkPaint& paint) {
1296 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001297
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 while (iter.next()) {
1299 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
1300 paint);
1301 }
reed@google.com4b226022011-01-11 18:32:13 +00001302
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 ITER_END
1304}
1305
1306void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1307 const SkScalar xpos[], SkScalar constY,
1308 const SkPaint& paint) {
1309 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001310
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 while (iter.next()) {
1312 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
1313 paint);
1314 }
reed@google.com4b226022011-01-11 18:32:13 +00001315
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316 ITER_END
1317}
1318
1319void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1320 const SkPath& path, const SkMatrix* matrix,
1321 const SkPaint& paint) {
1322 ITER_BEGIN(paint, SkDrawFilter::kText_Type)
1323
1324 while (iter.next()) {
1325 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
1326 matrix, paint);
1327 }
1328
1329 ITER_END
1330}
1331
1332void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1333 const SkPoint verts[], const SkPoint texs[],
1334 const SkColor colors[], SkXfermode* xmode,
1335 const uint16_t indices[], int indexCount,
1336 const SkPaint& paint) {
1337 ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001338
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 while (iter.next()) {
1340 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
1341 colors, xmode, indices, indexCount, paint);
1342 }
reed@google.com4b226022011-01-11 18:32:13 +00001343
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344 ITER_END
1345}
1346
reed@android.comcb608442009-12-04 21:32:27 +00001347void SkCanvas::drawData(const void* data, size_t length) {
1348 // do nothing. Subclasses may do something with the data
1349}
1350
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351//////////////////////////////////////////////////////////////////////////////
1352// These methods are NOT virtual, and therefore must call back into virtual
1353// methods, rather than actually drawing themselves.
1354//////////////////////////////////////////////////////////////////////////////
1355
1356void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001357 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 SkPaint paint;
1359
1360 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001361 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001362 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 }
1364 this->drawPaint(paint);
1365}
1366
reed@android.com845fdac2009-06-23 03:01:32 +00001367void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 SkPaint paint;
1369
1370 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001371 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001372 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 }
1374 this->drawPaint(paint);
1375}
1376
1377void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1378 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001379
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380 pt.set(x, y);
1381 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1382}
1383
1384void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1385 SkPoint pt;
1386 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001387
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 pt.set(x, y);
1389 paint.setColor(color);
1390 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1391}
1392
1393void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1394 const SkPaint& paint) {
1395 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001396
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 pts[0].set(x0, y0);
1398 pts[1].set(x1, y1);
1399 this->drawPoints(kLines_PointMode, 2, pts, paint);
1400}
1401
1402void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1403 SkScalar right, SkScalar bottom,
1404 const SkPaint& paint) {
1405 SkRect r;
1406
1407 r.set(left, top, right, bottom);
1408 this->drawRect(r, paint);
1409}
1410
1411void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1412 const SkPaint& paint) {
1413 if (radius < 0) {
1414 radius = 0;
1415 }
1416
1417 SkRect r;
1418 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001419
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 if (paint.canComputeFastBounds()) {
1421 SkRect storage;
1422 if (this->quickReject(paint.computeFastBounds(r, &storage),
1423 paint2EdgeType(&paint))) {
1424 return;
1425 }
1426 }
reed@google.com4b226022011-01-11 18:32:13 +00001427
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 SkPath path;
1429 path.addOval(r);
1430 this->drawPath(path, paint);
1431}
1432
1433void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1434 const SkPaint& paint) {
1435 if (rx > 0 && ry > 0) {
1436 if (paint.canComputeFastBounds()) {
1437 SkRect storage;
1438 if (this->quickReject(paint.computeFastBounds(r, &storage),
1439 paint2EdgeType(&paint))) {
1440 return;
1441 }
1442 }
1443
1444 SkPath path;
1445 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1446 this->drawPath(path, paint);
1447 } else {
1448 this->drawRect(r, paint);
1449 }
1450}
1451
1452void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1453 if (paint.canComputeFastBounds()) {
1454 SkRect storage;
1455 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1456 paint2EdgeType(&paint))) {
1457 return;
1458 }
1459 }
1460
1461 SkPath path;
1462 path.addOval(oval);
1463 this->drawPath(path, paint);
1464}
1465
1466void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1467 SkScalar sweepAngle, bool useCenter,
1468 const SkPaint& paint) {
1469 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1470 this->drawOval(oval, paint);
1471 } else {
1472 SkPath path;
1473 if (useCenter) {
1474 path.moveTo(oval.centerX(), oval.centerY());
1475 }
1476 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1477 if (useCenter) {
1478 path.close();
1479 }
1480 this->drawPath(path, paint);
1481 }
1482}
1483
1484void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1485 const SkPath& path, SkScalar hOffset,
1486 SkScalar vOffset, const SkPaint& paint) {
1487 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001488
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 matrix.setTranslate(hOffset, vOffset);
1490 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1491}
1492
reed@android.comf76bacf2009-05-13 14:00:33 +00001493///////////////////////////////////////////////////////////////////////////////
1494
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495void SkCanvas::drawPicture(SkPicture& picture) {
1496 int saveCount = save();
1497 picture.draw(this);
1498 restoreToCount(saveCount);
1499}
1500
reed@android.comf76bacf2009-05-13 14:00:33 +00001501void SkCanvas::drawShape(SkShape* shape) {
1502 // shape baseclass takes care of save/restore
1503 shape->draw(this);
1504}
1505
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506///////////////////////////////////////////////////////////////////////////////
1507///////////////////////////////////////////////////////////////////////////////
1508
1509SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001510 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511
1512 SkASSERT(canvas);
1513
1514 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1515 fDone = !fImpl->next();
1516}
1517
1518SkCanvas::LayerIter::~LayerIter() {
1519 fImpl->~SkDrawIter();
1520}
1521
1522void SkCanvas::LayerIter::next() {
1523 fDone = !fImpl->next();
1524}
1525
1526SkDevice* SkCanvas::LayerIter::device() const {
1527 return fImpl->getDevice();
1528}
1529
1530const SkMatrix& SkCanvas::LayerIter::matrix() const {
1531 return fImpl->getMatrix();
1532}
1533
1534const SkPaint& SkCanvas::LayerIter::paint() const {
1535 const SkPaint* paint = fImpl->getPaint();
1536 if (NULL == paint) {
1537 paint = &fDefaultPaint;
1538 }
1539 return *paint;
1540}
1541
1542const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1543int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1544int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1545