blob: 16cfc4bcb6acf506b857081ba77d1075ebda3917 [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)
420 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
421 fDeviceFactory(factory) {
422 inc_canvas();
423
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000424 if (!factory)
425 fDeviceFactory = SkNEW(SkRasterDeviceFactory);
426
427 this->init(NULL);
428}
429
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430SkCanvas::SkCanvas(SkDevice* device)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000431 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
432 fDeviceFactory(device->getDeviceFactory()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 inc_canvas();
434
435 this->init(device);
436}
437
438SkCanvas::SkCanvas(const SkBitmap& bitmap)
439 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
440 inc_canvas();
441
reed@android.comf2b98d62010-12-20 18:26:13 +0000442 SkDevice* device = SkNEW_ARGS(SkDevice, (this, bitmap, false));
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000443 fDeviceFactory = device->getDeviceFactory();
444 this->init(device)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445}
446
447SkCanvas::~SkCanvas() {
448 // free up the contents of our deque
449 this->restoreToCount(1); // restore everything but the last
450 this->internalRestore(); // restore the last, since we're going away
451
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000452 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000453 SkDELETE(fDeviceFactory);
454
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 dec_canvas();
456}
457
458SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
459 SkRefCnt_SafeAssign(fBounder, bounder);
460 return bounder;
461}
462
463SkDrawFilter* SkCanvas::getDrawFilter() const {
464 return fMCRec->fFilter;
465}
466
467SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
468 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
469 return filter;
470}
471
472///////////////////////////////////////////////////////////////////////////////
473
474SkDevice* SkCanvas::getDevice() const {
475 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000476 SkDeque::F2BIter iter(fMCStack);
477 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 SkASSERT(rec && rec->fLayer);
479 return rec->fLayer->fDevice;
480}
481
reed@google.com9266fed2011-03-30 00:18:03 +0000482SkDevice* SkCanvas::getTopDevice() const {
483 return fMCRec->fTopLayer->fDevice;
484}
485
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486SkDevice* SkCanvas::setDevice(SkDevice* device) {
487 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000488 SkDeque::F2BIter iter(fMCStack);
489 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 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000531 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 } else {
533 // compute our total bounds for all devices
534 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 bounds.set(0, 0, device->width(), device->height());
537
538 // now jam our 1st clip to be bounds, and intersect the rest with that
539 rec->fRegion->setRect(bounds);
540 while ((rec = (MCRec*)iter.next()) != NULL) {
541 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
542 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000543 fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 }
545 return device;
546}
547
reed@android.comf2b98d62010-12-20 18:26:13 +0000548SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap, bool forLayer) {
549 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (this, bitmap, forLayer)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 device->unref();
551 return device;
552}
553
reed@google.com51df9e32010-12-23 19:29:18 +0000554bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
555 SkDevice* device = this->getDevice();
556 if (!device) {
557 return false;
558 }
559 return device->readPixels(srcRect, bitmap);
560}
561
reed@google.com4b226022011-01-11 18:32:13 +0000562SkDeviceFactory* SkCanvas::setDeviceFactory(SkDeviceFactory* factory) {
563 SkDELETE(fDeviceFactory);
564 fDeviceFactory = factory;
reed@google.com9b2135a2011-01-11 19:45:38 +0000565 return factory;
reed@google.com4b226022011-01-11 18:32:13 +0000566}
567
568//////////////////////////////////////////////////////////////////////////////
569
reed@google.com51df9e32010-12-23 19:29:18 +0000570bool SkCanvas::readPixels(SkBitmap* bitmap) {
571 SkDevice* device = this->getDevice();
572 if (!device) {
573 return false;
574 }
575 SkIRect bounds;
576 bounds.set(0, 0, device->width(), device->height());
577 return this->readPixels(bounds, bitmap);
578}
579
580void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
581 SkDevice* device = this->getDevice();
582 if (device) {
583 device->writePixels(bitmap, x, y);
584 }
585}
586
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587//////////////////////////////////////////////////////////////////////////////
588
589bool SkCanvas::getViewport(SkIPoint* size) const {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000590 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000591 return false;
592 if (size)
593 size->set(getDevice()->width(), getDevice()->height());
594 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595}
596
597bool SkCanvas::setViewport(int width, int height) {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000598 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000599 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000600
601 this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height,
602 false, false))->unref();
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000603 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604}
605
606void SkCanvas::updateDeviceCMCache() {
607 if (fDeviceCMDirty) {
608 const SkMatrix& totalMatrix = this->getTotalMatrix();
609 const SkRegion& totalClip = this->getTotalClip();
610 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000613 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000614 if (fUseExternalMatrix) {
615 layer->updateExternalMatrix(fExternalMatrix,
616 fExternalInverse);
617 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 } else {
619 SkRegion clip;
620 clip = totalClip; // make a copy
621 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000622 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000623 if (fUseExternalMatrix) {
624 layer->updateExternalMatrix(fExternalMatrix,
625 fExternalInverse);
626 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 } while ((layer = layer->fNext) != NULL);
628 }
629 fDeviceCMDirty = false;
630 }
631}
632
reed@android.comf2b98d62010-12-20 18:26:13 +0000633void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000634 const SkRegion& clip,
635 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000637 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000638 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000639 fLastDeviceToGainFocus = device;
640 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000641}
642
643///////////////////////////////////////////////////////////////////////////////
644
645int SkCanvas::internalSave(SaveFlags flags) {
646 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000647
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 MCRec* newTop = (MCRec*)fMCStack.push_back();
649 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 newTop->fNext = fMCRec;
652 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000653
reed@google.com5c3d1472011-02-22 19:12:23 +0000654 fClipStack.save();
655 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
656
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 return saveCount;
658}
659
660int SkCanvas::save(SaveFlags flags) {
661 // call shared impl
662 return this->internalSave(flags);
663}
664
665#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
666#define C16MASK (1 << SkBitmap::kRGB_565_Config)
667#define C8MASK (1 << SkBitmap::kA8_Config)
668
669static SkBitmap::Config resolve_config(SkCanvas* canvas,
670 const SkIRect& bounds,
671 SkCanvas::SaveFlags flags,
672 bool* isOpaque) {
673 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
674
675#if 0
676 // loop through and union all the configs we may draw into
677 uint32_t configMask = 0;
678 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
679 {
680 SkDevice* device = canvas->getLayerDevice(i);
681 if (device->intersects(bounds))
682 configMask |= 1 << device->config();
683 }
684
685 // if the caller wants alpha or fullcolor, we can't return 565
686 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
687 SkCanvas::kHasAlphaLayer_SaveFlag))
688 configMask &= ~C16MASK;
689
690 switch (configMask) {
691 case C8MASK: // if we only have A8, return that
692 return SkBitmap::kA8_Config;
693
694 case C16MASK: // if we only have 565, return that
695 return SkBitmap::kRGB_565_Config;
696
697 default:
698 return SkBitmap::kARGB_8888_Config; // default answer
699 }
700#else
701 return SkBitmap::kARGB_8888_Config; // default answer
702#endif
703}
704
705static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
706 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
707}
708
709int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
710 SaveFlags flags) {
711 // do this before we create the layer. We don't call the public save() since
712 // that would invoke a possibly overridden virtual
713 int count = this->internalSave(flags);
714
715 fDeviceCMDirty = true;
716
717 SkIRect ir;
718 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000719 if (clipBounds.isEmpty()) {
720 return count;
721 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722
723 if (NULL != bounds) {
724 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000725
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 this->getTotalMatrix().mapRect(&r, *bounds);
727 r.roundOut(&ir);
728 // early exit if the layer's bounds are clipped out
729 if (!ir.intersect(clipBounds)) {
730 if (bounds_affects_clip(flags))
731 fMCRec->fRegion->setEmpty();
732 return count;
733 }
734 } else { // no user bounds, so just use the clip
735 ir = clipBounds;
736 }
737
reed@google.com5c3d1472011-02-22 19:12:23 +0000738 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 // early exit if the clip is now empty
740 if (bounds_affects_clip(flags) &&
741 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
742 return count;
743 }
744
745 bool isOpaque;
746 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
747
748 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
749 isOpaque, true);
reed@google.com6f8f2922011-03-04 22:27:10 +0000750 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
752 device->unref();
753
754 layer->fNext = fMCRec->fTopLayer;
755 fMCRec->fLayer = layer;
756 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
757
758 return count;
759}
760
761int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
762 SaveFlags flags) {
763 if (0xFF == alpha) {
764 return this->saveLayer(bounds, NULL, flags);
765 } else {
766 SkPaint tmpPaint;
767 tmpPaint.setAlpha(alpha);
768 return this->saveLayer(bounds, &tmpPaint, flags);
769 }
770}
771
772void SkCanvas::restore() {
773 // check for underflow
774 if (fMCStack.count() > 1) {
775 this->internalRestore();
776 }
777}
778
779void SkCanvas::internalRestore() {
780 SkASSERT(fMCStack.count() != 0);
781
782 fDeviceCMDirty = true;
783 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000784 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785
reed@google.com5c3d1472011-02-22 19:12:23 +0000786 fClipStack.restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 // reserve our layer (if any)
788 DeviceCM* layer = fMCRec->fLayer; // may be null
789 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
790 fMCRec->fLayer = NULL;
791
792 // now do the normal restore()
793 fMCRec->~MCRec(); // balanced in save()
794 fMCStack.pop_back();
795 fMCRec = (MCRec*)fMCStack.back();
796
797 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
798 since if we're being recorded, we don't want to record this (the
799 recorder will have already recorded the restore).
800 */
801 if (NULL != layer) {
802 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000803 const SkIPoint& origin = layer->fDevice->getOrigin();
804 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 layer->fPaint);
806 // reset this, since drawDevice will have set it to true
807 fDeviceCMDirty = true;
808 }
809 SkDELETE(layer);
810 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000811
812 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813}
814
815int SkCanvas::getSaveCount() const {
816 return fMCStack.count();
817}
818
819void SkCanvas::restoreToCount(int count) {
820 // sanity check
821 if (count < 1) {
822 count = 1;
823 }
824 while (fMCStack.count() > count) {
825 this->restore();
826 }
827}
828
829/////////////////////////////////////////////////////////////////////////////
830
831// can't draw it if its empty, or its too big for a fixed-point width or height
832static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000833 return bitmap.width() <= 0 || bitmap.height() <= 0
834#ifndef SK_ALLOW_OVER_32K_BITMAPS
835 || bitmap.width() > 32767 || bitmap.height() > 32767
836#endif
837 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838}
839
reed@android.comf2b98d62010-12-20 18:26:13 +0000840void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 const SkMatrix& matrix, const SkPaint* paint) {
842 if (reject_bitmap(bitmap)) {
843 return;
844 }
845
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000846 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000848 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000850 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851}
852
853void SkCanvas::drawDevice(SkDevice* device, int x, int y,
854 const SkPaint* paint) {
855 SkPaint tmp;
856 if (NULL == paint) {
857 tmp.setDither(true);
858 paint = &tmp;
859 }
reed@google.com4b226022011-01-11 18:32:13 +0000860
reed@google.com4e2b3d32011-04-07 14:18:59 +0000861 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 while (iter.next()) {
863 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000864 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000866 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867}
868
869/////////////////////////////////////////////////////////////////////////////
870
871bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
872 fDeviceCMDirty = true;
873 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000874 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 return fMCRec->fMatrix->preTranslate(dx, dy);
876}
877
878bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
879 fDeviceCMDirty = true;
880 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000881 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 return fMCRec->fMatrix->preScale(sx, sy);
883}
884
885bool SkCanvas::rotate(SkScalar degrees) {
886 fDeviceCMDirty = true;
887 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000888 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000889 return fMCRec->fMatrix->preRotate(degrees);
890}
891
892bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
893 fDeviceCMDirty = true;
894 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000895 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 return fMCRec->fMatrix->preSkew(sx, sy);
897}
898
899bool SkCanvas::concat(const SkMatrix& matrix) {
900 fDeviceCMDirty = true;
901 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000902 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 return fMCRec->fMatrix->preConcat(matrix);
904}
905
906void SkCanvas::setMatrix(const SkMatrix& matrix) {
907 fDeviceCMDirty = true;
908 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000909 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 *fMCRec->fMatrix = matrix;
911}
912
913// this is not virtual, so it must call a virtual method so that subclasses
914// will see its action
915void SkCanvas::resetMatrix() {
916 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000917
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 matrix.reset();
919 this->setMatrix(matrix);
920}
921
922//////////////////////////////////////////////////////////////////////////////
923
924bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000925 AutoValidateClip avc(this);
926
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 fDeviceCMDirty = true;
928 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000929 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930
931 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000932 // for these simpler matrices, we can stay a rect ever after applying
933 // the matrix. This means we don't have to a) make a path, and b) tell
934 // the region code to scan-convert the path, only to discover that it
935 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 SkRect r;
937 SkIRect ir;
938
939 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000940 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 r.round(&ir);
942 return fMCRec->fRegion->op(ir, op);
943 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000944 // since we're rotate or some such thing, we convert the rect to a path
945 // and clip against that, since it can handle any matrix. However, to
946 // avoid recursion in the case where we are subclassed (e.g. Pictures)
947 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 SkPath path;
949
950 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000951 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 }
953}
954
reed@google.com819c9212011-02-23 18:56:55 +0000955static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
956 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000957 // base is used to limit the size (and therefore memory allocation) of the
958 // region that results from scan converting devPath.
959 SkRegion base;
960
reed@google.com819c9212011-02-23 18:56:55 +0000961 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000962 // since we are intersect, we can do better (tighter) with currRgn's
963 // bounds, than just using the device. However, if currRgn is complex,
964 // our region blitter may hork, so we do that case in two steps.
965 if (currRgn->isRect()) {
966 return currRgn->setPath(devPath, *currRgn);
967 } else {
968 base.setRect(currRgn->getBounds());
969 SkRegion rgn;
970 rgn.setPath(devPath, base);
971 return currRgn->op(rgn, op);
972 }
reed@google.com819c9212011-02-23 18:56:55 +0000973 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000974 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
975 base.setRect(0, 0, bm.width(), bm.height());
976
977 if (SkRegion::kReplace_Op == op) {
978 return currRgn->setPath(devPath, base);
979 } else {
980 SkRegion rgn;
981 rgn.setPath(devPath, base);
982 return currRgn->op(rgn, op);
983 }
984 }
985}
986
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000988 AutoValidateClip avc(this);
989
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 fDeviceCMDirty = true;
991 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000992 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993
994 SkPath devPath;
995 path.transform(*fMCRec->fMatrix, &devPath);
996
reed@google.com5c3d1472011-02-22 19:12:23 +0000997 // if we called path.swap() we could avoid a deep copy of this path
998 fClipStack.clipDevPath(devPath, op);
999
reed@google.com819c9212011-02-23 18:56:55 +00001000 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001}
1002
1003bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001004 AutoValidateClip avc(this);
1005
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 fDeviceCMDirty = true;
1007 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001008 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009
reed@google.com5c3d1472011-02-22 19:12:23 +00001010 // todo: signal fClipStack that we have a region, and therefore (I guess)
1011 // we have to ignore it, and use the region directly?
1012 fClipStack.clipDevRect(rgn.getBounds());
1013
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 return fMCRec->fRegion->op(rgn, op);
1015}
1016
reed@google.com819c9212011-02-23 18:56:55 +00001017#ifdef SK_DEBUG
1018void SkCanvas::validateClip() const {
1019 // construct clipRgn from the clipstack
1020 const SkDevice* device = this->getDevice();
1021 SkIRect ir;
1022 ir.set(0, 0, device->width(), device->height());
1023 SkRegion clipRgn(ir);
1024
1025 SkClipStack::B2FIter iter(fClipStack);
1026 const SkClipStack::B2FIter::Clip* clip;
1027 while ((clip = iter.next()) != NULL) {
1028 if (clip->fPath) {
1029 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1030 } else if (clip->fRect) {
1031 clip->fRect->round(&ir);
1032 clipRgn.op(ir, clip->fOp);
1033 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001034 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001035 }
1036 }
1037
reed@google.com6f8f2922011-03-04 22:27:10 +00001038#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001039 // now compare against the current rgn
1040 const SkRegion& rgn = this->getTotalClip();
1041 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001042#endif
reed@google.com819c9212011-02-23 18:56:55 +00001043}
1044#endif
1045
reed@google.com5c3d1472011-02-22 19:12:23 +00001046///////////////////////////////////////////////////////////////////////////////
1047
reed@android.comba09de42010-02-05 20:46:05 +00001048void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001050 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1051 fLocalBoundsCompareTypeBW;
1052
1053 if (!this->getClipBounds(&r, et)) {
1054 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001056 rCompare.set(SkScalarToCompareType(r.fLeft),
1057 SkScalarToCompareType(r.fTop),
1058 SkScalarToCompareType(r.fRight),
1059 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001060 }
1061}
1062
reed@android.comd252db02009-04-01 18:31:44 +00001063/* current impl ignores edgetype, and relies on
1064 getLocalClipBoundsCompareType(), which always returns a value assuming
1065 antialiasing (worst case)
1066 */
reed@android.comba09de42010-02-05 20:46:05 +00001067bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001068
1069 if (!rect.hasValidCoordinates())
1070 return true;
1071
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072 if (fMCRec->fRegion->isEmpty()) {
1073 return true;
1074 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075
reed@android.coma380ae42009-07-21 01:17:02 +00001076 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1077 SkRect dst;
1078 fMCRec->fMatrix->mapRect(&dst, rect);
1079 SkIRect idst;
1080 dst.roundOut(&idst);
1081 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1082 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001083 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001084
reed@android.coma380ae42009-07-21 01:17:02 +00001085 // for speed, do the most likely reject compares first
1086 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1087 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1088 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1089 return true;
1090 }
1091 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1092 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1093 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1094 return true;
1095 }
1096 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098}
1099
1100bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001101 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102}
1103
1104bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1105 /* current impl ignores edgetype, and relies on
1106 getLocalClipBoundsCompareType(), which always returns a value assuming
1107 antialiasing (worst case)
1108 */
1109
1110 if (fMCRec->fRegion->isEmpty()) {
1111 return true;
1112 }
reed@google.com4b226022011-01-11 18:32:13 +00001113
reed@android.comaefd2bc2009-03-30 21:02:14 +00001114 SkScalarCompareType userT = SkScalarToCompareType(top);
1115 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001116
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001118 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 if (userT >= userB) {
1120 return true;
1121 }
reed@google.com4b226022011-01-11 18:32:13 +00001122
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 // check if we are above or below the local clip bounds
1124 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1125 return userT >= clipR.fBottom || userB <= clipR.fTop;
1126}
1127
1128bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1129 const SkRegion& clip = *fMCRec->fRegion;
1130 if (clip.isEmpty()) {
1131 if (bounds) {
1132 bounds->setEmpty();
1133 }
1134 return false;
1135 }
1136
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001137 SkMatrix inverse;
1138 // if we can't invert the CTM, we can't return local clip bounds
1139 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001140 if (bounds) {
1141 bounds->setEmpty();
1142 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001143 return false;
1144 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001146 if (NULL != bounds) {
1147 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 // get the clip's bounds
1149 const SkIRect& ibounds = clip.getBounds();
1150 // adjust it outwards if we are antialiasing
1151 int inset = (kAA_EdgeType == et);
1152 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1153 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001154
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155 // invert into local coordinates
1156 inverse.mapRect(bounds, r);
1157 }
1158 return true;
1159}
1160
1161const SkMatrix& SkCanvas::getTotalMatrix() const {
1162 return *fMCRec->fMatrix;
1163}
1164
1165const SkRegion& SkCanvas::getTotalClip() const {
1166 return *fMCRec->fRegion;
1167}
1168
reed@google.com7d7ca792011-02-23 22:39:18 +00001169const SkClipStack& SkCanvas::getTotalClipStack() const {
1170 return fClipStack;
1171}
1172
reed@android.comf2b98d62010-12-20 18:26:13 +00001173void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1174 if (NULL == matrix || matrix->isIdentity()) {
1175 if (fUseExternalMatrix) {
1176 fDeviceCMDirty = true;
1177 }
1178 fUseExternalMatrix = false;
1179 } else {
1180 fUseExternalMatrix = true;
1181 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001182
reed@android.comf2b98d62010-12-20 18:26:13 +00001183 fExternalMatrix = *matrix;
1184 matrix->invert(&fExternalInverse);
1185 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001186}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187
reed@android.comf2b98d62010-12-20 18:26:13 +00001188SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1189 bool isOpaque, bool forLayer) {
1190 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191}
1192
1193//////////////////////////////////////////////////////////////////////////////
1194// These are the virtual drawing methods
1195//////////////////////////////////////////////////////////////////////////////
1196
1197void SkCanvas::drawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001198 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199
1200 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001201 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 }
1203
reed@google.com4e2b3d32011-04-07 14:18:59 +00001204 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205}
1206
1207void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1208 const SkPaint& paint) {
1209 if ((long)count <= 0) {
1210 return;
1211 }
1212
1213 SkASSERT(pts != NULL);
1214
reed@google.com4e2b3d32011-04-07 14:18:59 +00001215 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001216
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001218 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219 }
reed@google.com4b226022011-01-11 18:32:13 +00001220
reed@google.com4e2b3d32011-04-07 14:18:59 +00001221 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222}
1223
1224void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1225 if (paint.canComputeFastBounds()) {
1226 SkRect storage;
1227 if (this->quickReject(paint.computeFastBounds(r, &storage),
1228 paint2EdgeType(&paint))) {
1229 return;
1230 }
1231 }
reed@google.com4b226022011-01-11 18:32:13 +00001232
reed@google.com4e2b3d32011-04-07 14:18:59 +00001233 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234
1235 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001236 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237 }
1238
reed@google.com4e2b3d32011-04-07 14:18:59 +00001239 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240}
1241
1242void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1243 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001244 SkRect storage;
1245 const SkRect& bounds = path.getBounds();
1246 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 paint2EdgeType(&paint))) {
1248 return;
1249 }
1250 }
1251
reed@google.com4e2b3d32011-04-07 14:18:59 +00001252 LOOPER_BEGIN(paint, SkDrawFilter::kPath_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->drawPath(iter, path, 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::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1262 const SkPaint* paint) {
1263 SkDEBUGCODE(bitmap.validate();)
1264
1265 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1266 SkRect fastBounds;
1267 fastBounds.set(x, y,
1268 x + SkIntToScalar(bitmap.width()),
1269 y + SkIntToScalar(bitmap.height()));
1270 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1271 return;
1272 }
1273 }
reed@google.com4b226022011-01-11 18:32:13 +00001274
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275 SkMatrix matrix;
1276 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001277 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278}
1279
1280void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1281 const SkRect& dst, const SkPaint* paint) {
1282 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1283 return;
1284 }
reed@google.com4b226022011-01-11 18:32:13 +00001285
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 // do this now, to avoid the cost of calling extract for RLE bitmaps
1287 if (this->quickReject(dst, paint2EdgeType(paint))) {
1288 return;
1289 }
reed@google.com4b226022011-01-11 18:32:13 +00001290
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001292
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001294 SkRect tmpSrc;
1295 if (src) {
1296 tmpSrc.set(*src);
1297 // if the extract process clipped off the top or left of the
1298 // original, we adjust for that here to get the position right.
1299 if (tmpSrc.fLeft > 0) {
1300 tmpSrc.fRight -= tmpSrc.fLeft;
1301 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001302 }
reed@android.com87899992009-10-16 14:48:38 +00001303 if (tmpSrc.fTop > 0) {
1304 tmpSrc.fBottom -= tmpSrc.fTop;
1305 tmpSrc.fTop = 0;
1306 }
1307 } else {
1308 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1309 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 }
reed@android.com87899992009-10-16 14:48:38 +00001311 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001312
1313 // ensure that src is "valid" before we pass it to our internal routines
1314 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1315 SkIRect tmpISrc;
1316 if (src) {
1317 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001318 if (!tmpISrc.intersect(*src)) {
1319 return;
1320 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001321 src = &tmpISrc;
1322 }
1323 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324}
1325
1326void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1327 const SkPaint* paint) {
1328 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001329 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330}
1331
reed@android.comf2b98d62010-12-20 18:26:13 +00001332void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1333 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001335
reed@google.com4e2b3d32011-04-07 14:18:59 +00001336 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001337
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001339 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 }
reed@android.com9b039062009-02-11 15:09:58 +00001341
reed@google.com4e2b3d32011-04-07 14:18:59 +00001342 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343}
1344
1345void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1346 const SkPaint* paint) {
1347 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001348
reed@android.com8a1c16f2008-12-17 15:59:43 +00001349 if (reject_bitmap(bitmap)) {
1350 return;
1351 }
reed@google.com4b226022011-01-11 18:32:13 +00001352
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 SkPaint tmp;
1354 if (NULL == paint) {
1355 paint = &tmp;
1356 }
reed@google.com4b226022011-01-11 18:32:13 +00001357
reed@google.com4e2b3d32011-04-07 14:18:59 +00001358 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001359
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 while (iter.next()) {
1361 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001362 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001364 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365}
1366
reed@google.comf67e4cf2011-03-15 20:56:58 +00001367class SkDeviceFilteredPaint {
1368public:
1369 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1370 SkDevice::TextFlags flags;
1371 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001372 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001373 newPaint->setFlags(flags.fFlags);
1374 newPaint->setHinting(flags.fHinting);
1375 fPaint = newPaint;
1376 } else {
1377 fPaint = &paint;
1378 }
1379 }
1380
reed@google.comf67e4cf2011-03-15 20:56:58 +00001381 const SkPaint& paint() const { return *fPaint; }
1382
1383private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001384 const SkPaint* fPaint;
1385 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001386};
1387
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388void SkCanvas::drawText(const void* text, size_t byteLength,
1389 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001390 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001391
1392 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001393 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001394 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395 }
1396
reed@google.com4e2b3d32011-04-07 14:18:59 +00001397 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398}
1399
1400void SkCanvas::drawPosText(const void* text, size_t byteLength,
1401 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001402 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001405 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001407 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 }
reed@google.com4b226022011-01-11 18:32:13 +00001409
reed@google.com4e2b3d32011-04-07 14:18:59 +00001410 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001411}
1412
1413void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1414 const SkScalar xpos[], SkScalar constY,
1415 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001416 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001417
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001419 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001421 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 }
reed@google.com4b226022011-01-11 18:32:13 +00001423
reed@google.com4e2b3d32011-04-07 14:18:59 +00001424 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425}
1426
1427void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1428 const SkPath& path, const SkMatrix* matrix,
1429 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001430 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431
1432 while (iter.next()) {
1433 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001434 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 }
1436
reed@google.com4e2b3d32011-04-07 14:18:59 +00001437 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438}
1439
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001440#ifdef ANDROID
1441void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1442 const SkPoint pos[], const SkPaint& paint,
1443 const SkPath& path, const SkMatrix* matrix) {
1444
reed@google.com4e2b3d32011-04-07 14:18:59 +00001445 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001446
1447 while (iter.next()) {
1448 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001449 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001450 }
1451
reed@google.com4e2b3d32011-04-07 14:18:59 +00001452 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001453}
1454#endif
1455
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1457 const SkPoint verts[], const SkPoint texs[],
1458 const SkColor colors[], SkXfermode* xmode,
1459 const uint16_t indices[], int indexCount,
1460 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001461 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001462
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 while (iter.next()) {
1464 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001465 colors, xmode, indices, indexCount,
1466 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 }
reed@google.com4b226022011-01-11 18:32:13 +00001468
reed@google.com4e2b3d32011-04-07 14:18:59 +00001469 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470}
1471
reed@android.comcb608442009-12-04 21:32:27 +00001472void SkCanvas::drawData(const void* data, size_t length) {
1473 // do nothing. Subclasses may do something with the data
1474}
1475
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476//////////////////////////////////////////////////////////////////////////////
1477// These methods are NOT virtual, and therefore must call back into virtual
1478// methods, rather than actually drawing themselves.
1479//////////////////////////////////////////////////////////////////////////////
1480
1481void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001482 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 SkPaint paint;
1484
1485 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001486 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001487 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 }
1489 this->drawPaint(paint);
1490}
1491
reed@android.com845fdac2009-06-23 03:01:32 +00001492void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493 SkPaint paint;
1494
1495 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001496 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001497 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498 }
1499 this->drawPaint(paint);
1500}
1501
1502void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1503 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001504
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505 pt.set(x, y);
1506 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1507}
1508
1509void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1510 SkPoint pt;
1511 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001512
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 pt.set(x, y);
1514 paint.setColor(color);
1515 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1516}
1517
1518void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1519 const SkPaint& paint) {
1520 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001521
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522 pts[0].set(x0, y0);
1523 pts[1].set(x1, y1);
1524 this->drawPoints(kLines_PointMode, 2, pts, paint);
1525}
1526
1527void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1528 SkScalar right, SkScalar bottom,
1529 const SkPaint& paint) {
1530 SkRect r;
1531
1532 r.set(left, top, right, bottom);
1533 this->drawRect(r, paint);
1534}
1535
1536void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1537 const SkPaint& paint) {
1538 if (radius < 0) {
1539 radius = 0;
1540 }
1541
1542 SkRect r;
1543 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001544
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545 if (paint.canComputeFastBounds()) {
1546 SkRect storage;
1547 if (this->quickReject(paint.computeFastBounds(r, &storage),
1548 paint2EdgeType(&paint))) {
1549 return;
1550 }
1551 }
reed@google.com4b226022011-01-11 18:32:13 +00001552
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553 SkPath path;
1554 path.addOval(r);
1555 this->drawPath(path, paint);
1556}
1557
1558void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1559 const SkPaint& paint) {
1560 if (rx > 0 && ry > 0) {
1561 if (paint.canComputeFastBounds()) {
1562 SkRect storage;
1563 if (this->quickReject(paint.computeFastBounds(r, &storage),
1564 paint2EdgeType(&paint))) {
1565 return;
1566 }
1567 }
1568
1569 SkPath path;
1570 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1571 this->drawPath(path, paint);
1572 } else {
1573 this->drawRect(r, paint);
1574 }
1575}
1576
1577void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1578 if (paint.canComputeFastBounds()) {
1579 SkRect storage;
1580 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1581 paint2EdgeType(&paint))) {
1582 return;
1583 }
1584 }
1585
1586 SkPath path;
1587 path.addOval(oval);
1588 this->drawPath(path, paint);
1589}
1590
1591void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1592 SkScalar sweepAngle, bool useCenter,
1593 const SkPaint& paint) {
1594 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1595 this->drawOval(oval, paint);
1596 } else {
1597 SkPath path;
1598 if (useCenter) {
1599 path.moveTo(oval.centerX(), oval.centerY());
1600 }
1601 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1602 if (useCenter) {
1603 path.close();
1604 }
1605 this->drawPath(path, paint);
1606 }
1607}
1608
1609void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1610 const SkPath& path, SkScalar hOffset,
1611 SkScalar vOffset, const SkPaint& paint) {
1612 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001613
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614 matrix.setTranslate(hOffset, vOffset);
1615 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1616}
1617
reed@android.comf76bacf2009-05-13 14:00:33 +00001618///////////////////////////////////////////////////////////////////////////////
1619
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620void SkCanvas::drawPicture(SkPicture& picture) {
1621 int saveCount = save();
1622 picture.draw(this);
1623 restoreToCount(saveCount);
1624}
1625
reed@android.comf76bacf2009-05-13 14:00:33 +00001626void SkCanvas::drawShape(SkShape* shape) {
1627 // shape baseclass takes care of save/restore
1628 shape->draw(this);
1629}
1630
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631///////////////////////////////////////////////////////////////////////////////
1632///////////////////////////////////////////////////////////////////////////////
1633
1634SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001635 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636
1637 SkASSERT(canvas);
1638
1639 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1640 fDone = !fImpl->next();
1641}
1642
1643SkCanvas::LayerIter::~LayerIter() {
1644 fImpl->~SkDrawIter();
1645}
1646
1647void SkCanvas::LayerIter::next() {
1648 fDone = !fImpl->next();
1649}
1650
1651SkDevice* SkCanvas::LayerIter::device() const {
1652 return fImpl->getDevice();
1653}
1654
1655const SkMatrix& SkCanvas::LayerIter::matrix() const {
1656 return fImpl->getMatrix();
1657}
1658
1659const SkPaint& SkCanvas::LayerIter::paint() const {
1660 const SkPaint* paint = fImpl->getPaint();
1661 if (NULL == paint) {
1662 paint = &fDefaultPaint;
1663 }
1664 return *paint;
1665}
1666
1667const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1668int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1669int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1670