blob: 0b6f086313c3ed5c7860b87a261a25f05001b579 [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"
reed@google.coma076e9b2011-04-06 20:17:29 +000027#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkUtils.h"
29#include <new>
30
31//#define SK_TRACE_SAVERESTORE
32
33#ifdef SK_TRACE_SAVERESTORE
34 static int gLayerCounter;
35 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
36 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
37
38 static int gRecCounter;
39 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
40 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
41
42 static int gCanvasCounter;
43 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
44 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
45#else
46 #define inc_layer()
47 #define dec_layer()
48 #define inc_rec()
49 #define dec_rec()
50 #define inc_canvas()
51 #define dec_canvas()
52#endif
53
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000054typedef SkTLazy<SkPaint> SkLazyPaint;
55
reed@android.com8a1c16f2008-12-17 15:59:43 +000056///////////////////////////////////////////////////////////////////////////////
57// Helpers for computing fast bounds for quickReject tests
58
59static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
60 return paint != NULL && paint->isAntiAlias() ?
61 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
62}
63
64///////////////////////////////////////////////////////////////////////////////
65
66/* This is the record we keep for each SkDevice that the user installs.
67 The clip/matrix/proc are fields that reflect the top of the save/restore
68 stack. Whenever the canvas changes, it marks a dirty flag, and then before
69 these are used (assuming we're not on a layer) we rebuild these cache
70 values: they reflect the top of the save stack, but translated and clipped
71 by the device's XY offset and bitmap-bounds.
72*/
73struct DeviceCM {
74 DeviceCM* fNext;
75 SkDevice* fDevice;
76 SkRegion fClip;
77 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000078 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000079 // optional, related to canvas' external matrix
80 const SkMatrix* fMVMatrix;
81 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000082
83 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
84 : fNext(NULL) {
85 if (NULL != device) {
86 device->ref();
87 device->lockPixels();
88 }
reed@google.com4b226022011-01-11 18:32:13 +000089 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 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,
reed@google.com46799cd2011-02-22 20:56:26 +0000102 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000103 int x = fDevice->getOrigin().x();
104 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 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@google.com46799cd2011-02-22 20:56:26 +0000129 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130
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 +0000150private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000151 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152};
153
154/* This is the record we keep for each save/restore level in the stack.
155 Since a level optionally copies the matrix and/or stack, we have pointers
156 for these fields. If the value is copied for this level, the copy is
157 stored in the ...Storage field, and the pointer points to that. If the
158 value is not copied for this level, we ignore ...Storage, and just point
159 at the corresponding value in the previous level in the stack.
160*/
161class SkCanvas::MCRec {
162public:
163 MCRec* fNext;
164 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
165 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
166 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 DeviceCM* fLayer;
169 /* If there are any layers in the stack, this points to the top-most
170 one that is at or below this level in the stack (so we know what
171 bitmap/device to draw into from this level. This value is NOT
172 reference counted, since the real owner is either our fLayer field,
173 or a previous one in a lower level.)
174 */
175 DeviceCM* fTopLayer;
176
177 MCRec(const MCRec* prev, int flags) {
178 if (NULL != prev) {
179 if (flags & SkCanvas::kMatrix_SaveFlag) {
180 fMatrixStorage = *prev->fMatrix;
181 fMatrix = &fMatrixStorage;
182 } else {
183 fMatrix = prev->fMatrix;
184 }
reed@google.com4b226022011-01-11 18:32:13 +0000185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 if (flags & SkCanvas::kClip_SaveFlag) {
187 fRegionStorage = *prev->fRegion;
188 fRegion = &fRegionStorage;
189 } else {
190 fRegion = prev->fRegion;
191 }
192
193 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000194 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195
196 fTopLayer = prev->fTopLayer;
197 } else { // no prev
198 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000199
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 fMatrix = &fMatrixStorage;
201 fRegion = &fRegionStorage;
202 fFilter = NULL;
203 fTopLayer = NULL;
204 }
205 fLayer = NULL;
206
207 // don't bother initializing fNext
208 inc_rec();
209 }
210 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000211 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 SkDELETE(fLayer);
213 dec_rec();
214 }
reed@google.com4b226022011-01-11 18:32:13 +0000215
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216private:
217 SkMatrix fMatrixStorage;
218 SkRegion fRegionStorage;
219};
220
221class SkDrawIter : public SkDraw {
222public:
223 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
224 fCanvas = canvas;
225 canvas->updateDeviceCMCache();
226
reed@google.com7d7ca792011-02-23 22:39:18 +0000227 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 fBounder = canvas->getBounder();
229 fCurrLayer = canvas->fMCRec->fTopLayer;
230 fSkipEmptyClips = skipEmptyClips;
231 }
reed@google.com4b226022011-01-11 18:32:13 +0000232
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 bool next() {
234 // skip over recs with empty clips
235 if (fSkipEmptyClips) {
236 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
237 fCurrLayer = fCurrLayer->fNext;
238 }
239 }
240
241 if (NULL != fCurrLayer) {
242 const DeviceCM* rec = fCurrLayer;
243
244 fMatrix = rec->fMatrix;
245 fClip = &rec->fClip;
246 fDevice = rec->fDevice;
247 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000249 fMVMatrix = rec->fMVMatrix;
250 fExtMatrix = rec->fExtMatrix;
251 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252
253 fCurrLayer = rec->fNext;
254 if (fBounder) {
255 fBounder->setClip(fClip);
256 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000258
bsalomon@google.comd302f142011-03-03 13:54:13 +0000259 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 return true;
261 }
262 return false;
263 }
reed@google.com4b226022011-01-11 18:32:13 +0000264
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000266 int getX() const { return fDevice->getOrigin().x(); }
267 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 const SkMatrix& getMatrix() const { return *fMatrix; }
269 const SkRegion& getClip() const { return *fClip; }
270 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000271
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272private:
273 SkCanvas* fCanvas;
274 const DeviceCM* fCurrLayer;
275 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 SkBool8 fSkipEmptyClips;
277
278 typedef SkDraw INHERITED;
279};
280
281/////////////////////////////////////////////////////////////////////////////
282
283class AutoDrawLooper {
284public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000285 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
286 fCanvas = canvas;
287 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000289 fPaint = NULL;
290 fSaveCount = canvas->getSaveCount();
291 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293 if (fLooper) {
294 fLooper->init(canvas);
295 }
296 }
297
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000299 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000301
302 const SkPaint& paint() const {
303 SkASSERT(fPaint);
304 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000306
307 bool next(SkDrawFilter::Type drawType);
308
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000310 SkLazyPaint fLazyPaint;
311 SkCanvas* fCanvas;
312 const SkPaint& fOrigPaint;
313 SkDrawLooper* fLooper;
314 SkDrawFilter* fFilter;
315 const SkPaint* fPaint;
316 int fSaveCount;
317 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000318};
319
reed@google.com4e2b3d32011-04-07 14:18:59 +0000320bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
321 if (fDone) {
322 fPaint = NULL;
323 return false;
324 }
325 if (!fLooper && !fFilter) {
326 fDone = true;
327 fPaint = &fOrigPaint;
328 return true;
329 }
330
331 SkPaint* paint = fLazyPaint.set(fOrigPaint);
332 if (fLooper && !fLooper->next(fCanvas, paint)) {
333 fDone = true;
334 fPaint = NULL;
335 return false;
336 }
337 if (fFilter) {
338 fFilter->filter(paint, drawType);
mike@reedtribe.org53e3bed2011-04-08 00:37:03 +0000339 if (NULL == fLooper) {
340 // no looper means we only draw once
341 fDone = true;
342 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000343 }
344 fPaint = paint;
345 return true;
346}
347
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348/* Stack helper for managing a SkBounder. In the destructor, if we were
349 given a bounder, we call its commit() method, signifying that we are
350 done accumulating bounds for that draw.
351*/
352class SkAutoBounderCommit {
353public:
354 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
355 ~SkAutoBounderCommit() {
356 if (NULL != fBounder) {
357 fBounder->commit();
358 }
359 }
360private:
361 SkBounder* fBounder;
362};
363
364#include "SkColorPriv.h"
365
366class AutoValidator {
367public:
368 AutoValidator(SkDevice* device) : fDevice(device) {}
369 ~AutoValidator() {
370#ifdef SK_DEBUG
371 const SkBitmap& bm = fDevice->accessBitmap(false);
372 if (bm.config() == SkBitmap::kARGB_4444_Config) {
373 for (int y = 0; y < bm.height(); y++) {
374 const SkPMColor16* p = bm.getAddr16(0, y);
375 for (int x = 0; x < bm.width(); x++) {
376 SkPMColor16 c = p[x];
377 SkPMColor16Assert(c);
378 }
379 }
380 }
381#endif
382 }
383private:
384 SkDevice* fDevice;
385};
386
387////////// macros to place around the internal draw calls //////////////////
388
reed@google.com4e2b3d32011-04-07 14:18:59 +0000389#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000391 AutoDrawLooper looper(this, paint); \
392 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 SkAutoBounderCommit ac(fBounder); \
394 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000395
reed@google.com4e2b3d32011-04-07 14:18:59 +0000396#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397
398////////////////////////////////////////////////////////////////////////////
399
400SkDevice* SkCanvas::init(SkDevice* device) {
401 fBounder = NULL;
402 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000403 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000404 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000405 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406
407 fMCRec = (MCRec*)fMCStack.push_back();
408 new (fMCRec) MCRec(NULL, 0);
409
410 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
411 fMCRec->fTopLayer = fMCRec->fLayer;
412 fMCRec->fNext = NULL;
413
reed@android.comf2b98d62010-12-20 18:26:13 +0000414 fUseExternalMatrix = false;
415
reed@android.com8a1c16f2008-12-17 15:59:43 +0000416 return this->setDevice(device);
417}
418
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000419SkCanvas::SkCanvas(SkDeviceFactory* factory)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000420 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000421 inc_canvas();
422
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000423 if (factory) {
424 factory->ref();
425 } else {
426 factory = SkNEW(SkRasterDeviceFactory);
427 }
428 fDeviceFactory = factory;
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000429
430 this->init(NULL);
431}
432
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000434 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 inc_canvas();
436
bsalomon@google.come97f0852011-06-17 13:10:25 +0000437 fDeviceFactory = device->getDeviceFactory();
438 SkSafeRef(fDeviceFactory);
439
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440 this->init(device);
441}
442
443SkCanvas::SkCanvas(const SkBitmap& bitmap)
444 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
445 inc_canvas();
446
reed@google.comaf951c92011-06-16 19:10:39 +0000447 SkDevice* device = SkNEW_ARGS(SkDevice, (bitmap));
bsalomon@google.come97f0852011-06-17 13:10:25 +0000448 fDeviceFactory = device->getDeviceFactory();
449 SkSafeRef(fDeviceFactory);
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000450
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000451 this->init(device)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452}
453
454SkCanvas::~SkCanvas() {
455 // free up the contents of our deque
456 this->restoreToCount(1); // restore everything but the last
457 this->internalRestore(); // restore the last, since we're going away
458
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000459 SkSafeUnref(fBounder);
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000460 SkSafeUnref(fDeviceFactory);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000461
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462 dec_canvas();
463}
464
465SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
466 SkRefCnt_SafeAssign(fBounder, bounder);
467 return bounder;
468}
469
470SkDrawFilter* SkCanvas::getDrawFilter() const {
471 return fMCRec->fFilter;
472}
473
474SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
475 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
476 return filter;
477}
478
479///////////////////////////////////////////////////////////////////////////////
480
481SkDevice* SkCanvas::getDevice() const {
482 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000483 SkDeque::F2BIter iter(fMCStack);
484 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 SkASSERT(rec && rec->fLayer);
486 return rec->fLayer->fDevice;
487}
488
reed@google.com9266fed2011-03-30 00:18:03 +0000489SkDevice* SkCanvas::getTopDevice() const {
490 return fMCRec->fTopLayer->fDevice;
491}
492
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493SkDevice* SkCanvas::setDevice(SkDevice* device) {
494 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000495 SkDeque::F2BIter iter(fMCStack);
496 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 SkASSERT(rec && rec->fLayer);
498 SkDevice* rootDevice = rec->fLayer->fDevice;
499
500 if (rootDevice == device) {
501 return device;
502 }
reed@google.com4b226022011-01-11 18:32:13 +0000503
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 /* Notify the devices that they are going in/out of scope, so they can do
505 things like lock/unlock their pixels, etc.
506 */
507 if (device) {
508 device->lockPixels();
509 }
510 if (rootDevice) {
511 rootDevice->unlockPixels();
512 }
513
514 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
515 rootDevice = device;
516
517 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000518
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 /* Now we update our initial region to have the bounds of the new device,
520 and then intersect all of the clips in our stack with these bounds,
521 to ensure that we can't draw outside of the device's bounds (and trash
522 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 NOTE: this is only a partial-fix, since if the new device is larger than
525 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000526 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
528 reconstruct the correct clips, so this approximation will have to do.
529 The caller really needs to restore() back to the base if they want to
530 accurately take advantage of the new device bounds.
531 */
532
533 if (NULL == device) {
534 rec->fRegion->setEmpty();
535 while ((rec = (MCRec*)iter.next()) != NULL) {
536 (void)rec->fRegion->setEmpty();
537 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000538 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 } else {
540 // compute our total bounds for all devices
541 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000542
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 bounds.set(0, 0, device->width(), device->height());
544
545 // now jam our 1st clip to be bounds, and intersect the rest with that
546 rec->fRegion->setRect(bounds);
547 while ((rec = (MCRec*)iter.next()) != NULL) {
548 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
549 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000550 fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 }
552 return device;
553}
554
reed@google.comaf951c92011-06-16 19:10:39 +0000555SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
556 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000557 device->unref();
558 return device;
559}
560
reed@google.com51df9e32010-12-23 19:29:18 +0000561bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
562 SkDevice* device = this->getDevice();
563 if (!device) {
564 return false;
565 }
566 return device->readPixels(srcRect, bitmap);
567}
568
reed@google.com4b226022011-01-11 18:32:13 +0000569SkDeviceFactory* SkCanvas::setDeviceFactory(SkDeviceFactory* factory) {
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000570 SkRefCnt_SafeAssign(fDeviceFactory, factory);
reed@google.com9b2135a2011-01-11 19:45:38 +0000571 return factory;
reed@google.com4b226022011-01-11 18:32:13 +0000572}
573
574//////////////////////////////////////////////////////////////////////////////
575
reed@google.com51df9e32010-12-23 19:29:18 +0000576bool SkCanvas::readPixels(SkBitmap* bitmap) {
577 SkDevice* device = this->getDevice();
578 if (!device) {
579 return false;
580 }
581 SkIRect bounds;
582 bounds.set(0, 0, device->width(), device->height());
583 return this->readPixels(bounds, bitmap);
584}
585
586void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
587 SkDevice* device = this->getDevice();
588 if (device) {
589 device->writePixels(bitmap, x, y);
590 }
591}
592
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593//////////////////////////////////////////////////////////////////////////////
594
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595void SkCanvas::updateDeviceCMCache() {
596 if (fDeviceCMDirty) {
597 const SkMatrix& totalMatrix = this->getTotalMatrix();
598 const SkRegion& totalClip = this->getTotalClip();
599 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000602 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000603 if (fUseExternalMatrix) {
604 layer->updateExternalMatrix(fExternalMatrix,
605 fExternalInverse);
606 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 } else {
608 SkRegion clip;
609 clip = totalClip; // make a copy
610 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000611 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
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 } while ((layer = layer->fNext) != NULL);
617 }
618 fDeviceCMDirty = false;
619 }
620}
621
reed@android.comf2b98d62010-12-20 18:26:13 +0000622void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000623 const SkRegion& clip,
624 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000626 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000627 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000628 fLastDeviceToGainFocus = device;
629 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630}
631
632///////////////////////////////////////////////////////////////////////////////
633
634int SkCanvas::internalSave(SaveFlags flags) {
635 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000636
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 MCRec* newTop = (MCRec*)fMCStack.push_back();
638 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000639
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 newTop->fNext = fMCRec;
641 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000642
reed@google.com5c3d1472011-02-22 19:12:23 +0000643 fClipStack.save();
644 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 return saveCount;
647}
648
649int SkCanvas::save(SaveFlags flags) {
650 // call shared impl
651 return this->internalSave(flags);
652}
653
654#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
655#define C16MASK (1 << SkBitmap::kRGB_565_Config)
656#define C8MASK (1 << SkBitmap::kA8_Config)
657
658static SkBitmap::Config resolve_config(SkCanvas* canvas,
659 const SkIRect& bounds,
660 SkCanvas::SaveFlags flags,
661 bool* isOpaque) {
662 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
663
664#if 0
665 // loop through and union all the configs we may draw into
666 uint32_t configMask = 0;
667 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
668 {
669 SkDevice* device = canvas->getLayerDevice(i);
670 if (device->intersects(bounds))
671 configMask |= 1 << device->config();
672 }
673
674 // if the caller wants alpha or fullcolor, we can't return 565
675 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
676 SkCanvas::kHasAlphaLayer_SaveFlag))
677 configMask &= ~C16MASK;
678
679 switch (configMask) {
680 case C8MASK: // if we only have A8, return that
681 return SkBitmap::kA8_Config;
682
683 case C16MASK: // if we only have 565, return that
684 return SkBitmap::kRGB_565_Config;
685
686 default:
687 return SkBitmap::kARGB_8888_Config; // default answer
688 }
689#else
690 return SkBitmap::kARGB_8888_Config; // default answer
691#endif
692}
693
694static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
695 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
696}
697
698int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
699 SaveFlags flags) {
700 // do this before we create the layer. We don't call the public save() since
701 // that would invoke a possibly overridden virtual
702 int count = this->internalSave(flags);
703
704 fDeviceCMDirty = true;
705
706 SkIRect ir;
707 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000708 if (clipBounds.isEmpty()) {
709 return count;
710 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711
712 if (NULL != bounds) {
713 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 this->getTotalMatrix().mapRect(&r, *bounds);
716 r.roundOut(&ir);
717 // early exit if the layer's bounds are clipped out
718 if (!ir.intersect(clipBounds)) {
719 if (bounds_affects_clip(flags))
720 fMCRec->fRegion->setEmpty();
721 return count;
722 }
723 } else { // no user bounds, so just use the clip
724 ir = clipBounds;
725 }
726
reed@google.com5c3d1472011-02-22 19:12:23 +0000727 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 // early exit if the clip is now empty
729 if (bounds_affects_clip(flags) &&
730 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
731 return count;
732 }
733
734 bool isOpaque;
735 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
736
bsalomon@google.come97f0852011-06-17 13:10:25 +0000737 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
738 isOpaque);
739
reed@google.com6f8f2922011-03-04 22:27:10 +0000740 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
742 device->unref();
743
744 layer->fNext = fMCRec->fTopLayer;
745 fMCRec->fLayer = layer;
746 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
747
748 return count;
749}
750
751int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
752 SaveFlags flags) {
753 if (0xFF == alpha) {
754 return this->saveLayer(bounds, NULL, flags);
755 } else {
756 SkPaint tmpPaint;
757 tmpPaint.setAlpha(alpha);
758 return this->saveLayer(bounds, &tmpPaint, flags);
759 }
760}
761
762void SkCanvas::restore() {
763 // check for underflow
764 if (fMCStack.count() > 1) {
765 this->internalRestore();
766 }
767}
768
769void SkCanvas::internalRestore() {
770 SkASSERT(fMCStack.count() != 0);
771
772 fDeviceCMDirty = true;
773 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000774 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775
reed@google.com5c3d1472011-02-22 19:12:23 +0000776 fClipStack.restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 // 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) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000793 const SkIPoint& origin = layer->fDevice->getOrigin();
794 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 layer->fPaint);
796 // reset this, since drawDevice will have set it to true
797 fDeviceCMDirty = true;
798 }
799 SkDELETE(layer);
800 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000801
802 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803}
804
805int SkCanvas::getSaveCount() const {
806 return fMCStack.count();
807}
808
809void SkCanvas::restoreToCount(int count) {
810 // sanity check
811 if (count < 1) {
812 count = 1;
813 }
814 while (fMCStack.count() > count) {
815 this->restore();
816 }
817}
818
819/////////////////////////////////////////////////////////////////////////////
820
821// can't draw it if its empty, or its too big for a fixed-point width or height
822static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000823 return bitmap.width() <= 0 || bitmap.height() <= 0
824#ifndef SK_ALLOW_OVER_32K_BITMAPS
825 || bitmap.width() > 32767 || bitmap.height() > 32767
826#endif
827 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828}
829
reed@android.comf2b98d62010-12-20 18:26:13 +0000830void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 const SkMatrix& matrix, const SkPaint* paint) {
832 if (reject_bitmap(bitmap)) {
833 return;
834 }
835
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000836 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000837 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000838 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000840 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841}
842
843void SkCanvas::drawDevice(SkDevice* device, int x, int y,
844 const SkPaint* paint) {
845 SkPaint tmp;
846 if (NULL == paint) {
847 tmp.setDither(true);
848 paint = &tmp;
849 }
reed@google.com4b226022011-01-11 18:32:13 +0000850
reed@google.com4e2b3d32011-04-07 14:18:59 +0000851 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 while (iter.next()) {
853 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000854 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000856 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857}
858
859/////////////////////////////////////////////////////////////////////////////
860
861bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
862 fDeviceCMDirty = true;
863 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000864 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 return fMCRec->fMatrix->preTranslate(dx, dy);
866}
867
868bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
869 fDeviceCMDirty = true;
870 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000871 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 return fMCRec->fMatrix->preScale(sx, sy);
873}
874
875bool SkCanvas::rotate(SkScalar degrees) {
876 fDeviceCMDirty = true;
877 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000878 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 return fMCRec->fMatrix->preRotate(degrees);
880}
881
882bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
883 fDeviceCMDirty = true;
884 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000885 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 return fMCRec->fMatrix->preSkew(sx, sy);
887}
888
889bool SkCanvas::concat(const SkMatrix& matrix) {
890 fDeviceCMDirty = true;
891 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000892 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000893 return fMCRec->fMatrix->preConcat(matrix);
894}
895
896void SkCanvas::setMatrix(const SkMatrix& matrix) {
897 fDeviceCMDirty = true;
898 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000899 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 *fMCRec->fMatrix = matrix;
901}
902
903// this is not virtual, so it must call a virtual method so that subclasses
904// will see its action
905void SkCanvas::resetMatrix() {
906 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000907
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 matrix.reset();
909 this->setMatrix(matrix);
910}
911
912//////////////////////////////////////////////////////////////////////////////
913
914bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000915 AutoValidateClip avc(this);
916
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 fDeviceCMDirty = true;
918 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000919 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920
921 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000922 // for these simpler matrices, we can stay a rect ever after applying
923 // the matrix. This means we don't have to a) make a path, and b) tell
924 // the region code to scan-convert the path, only to discover that it
925 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 SkRect r;
927 SkIRect ir;
928
929 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000930 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 r.round(&ir);
932 return fMCRec->fRegion->op(ir, op);
933 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000934 // since we're rotate or some such thing, we convert the rect to a path
935 // and clip against that, since it can handle any matrix. However, to
936 // avoid recursion in the case where we are subclassed (e.g. Pictures)
937 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 SkPath path;
939
940 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000941 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942 }
943}
944
reed@google.com819c9212011-02-23 18:56:55 +0000945static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
946 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000947 // base is used to limit the size (and therefore memory allocation) of the
948 // region that results from scan converting devPath.
949 SkRegion base;
950
reed@google.com819c9212011-02-23 18:56:55 +0000951 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000952 // since we are intersect, we can do better (tighter) with currRgn's
953 // bounds, than just using the device. However, if currRgn is complex,
954 // our region blitter may hork, so we do that case in two steps.
955 if (currRgn->isRect()) {
956 return currRgn->setPath(devPath, *currRgn);
957 } else {
958 base.setRect(currRgn->getBounds());
959 SkRegion rgn;
960 rgn.setPath(devPath, base);
961 return currRgn->op(rgn, op);
962 }
reed@google.com819c9212011-02-23 18:56:55 +0000963 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000964 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
965 base.setRect(0, 0, bm.width(), bm.height());
966
967 if (SkRegion::kReplace_Op == op) {
968 return currRgn->setPath(devPath, base);
969 } else {
970 SkRegion rgn;
971 rgn.setPath(devPath, base);
972 return currRgn->op(rgn, op);
973 }
974 }
975}
976
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000978 AutoValidateClip avc(this);
979
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 fDeviceCMDirty = true;
981 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000982 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983
984 SkPath devPath;
985 path.transform(*fMCRec->fMatrix, &devPath);
986
reed@google.com5c3d1472011-02-22 19:12:23 +0000987 // if we called path.swap() we could avoid a deep copy of this path
988 fClipStack.clipDevPath(devPath, op);
989
reed@google.com819c9212011-02-23 18:56:55 +0000990 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991}
992
993bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000994 AutoValidateClip avc(this);
995
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996 fDeviceCMDirty = true;
997 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000998 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999
reed@google.com5c3d1472011-02-22 19:12:23 +00001000 // todo: signal fClipStack that we have a region, and therefore (I guess)
1001 // we have to ignore it, and use the region directly?
1002 fClipStack.clipDevRect(rgn.getBounds());
1003
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 return fMCRec->fRegion->op(rgn, op);
1005}
1006
reed@google.com819c9212011-02-23 18:56:55 +00001007#ifdef SK_DEBUG
1008void SkCanvas::validateClip() const {
1009 // construct clipRgn from the clipstack
1010 const SkDevice* device = this->getDevice();
1011 SkIRect ir;
1012 ir.set(0, 0, device->width(), device->height());
1013 SkRegion clipRgn(ir);
1014
1015 SkClipStack::B2FIter iter(fClipStack);
1016 const SkClipStack::B2FIter::Clip* clip;
1017 while ((clip = iter.next()) != NULL) {
1018 if (clip->fPath) {
1019 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1020 } else if (clip->fRect) {
1021 clip->fRect->round(&ir);
1022 clipRgn.op(ir, clip->fOp);
1023 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001024 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001025 }
1026 }
1027
reed@google.com6f8f2922011-03-04 22:27:10 +00001028#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001029 // now compare against the current rgn
1030 const SkRegion& rgn = this->getTotalClip();
1031 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001032#endif
reed@google.com819c9212011-02-23 18:56:55 +00001033}
1034#endif
1035
reed@google.com5c3d1472011-02-22 19:12:23 +00001036///////////////////////////////////////////////////////////////////////////////
1037
reed@android.comba09de42010-02-05 20:46:05 +00001038void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001040 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1041 fLocalBoundsCompareTypeBW;
1042
1043 if (!this->getClipBounds(&r, et)) {
1044 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001045 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001046 rCompare.set(SkScalarToCompareType(r.fLeft),
1047 SkScalarToCompareType(r.fTop),
1048 SkScalarToCompareType(r.fRight),
1049 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 }
1051}
1052
reed@android.comd252db02009-04-01 18:31:44 +00001053/* current impl ignores edgetype, and relies on
1054 getLocalClipBoundsCompareType(), which always returns a value assuming
1055 antialiasing (worst case)
1056 */
reed@android.comba09de42010-02-05 20:46:05 +00001057bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001058
1059 if (!rect.hasValidCoordinates())
1060 return true;
1061
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 if (fMCRec->fRegion->isEmpty()) {
1063 return true;
1064 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065
tomhudson@google.com8d430182011-06-06 19:11:19 +00001066 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001067 SkRect dst;
1068 fMCRec->fMatrix->mapRect(&dst, rect);
1069 SkIRect idst;
1070 dst.roundOut(&idst);
1071 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1072 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001073 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001074
reed@android.coma380ae42009-07-21 01:17:02 +00001075 // for speed, do the most likely reject compares first
1076 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1077 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1078 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1079 return true;
1080 }
1081 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1082 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1083 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1084 return true;
1085 }
1086 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088}
1089
1090bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001091 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092}
1093
1094bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1095 /* current impl ignores edgetype, and relies on
1096 getLocalClipBoundsCompareType(), which always returns a value assuming
1097 antialiasing (worst case)
1098 */
1099
1100 if (fMCRec->fRegion->isEmpty()) {
1101 return true;
1102 }
reed@google.com4b226022011-01-11 18:32:13 +00001103
reed@android.comaefd2bc2009-03-30 21:02:14 +00001104 SkScalarCompareType userT = SkScalarToCompareType(top);
1105 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001106
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001108 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 if (userT >= userB) {
1110 return true;
1111 }
reed@google.com4b226022011-01-11 18:32:13 +00001112
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113 // check if we are above or below the local clip bounds
1114 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1115 return userT >= clipR.fBottom || userB <= clipR.fTop;
1116}
1117
1118bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1119 const SkRegion& clip = *fMCRec->fRegion;
1120 if (clip.isEmpty()) {
1121 if (bounds) {
1122 bounds->setEmpty();
1123 }
1124 return false;
1125 }
1126
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001127 SkMatrix inverse;
1128 // if we can't invert the CTM, we can't return local clip bounds
1129 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001130 if (bounds) {
1131 bounds->setEmpty();
1132 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001133 return false;
1134 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001136 if (NULL != bounds) {
1137 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 // get the clip's bounds
1139 const SkIRect& ibounds = clip.getBounds();
1140 // adjust it outwards if we are antialiasing
1141 int inset = (kAA_EdgeType == et);
1142 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1143 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001144
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145 // invert into local coordinates
1146 inverse.mapRect(bounds, r);
1147 }
1148 return true;
1149}
1150
1151const SkMatrix& SkCanvas::getTotalMatrix() const {
1152 return *fMCRec->fMatrix;
1153}
1154
1155const SkRegion& SkCanvas::getTotalClip() const {
1156 return *fMCRec->fRegion;
1157}
1158
reed@google.com7d7ca792011-02-23 22:39:18 +00001159const SkClipStack& SkCanvas::getTotalClipStack() const {
1160 return fClipStack;
1161}
1162
reed@android.comf2b98d62010-12-20 18:26:13 +00001163void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1164 if (NULL == matrix || matrix->isIdentity()) {
1165 if (fUseExternalMatrix) {
1166 fDeviceCMDirty = true;
1167 }
1168 fUseExternalMatrix = false;
1169 } else {
1170 fUseExternalMatrix = true;
1171 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001172
reed@android.comf2b98d62010-12-20 18:26:13 +00001173 fExternalMatrix = *matrix;
1174 matrix->invert(&fExternalInverse);
1175 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001176}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177
bsalomon@google.come97f0852011-06-17 13:10:25 +00001178SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1179 int width, int height,
1180 bool isOpaque) {
1181 if (fDeviceFactory) {
1182 return fDeviceFactory->newDevice(this, config, width, height,
1183 isOpaque, true);
1184 } else {
1185 return this->getDevice()->createCompatibleDeviceForSaveLayer(
1186 config, width, height,
1187 isOpaque);
1188 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189}
1190
bsalomon@google.come97f0852011-06-17 13:10:25 +00001191SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1192 int width, int height,
1193 bool isOpaque) {
1194 SkDevice* device = this->getDevice();
1195 if (device) {
1196 return device->createCompatibleDevice(config, width, height,
1197 isOpaque);
1198 } else {
1199 return NULL;
1200 }
1201}
1202
1203
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204//////////////////////////////////////////////////////////////////////////////
1205// These are the virtual drawing methods
1206//////////////////////////////////////////////////////////////////////////////
1207
reed@google.com2a981812011-04-14 18:59:28 +00001208void SkCanvas::clear(SkColor color) {
1209 SkDrawIter iter(this);
1210
1211 while (iter.next()) {
1212 iter.fDevice->clear(color);
1213 }
1214}
1215
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216void SkCanvas::drawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001217 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218
1219 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001220 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 }
1222
reed@google.com4e2b3d32011-04-07 14:18:59 +00001223 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224}
1225
1226void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1227 const SkPaint& paint) {
1228 if ((long)count <= 0) {
1229 return;
1230 }
1231
1232 SkASSERT(pts != NULL);
1233
reed@google.com4e2b3d32011-04-07 14:18:59 +00001234 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001235
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001237 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238 }
reed@google.com4b226022011-01-11 18:32:13 +00001239
reed@google.com4e2b3d32011-04-07 14:18:59 +00001240 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241}
1242
1243void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1244 if (paint.canComputeFastBounds()) {
1245 SkRect storage;
1246 if (this->quickReject(paint.computeFastBounds(r, &storage),
1247 paint2EdgeType(&paint))) {
1248 return;
1249 }
1250 }
reed@google.com4b226022011-01-11 18:32:13 +00001251
reed@google.com4e2b3d32011-04-07 14:18:59 +00001252 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253
1254 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001255 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 }
1257
reed@google.com4e2b3d32011-04-07 14:18:59 +00001258 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259}
1260
1261void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1262 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001263 SkRect storage;
1264 const SkRect& bounds = path.getBounds();
1265 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 paint2EdgeType(&paint))) {
1267 return;
1268 }
1269 }
1270
reed@google.com4e2b3d32011-04-07 14:18:59 +00001271 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272
1273 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001274 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275 }
1276
reed@google.com4e2b3d32011-04-07 14:18:59 +00001277 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278}
1279
1280void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1281 const SkPaint* paint) {
1282 SkDEBUGCODE(bitmap.validate();)
1283
1284 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1285 SkRect fastBounds;
1286 fastBounds.set(x, y,
1287 x + SkIntToScalar(bitmap.width()),
1288 y + SkIntToScalar(bitmap.height()));
1289 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1290 return;
1291 }
1292 }
reed@google.com4b226022011-01-11 18:32:13 +00001293
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 SkMatrix matrix;
1295 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001296 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297}
1298
1299void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1300 const SkRect& dst, const SkPaint* paint) {
1301 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1302 return;
1303 }
reed@google.com4b226022011-01-11 18:32:13 +00001304
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 // do this now, to avoid the cost of calling extract for RLE bitmaps
1306 if (this->quickReject(dst, paint2EdgeType(paint))) {
1307 return;
1308 }
reed@google.com4b226022011-01-11 18:32:13 +00001309
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001311
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001313 SkRect tmpSrc;
1314 if (src) {
1315 tmpSrc.set(*src);
1316 // if the extract process clipped off the top or left of the
1317 // original, we adjust for that here to get the position right.
1318 if (tmpSrc.fLeft > 0) {
1319 tmpSrc.fRight -= tmpSrc.fLeft;
1320 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001321 }
reed@android.com87899992009-10-16 14:48:38 +00001322 if (tmpSrc.fTop > 0) {
1323 tmpSrc.fBottom -= tmpSrc.fTop;
1324 tmpSrc.fTop = 0;
1325 }
1326 } else {
1327 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1328 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 }
reed@android.com87899992009-10-16 14:48:38 +00001330 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001331
1332 // ensure that src is "valid" before we pass it to our internal routines
1333 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1334 SkIRect tmpISrc;
1335 if (src) {
1336 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001337 if (!tmpISrc.intersect(*src)) {
1338 return;
1339 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001340 src = &tmpISrc;
1341 }
1342 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343}
1344
1345void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1346 const SkPaint* paint) {
1347 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001348 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349}
1350
reed@android.comf2b98d62010-12-20 18:26:13 +00001351void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1352 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001354
reed@google.com4e2b3d32011-04-07 14:18:59 +00001355 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001356
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001358 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359 }
reed@android.com9b039062009-02-11 15:09:58 +00001360
reed@google.com4e2b3d32011-04-07 14:18:59 +00001361 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362}
1363
1364void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1365 const SkPaint* paint) {
1366 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001367
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 if (reject_bitmap(bitmap)) {
1369 return;
1370 }
reed@google.com4b226022011-01-11 18:32:13 +00001371
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 SkPaint tmp;
1373 if (NULL == paint) {
1374 paint = &tmp;
1375 }
reed@google.com4b226022011-01-11 18:32:13 +00001376
reed@google.com4e2b3d32011-04-07 14:18:59 +00001377 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001378
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379 while (iter.next()) {
1380 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001381 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001383 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384}
1385
reed@google.comf67e4cf2011-03-15 20:56:58 +00001386class SkDeviceFilteredPaint {
1387public:
1388 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1389 SkDevice::TextFlags flags;
1390 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001391 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001392 newPaint->setFlags(flags.fFlags);
1393 newPaint->setHinting(flags.fHinting);
1394 fPaint = newPaint;
1395 } else {
1396 fPaint = &paint;
1397 }
1398 }
1399
reed@google.comf67e4cf2011-03-15 20:56:58 +00001400 const SkPaint& paint() const { return *fPaint; }
1401
1402private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001403 const SkPaint* fPaint;
1404 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001405};
1406
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407void SkCanvas::drawText(const void* text, size_t byteLength,
1408 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001409 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410
1411 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001412 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001413 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 }
1415
reed@google.com4e2b3d32011-04-07 14:18:59 +00001416 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417}
1418
1419void SkCanvas::drawPosText(const void* text, size_t byteLength,
1420 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001421 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001422
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001424 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001426 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 }
reed@google.com4b226022011-01-11 18:32:13 +00001428
reed@google.com4e2b3d32011-04-07 14:18:59 +00001429 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430}
1431
1432void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1433 const SkScalar xpos[], SkScalar constY,
1434 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001435 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001436
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001438 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001440 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441 }
reed@google.com4b226022011-01-11 18:32:13 +00001442
reed@google.com4e2b3d32011-04-07 14:18:59 +00001443 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444}
1445
1446void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1447 const SkPath& path, const SkMatrix* matrix,
1448 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001449 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450
1451 while (iter.next()) {
1452 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001453 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 }
1455
reed@google.com4e2b3d32011-04-07 14:18:59 +00001456 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457}
1458
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001459#ifdef ANDROID
1460void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1461 const SkPoint pos[], const SkPaint& paint,
1462 const SkPath& path, const SkMatrix* matrix) {
1463
reed@google.com4e2b3d32011-04-07 14:18:59 +00001464 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001465
1466 while (iter.next()) {
1467 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001468 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001469 }
1470
reed@google.com4e2b3d32011-04-07 14:18:59 +00001471 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001472}
1473#endif
1474
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1476 const SkPoint verts[], const SkPoint texs[],
1477 const SkColor colors[], SkXfermode* xmode,
1478 const uint16_t indices[], int indexCount,
1479 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001480 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001481
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482 while (iter.next()) {
1483 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001484 colors, xmode, indices, indexCount,
1485 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486 }
reed@google.com4b226022011-01-11 18:32:13 +00001487
reed@google.com4e2b3d32011-04-07 14:18:59 +00001488 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489}
1490
reed@android.comcb608442009-12-04 21:32:27 +00001491void SkCanvas::drawData(const void* data, size_t length) {
1492 // do nothing. Subclasses may do something with the data
1493}
1494
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495//////////////////////////////////////////////////////////////////////////////
1496// These methods are NOT virtual, and therefore must call back into virtual
1497// methods, rather than actually drawing themselves.
1498//////////////////////////////////////////////////////////////////////////////
1499
1500void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001501 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502 SkPaint paint;
1503
1504 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001505 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001506 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507 }
1508 this->drawPaint(paint);
1509}
1510
reed@android.com845fdac2009-06-23 03:01:32 +00001511void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512 SkPaint paint;
1513
1514 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001515 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001516 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517 }
1518 this->drawPaint(paint);
1519}
1520
1521void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1522 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001523
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 pt.set(x, y);
1525 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1526}
1527
1528void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1529 SkPoint pt;
1530 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001531
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532 pt.set(x, y);
1533 paint.setColor(color);
1534 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1535}
1536
1537void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1538 const SkPaint& paint) {
1539 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001540
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541 pts[0].set(x0, y0);
1542 pts[1].set(x1, y1);
1543 this->drawPoints(kLines_PointMode, 2, pts, paint);
1544}
1545
1546void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1547 SkScalar right, SkScalar bottom,
1548 const SkPaint& paint) {
1549 SkRect r;
1550
1551 r.set(left, top, right, bottom);
1552 this->drawRect(r, paint);
1553}
1554
1555void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1556 const SkPaint& paint) {
1557 if (radius < 0) {
1558 radius = 0;
1559 }
1560
1561 SkRect r;
1562 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001563
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564 if (paint.canComputeFastBounds()) {
1565 SkRect storage;
1566 if (this->quickReject(paint.computeFastBounds(r, &storage),
1567 paint2EdgeType(&paint))) {
1568 return;
1569 }
1570 }
reed@google.com4b226022011-01-11 18:32:13 +00001571
reed@android.com8a1c16f2008-12-17 15:59:43 +00001572 SkPath path;
1573 path.addOval(r);
1574 this->drawPath(path, paint);
1575}
1576
1577void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1578 const SkPaint& paint) {
1579 if (rx > 0 && ry > 0) {
1580 if (paint.canComputeFastBounds()) {
1581 SkRect storage;
1582 if (this->quickReject(paint.computeFastBounds(r, &storage),
1583 paint2EdgeType(&paint))) {
1584 return;
1585 }
1586 }
1587
1588 SkPath path;
1589 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1590 this->drawPath(path, paint);
1591 } else {
1592 this->drawRect(r, paint);
1593 }
1594}
1595
1596void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1597 if (paint.canComputeFastBounds()) {
1598 SkRect storage;
1599 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1600 paint2EdgeType(&paint))) {
1601 return;
1602 }
1603 }
1604
1605 SkPath path;
1606 path.addOval(oval);
1607 this->drawPath(path, paint);
1608}
1609
1610void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1611 SkScalar sweepAngle, bool useCenter,
1612 const SkPaint& paint) {
1613 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1614 this->drawOval(oval, paint);
1615 } else {
1616 SkPath path;
1617 if (useCenter) {
1618 path.moveTo(oval.centerX(), oval.centerY());
1619 }
1620 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1621 if (useCenter) {
1622 path.close();
1623 }
1624 this->drawPath(path, paint);
1625 }
1626}
1627
1628void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1629 const SkPath& path, SkScalar hOffset,
1630 SkScalar vOffset, const SkPaint& paint) {
1631 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001632
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633 matrix.setTranslate(hOffset, vOffset);
1634 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1635}
1636
reed@android.comf76bacf2009-05-13 14:00:33 +00001637///////////////////////////////////////////////////////////////////////////////
1638
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639void SkCanvas::drawPicture(SkPicture& picture) {
1640 int saveCount = save();
1641 picture.draw(this);
1642 restoreToCount(saveCount);
1643}
1644
reed@android.comf76bacf2009-05-13 14:00:33 +00001645void SkCanvas::drawShape(SkShape* shape) {
1646 // shape baseclass takes care of save/restore
1647 shape->draw(this);
1648}
1649
reed@android.com8a1c16f2008-12-17 15:59:43 +00001650///////////////////////////////////////////////////////////////////////////////
1651///////////////////////////////////////////////////////////////////////////////
1652
1653SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001654 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655
1656 SkASSERT(canvas);
1657
1658 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1659 fDone = !fImpl->next();
1660}
1661
1662SkCanvas::LayerIter::~LayerIter() {
1663 fImpl->~SkDrawIter();
1664}
1665
1666void SkCanvas::LayerIter::next() {
1667 fDone = !fImpl->next();
1668}
1669
1670SkDevice* SkCanvas::LayerIter::device() const {
1671 return fImpl->getDevice();
1672}
1673
1674const SkMatrix& SkCanvas::LayerIter::matrix() const {
1675 return fImpl->getMatrix();
1676}
1677
1678const SkPaint& SkCanvas::LayerIter::paint() const {
1679 const SkPaint* paint = fImpl->getPaint();
1680 if (NULL == paint) {
1681 paint = &fDefaultPaint;
1682 }
1683 return *paint;
1684}
1685
1686const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1687int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1688int SkCanvas::LayerIter::y() const { return fImpl->getY(); }