blob: 1926d89ccea29519728002ea39eaf5977e83a10b [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
54///////////////////////////////////////////////////////////////////////////////
55// Helpers for computing fast bounds for quickReject tests
56
57static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
58 return paint != NULL && paint->isAntiAlias() ?
59 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
60}
61
62///////////////////////////////////////////////////////////////////////////////
63
64/* This is the record we keep for each SkDevice that the user installs.
65 The clip/matrix/proc are fields that reflect the top of the save/restore
66 stack. Whenever the canvas changes, it marks a dirty flag, and then before
67 these are used (assuming we're not on a layer) we rebuild these cache
68 values: they reflect the top of the save stack, but translated and clipped
69 by the device's XY offset and bitmap-bounds.
70*/
71struct DeviceCM {
72 DeviceCM* fNext;
73 SkDevice* fDevice;
74 SkRegion fClip;
75 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000076 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000077 // optional, related to canvas' external matrix
78 const SkMatrix* fMVMatrix;
79 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
81 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
82 : fNext(NULL) {
83 if (NULL != device) {
84 device->ref();
85 device->lockPixels();
86 }
reed@google.com4b226022011-01-11 18:32:13 +000087 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
89 }
90
91 ~DeviceCM() {
92 if (NULL != fDevice) {
93 fDevice->unlockPixels();
94 fDevice->unref();
95 }
96 SkDELETE(fPaint);
97 }
reed@google.com4b226022011-01-11 18:32:13 +000098
reed@android.com8a1c16f2008-12-17 15:59:43 +000099 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +0000100 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000101 int x = fDevice->getOrigin().x();
102 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 int width = fDevice->width();
104 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000105
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 if ((x | y) == 0) {
107 fMatrix = &totalMatrix;
108 fClip = totalClip;
109 } else {
110 fMatrixStorage = totalMatrix;
111 fMatrixStorage.postTranslate(SkIntToScalar(-x),
112 SkIntToScalar(-y));
113 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 totalClip.translate(-x, -y, &fClip);
116 }
117
118 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
119
120 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 if (updateClip) {
123 updateClip->op(x, y, x + width, y + height,
124 SkRegion::kDifference_Op);
125 }
reed@google.com4b226022011-01-11 18:32:13 +0000126
reed@google.com46799cd2011-02-22 20:56:26 +0000127 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128
129#ifdef SK_DEBUG
130 if (!fClip.isEmpty()) {
131 SkIRect deviceR;
132 deviceR.set(0, 0, width, height);
133 SkASSERT(deviceR.contains(fClip.getBounds()));
134 }
135#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000136 // default is to assume no external matrix
137 fMVMatrix = NULL;
138 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000140
141 // can only be called after calling updateMC()
142 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
143 fMVMatrixStorage.setConcat(extI, *fMatrix);
144 fMVMatrix = &fMVMatrixStorage;
145 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
146 }
147
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000149 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150};
151
152/* This is the record we keep for each save/restore level in the stack.
153 Since a level optionally copies the matrix and/or stack, we have pointers
154 for these fields. If the value is copied for this level, the copy is
155 stored in the ...Storage field, and the pointer points to that. If the
156 value is not copied for this level, we ignore ...Storage, and just point
157 at the corresponding value in the previous level in the stack.
158*/
159class SkCanvas::MCRec {
160public:
161 MCRec* fNext;
162 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
163 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
164 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000165
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 DeviceCM* fLayer;
167 /* If there are any layers in the stack, this points to the top-most
168 one that is at or below this level in the stack (so we know what
169 bitmap/device to draw into from this level. This value is NOT
170 reference counted, since the real owner is either our fLayer field,
171 or a previous one in a lower level.)
172 */
173 DeviceCM* fTopLayer;
174
175 MCRec(const MCRec* prev, int flags) {
176 if (NULL != prev) {
177 if (flags & SkCanvas::kMatrix_SaveFlag) {
178 fMatrixStorage = *prev->fMatrix;
179 fMatrix = &fMatrixStorage;
180 } else {
181 fMatrix = prev->fMatrix;
182 }
reed@google.com4b226022011-01-11 18:32:13 +0000183
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 if (flags & SkCanvas::kClip_SaveFlag) {
185 fRegionStorage = *prev->fRegion;
186 fRegion = &fRegionStorage;
187 } else {
188 fRegion = prev->fRegion;
189 }
190
191 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000192 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
194 fTopLayer = prev->fTopLayer;
195 } else { // no prev
196 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000197
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 fMatrix = &fMatrixStorage;
199 fRegion = &fRegionStorage;
200 fFilter = NULL;
201 fTopLayer = NULL;
202 }
203 fLayer = NULL;
204
205 // don't bother initializing fNext
206 inc_rec();
207 }
208 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000209 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000210 SkDELETE(fLayer);
211 dec_rec();
212 }
reed@google.com4b226022011-01-11 18:32:13 +0000213
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214private:
215 SkMatrix fMatrixStorage;
216 SkRegion fRegionStorage;
217};
218
219class SkDrawIter : public SkDraw {
220public:
221 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
222 fCanvas = canvas;
223 canvas->updateDeviceCMCache();
224
reed@google.com7d7ca792011-02-23 22:39:18 +0000225 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 fBounder = canvas->getBounder();
227 fCurrLayer = canvas->fMCRec->fTopLayer;
228 fSkipEmptyClips = skipEmptyClips;
229 }
reed@google.com4b226022011-01-11 18:32:13 +0000230
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 bool next() {
232 // skip over recs with empty clips
233 if (fSkipEmptyClips) {
234 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
235 fCurrLayer = fCurrLayer->fNext;
236 }
237 }
238
239 if (NULL != fCurrLayer) {
240 const DeviceCM* rec = fCurrLayer;
241
242 fMatrix = rec->fMatrix;
243 fClip = &rec->fClip;
244 fDevice = rec->fDevice;
245 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000247 fMVMatrix = rec->fMVMatrix;
248 fExtMatrix = rec->fExtMatrix;
249 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
251 fCurrLayer = rec->fNext;
252 if (fBounder) {
253 fBounder->setClip(fClip);
254 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000256
bsalomon@google.comd302f142011-03-03 13:54:13 +0000257 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 return true;
259 }
260 return false;
261 }
reed@google.com4b226022011-01-11 18:32:13 +0000262
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000264 int getX() const { return fDevice->getOrigin().x(); }
265 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 const SkMatrix& getMatrix() const { return *fMatrix; }
267 const SkRegion& getClip() const { return *fClip; }
268 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000269
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270private:
271 SkCanvas* fCanvas;
272 const DeviceCM* fCurrLayer;
273 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 SkBool8 fSkipEmptyClips;
275
276 typedef SkDraw INHERITED;
277};
278
279/////////////////////////////////////////////////////////////////////////////
280
281class AutoDrawLooper {
282public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000283 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
284 fCanvas = canvas;
285 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000287 fPaint = NULL;
288 fSaveCount = canvas->getSaveCount();
289 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290
reed@google.com4e2b3d32011-04-07 14:18:59 +0000291 if (fLooper) {
292 fLooper->init(canvas);
293 }
294 }
295
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000297 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000299
300 const SkPaint& paint() const {
301 SkASSERT(fPaint);
302 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000304
305 bool next(SkDrawFilter::Type drawType);
306
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307private:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000308 SkTLazy<SkPaint> fLazyPaint;
309 SkCanvas* fCanvas;
310 const SkPaint& fOrigPaint;
311 SkDrawLooper* fLooper;
312 SkDrawFilter* fFilter;
313 const SkPaint* fPaint;
314 int fSaveCount;
315 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316};
317
reed@google.com4e2b3d32011-04-07 14:18:59 +0000318bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
319 if (fDone) {
320 fPaint = NULL;
321 return false;
322 }
323 if (!fLooper && !fFilter) {
324 fDone = true;
325 fPaint = &fOrigPaint;
326 return true;
327 }
328
329 SkPaint* paint = fLazyPaint.set(fOrigPaint);
330 if (fLooper && !fLooper->next(fCanvas, paint)) {
331 fDone = true;
332 fPaint = NULL;
333 return false;
334 }
335 if (fFilter) {
336 fFilter->filter(paint, drawType);
337 }
338 fPaint = paint;
339 return true;
340}
341
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342/* Stack helper for managing a SkBounder. In the destructor, if we were
343 given a bounder, we call its commit() method, signifying that we are
344 done accumulating bounds for that draw.
345*/
346class SkAutoBounderCommit {
347public:
348 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
349 ~SkAutoBounderCommit() {
350 if (NULL != fBounder) {
351 fBounder->commit();
352 }
353 }
354private:
355 SkBounder* fBounder;
356};
357
358#include "SkColorPriv.h"
359
360class AutoValidator {
361public:
362 AutoValidator(SkDevice* device) : fDevice(device) {}
363 ~AutoValidator() {
364#ifdef SK_DEBUG
365 const SkBitmap& bm = fDevice->accessBitmap(false);
366 if (bm.config() == SkBitmap::kARGB_4444_Config) {
367 for (int y = 0; y < bm.height(); y++) {
368 const SkPMColor16* p = bm.getAddr16(0, y);
369 for (int x = 0; x < bm.width(); x++) {
370 SkPMColor16 c = p[x];
371 SkPMColor16Assert(c);
372 }
373 }
374 }
375#endif
376 }
377private:
378 SkDevice* fDevice;
379};
380
381////////// macros to place around the internal draw calls //////////////////
382
reed@google.com4e2b3d32011-04-07 14:18:59 +0000383#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000385 AutoDrawLooper looper(this, paint); \
386 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387 SkAutoBounderCommit ac(fBounder); \
388 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000389
reed@google.com4e2b3d32011-04-07 14:18:59 +0000390#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391
392////////////////////////////////////////////////////////////////////////////
393
394SkDevice* SkCanvas::init(SkDevice* device) {
395 fBounder = NULL;
396 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000397 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000398 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000399 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400
401 fMCRec = (MCRec*)fMCStack.push_back();
402 new (fMCRec) MCRec(NULL, 0);
403
404 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
405 fMCRec->fTopLayer = fMCRec->fLayer;
406 fMCRec->fNext = NULL;
407
reed@android.comf2b98d62010-12-20 18:26:13 +0000408 fUseExternalMatrix = false;
409
reed@android.com8a1c16f2008-12-17 15:59:43 +0000410 return this->setDevice(device);
411}
412
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000413SkCanvas::SkCanvas(SkDeviceFactory* factory)
414 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
415 fDeviceFactory(factory) {
416 inc_canvas();
417
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000418 if (!factory)
419 fDeviceFactory = SkNEW(SkRasterDeviceFactory);
420
421 this->init(NULL);
422}
423
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424SkCanvas::SkCanvas(SkDevice* device)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000425 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)),
426 fDeviceFactory(device->getDeviceFactory()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 inc_canvas();
428
429 this->init(device);
430}
431
432SkCanvas::SkCanvas(const SkBitmap& bitmap)
433 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
434 inc_canvas();
435
reed@android.comf2b98d62010-12-20 18:26:13 +0000436 SkDevice* device = SkNEW_ARGS(SkDevice, (this, bitmap, false));
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000437 fDeviceFactory = device->getDeviceFactory();
438 this->init(device)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439}
440
441SkCanvas::~SkCanvas() {
442 // free up the contents of our deque
443 this->restoreToCount(1); // restore everything but the last
444 this->internalRestore(); // restore the last, since we're going away
445
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000446 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000447 SkDELETE(fDeviceFactory);
448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 dec_canvas();
450}
451
452SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
453 SkRefCnt_SafeAssign(fBounder, bounder);
454 return bounder;
455}
456
457SkDrawFilter* SkCanvas::getDrawFilter() const {
458 return fMCRec->fFilter;
459}
460
461SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
462 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
463 return filter;
464}
465
466///////////////////////////////////////////////////////////////////////////////
467
468SkDevice* SkCanvas::getDevice() const {
469 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000470 SkDeque::F2BIter iter(fMCStack);
471 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472 SkASSERT(rec && rec->fLayer);
473 return rec->fLayer->fDevice;
474}
475
reed@google.com9266fed2011-03-30 00:18:03 +0000476SkDevice* SkCanvas::getTopDevice() const {
477 return fMCRec->fTopLayer->fDevice;
478}
479
reed@android.com8a1c16f2008-12-17 15:59:43 +0000480SkDevice* SkCanvas::setDevice(SkDevice* device) {
481 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000482 SkDeque::F2BIter iter(fMCStack);
483 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 SkASSERT(rec && rec->fLayer);
485 SkDevice* rootDevice = rec->fLayer->fDevice;
486
487 if (rootDevice == device) {
488 return device;
489 }
reed@google.com4b226022011-01-11 18:32:13 +0000490
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 /* Notify the devices that they are going in/out of scope, so they can do
492 things like lock/unlock their pixels, etc.
493 */
494 if (device) {
495 device->lockPixels();
496 }
497 if (rootDevice) {
498 rootDevice->unlockPixels();
499 }
500
501 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
502 rootDevice = device;
503
504 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000505
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 /* Now we update our initial region to have the bounds of the new device,
507 and then intersect all of the clips in our stack with these bounds,
508 to ensure that we can't draw outside of the device's bounds (and trash
509 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000510
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 NOTE: this is only a partial-fix, since if the new device is larger than
512 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000513 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
515 reconstruct the correct clips, so this approximation will have to do.
516 The caller really needs to restore() back to the base if they want to
517 accurately take advantage of the new device bounds.
518 */
519
520 if (NULL == device) {
521 rec->fRegion->setEmpty();
522 while ((rec = (MCRec*)iter.next()) != NULL) {
523 (void)rec->fRegion->setEmpty();
524 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000525 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 } else {
527 // compute our total bounds for all devices
528 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000529
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 bounds.set(0, 0, device->width(), device->height());
531
532 // now jam our 1st clip to be bounds, and intersect the rest with that
533 rec->fRegion->setRect(bounds);
534 while ((rec = (MCRec*)iter.next()) != NULL) {
535 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
536 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000537 fClipStack.clipDevRect(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 }
539 return device;
540}
541
reed@android.comf2b98d62010-12-20 18:26:13 +0000542SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap, bool forLayer) {
543 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (this, bitmap, forLayer)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 device->unref();
545 return device;
546}
547
reed@google.com51df9e32010-12-23 19:29:18 +0000548bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
549 SkDevice* device = this->getDevice();
550 if (!device) {
551 return false;
552 }
553 return device->readPixels(srcRect, bitmap);
554}
555
reed@google.com4b226022011-01-11 18:32:13 +0000556SkDeviceFactory* SkCanvas::setDeviceFactory(SkDeviceFactory* factory) {
557 SkDELETE(fDeviceFactory);
558 fDeviceFactory = factory;
reed@google.com9b2135a2011-01-11 19:45:38 +0000559 return factory;
reed@google.com4b226022011-01-11 18:32:13 +0000560}
561
562//////////////////////////////////////////////////////////////////////////////
563
reed@google.com51df9e32010-12-23 19:29:18 +0000564bool SkCanvas::readPixels(SkBitmap* bitmap) {
565 SkDevice* device = this->getDevice();
566 if (!device) {
567 return false;
568 }
569 SkIRect bounds;
570 bounds.set(0, 0, device->width(), device->height());
571 return this->readPixels(bounds, bitmap);
572}
573
574void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
575 SkDevice* device = this->getDevice();
576 if (device) {
577 device->writePixels(bitmap, x, y);
578 }
579}
580
reed@android.com8a1c16f2008-12-17 15:59:43 +0000581//////////////////////////////////////////////////////////////////////////////
582
583bool SkCanvas::getViewport(SkIPoint* size) const {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000584 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000585 return false;
586 if (size)
587 size->set(getDevice()->width(), getDevice()->height());
588 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589}
590
591bool SkCanvas::setViewport(int width, int height) {
vandebo@chromium.org35fc62b2010-10-26 19:47:30 +0000592 if ((getDevice()->getDeviceCapabilities() & SkDevice::kGL_Capability) == 0)
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000593 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000594
595 this->setDevice(this->createDevice(SkBitmap::kARGB_8888_Config, width, height,
596 false, false))->unref();
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000597 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598}
599
600void SkCanvas::updateDeviceCMCache() {
601 if (fDeviceCMDirty) {
602 const SkMatrix& totalMatrix = this->getTotalMatrix();
603 const SkRegion& totalClip = this->getTotalClip();
604 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000607 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000608 if (fUseExternalMatrix) {
609 layer->updateExternalMatrix(fExternalMatrix,
610 fExternalInverse);
611 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 } else {
613 SkRegion clip;
614 clip = totalClip; // make a copy
615 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000616 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000617 if (fUseExternalMatrix) {
618 layer->updateExternalMatrix(fExternalMatrix,
619 fExternalInverse);
620 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 } while ((layer = layer->fNext) != NULL);
622 }
623 fDeviceCMDirty = false;
624 }
625}
626
reed@android.comf2b98d62010-12-20 18:26:13 +0000627void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000628 const SkRegion& clip,
629 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000631 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000632 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000633 fLastDeviceToGainFocus = device;
634 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635}
636
637///////////////////////////////////////////////////////////////////////////////
638
639int SkCanvas::internalSave(SaveFlags flags) {
640 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000641
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 MCRec* newTop = (MCRec*)fMCStack.push_back();
643 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000644
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 newTop->fNext = fMCRec;
646 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000647
reed@google.com5c3d1472011-02-22 19:12:23 +0000648 fClipStack.save();
649 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 return saveCount;
652}
653
654int SkCanvas::save(SaveFlags flags) {
655 // call shared impl
656 return this->internalSave(flags);
657}
658
659#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
660#define C16MASK (1 << SkBitmap::kRGB_565_Config)
661#define C8MASK (1 << SkBitmap::kA8_Config)
662
663static SkBitmap::Config resolve_config(SkCanvas* canvas,
664 const SkIRect& bounds,
665 SkCanvas::SaveFlags flags,
666 bool* isOpaque) {
667 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
668
669#if 0
670 // loop through and union all the configs we may draw into
671 uint32_t configMask = 0;
672 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
673 {
674 SkDevice* device = canvas->getLayerDevice(i);
675 if (device->intersects(bounds))
676 configMask |= 1 << device->config();
677 }
678
679 // if the caller wants alpha or fullcolor, we can't return 565
680 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
681 SkCanvas::kHasAlphaLayer_SaveFlag))
682 configMask &= ~C16MASK;
683
684 switch (configMask) {
685 case C8MASK: // if we only have A8, return that
686 return SkBitmap::kA8_Config;
687
688 case C16MASK: // if we only have 565, return that
689 return SkBitmap::kRGB_565_Config;
690
691 default:
692 return SkBitmap::kARGB_8888_Config; // default answer
693 }
694#else
695 return SkBitmap::kARGB_8888_Config; // default answer
696#endif
697}
698
699static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
700 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
701}
702
703int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
704 SaveFlags flags) {
705 // do this before we create the layer. We don't call the public save() since
706 // that would invoke a possibly overridden virtual
707 int count = this->internalSave(flags);
708
709 fDeviceCMDirty = true;
710
711 SkIRect ir;
712 const SkIRect& clipBounds = this->getTotalClip().getBounds();
reed@android.comf2b98d62010-12-20 18:26:13 +0000713 if (clipBounds.isEmpty()) {
714 return count;
715 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716
717 if (NULL != bounds) {
718 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000719
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 this->getTotalMatrix().mapRect(&r, *bounds);
721 r.roundOut(&ir);
722 // early exit if the layer's bounds are clipped out
723 if (!ir.intersect(clipBounds)) {
724 if (bounds_affects_clip(flags))
725 fMCRec->fRegion->setEmpty();
726 return count;
727 }
728 } else { // no user bounds, so just use the clip
729 ir = clipBounds;
730 }
731
reed@google.com5c3d1472011-02-22 19:12:23 +0000732 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 // early exit if the clip is now empty
734 if (bounds_affects_clip(flags) &&
735 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
736 return count;
737 }
738
739 bool isOpaque;
740 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
741
742 SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
743 isOpaque, true);
reed@google.com6f8f2922011-03-04 22:27:10 +0000744 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
746 device->unref();
747
748 layer->fNext = fMCRec->fTopLayer;
749 fMCRec->fLayer = layer;
750 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
751
752 return count;
753}
754
755int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
756 SaveFlags flags) {
757 if (0xFF == alpha) {
758 return this->saveLayer(bounds, NULL, flags);
759 } else {
760 SkPaint tmpPaint;
761 tmpPaint.setAlpha(alpha);
762 return this->saveLayer(bounds, &tmpPaint, flags);
763 }
764}
765
766void SkCanvas::restore() {
767 // check for underflow
768 if (fMCStack.count() > 1) {
769 this->internalRestore();
770 }
771}
772
773void SkCanvas::internalRestore() {
774 SkASSERT(fMCStack.count() != 0);
775
776 fDeviceCMDirty = true;
777 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000778 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779
reed@google.com5c3d1472011-02-22 19:12:23 +0000780 fClipStack.restore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 // reserve our layer (if any)
782 DeviceCM* layer = fMCRec->fLayer; // may be null
783 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
784 fMCRec->fLayer = NULL;
785
786 // now do the normal restore()
787 fMCRec->~MCRec(); // balanced in save()
788 fMCStack.pop_back();
789 fMCRec = (MCRec*)fMCStack.back();
790
791 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
792 since if we're being recorded, we don't want to record this (the
793 recorder will have already recorded the restore).
794 */
795 if (NULL != layer) {
796 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000797 const SkIPoint& origin = layer->fDevice->getOrigin();
798 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 layer->fPaint);
800 // reset this, since drawDevice will have set it to true
801 fDeviceCMDirty = true;
802 }
803 SkDELETE(layer);
804 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000805
806 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807}
808
809int SkCanvas::getSaveCount() const {
810 return fMCStack.count();
811}
812
813void SkCanvas::restoreToCount(int count) {
814 // sanity check
815 if (count < 1) {
816 count = 1;
817 }
818 while (fMCStack.count() > count) {
819 this->restore();
820 }
821}
822
823/////////////////////////////////////////////////////////////////////////////
824
825// can't draw it if its empty, or its too big for a fixed-point width or height
826static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000827 return bitmap.width() <= 0 || bitmap.height() <= 0
828#ifndef SK_ALLOW_OVER_32K_BITMAPS
829 || bitmap.width() > 32767 || bitmap.height() > 32767
830#endif
831 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832}
833
reed@android.comf2b98d62010-12-20 18:26:13 +0000834void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 const SkMatrix& matrix, const SkPaint* paint) {
836 if (reject_bitmap(bitmap)) {
837 return;
838 }
839
840 if (NULL == paint) {
841 SkPaint tmpPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000842 this->commonDrawBitmap(bitmap, srcRect, matrix, tmpPaint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 } else {
reed@android.comf2b98d62010-12-20 18:26:13 +0000844 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 }
846}
847
848void SkCanvas::drawDevice(SkDevice* device, int x, int y,
849 const SkPaint* paint) {
850 SkPaint tmp;
851 if (NULL == paint) {
852 tmp.setDither(true);
853 paint = &tmp;
854 }
reed@google.com4b226022011-01-11 18:32:13 +0000855
reed@google.com4e2b3d32011-04-07 14:18:59 +0000856 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857 while (iter.next()) {
858 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000859 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000861 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862}
863
864/////////////////////////////////////////////////////////////////////////////
865
866bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
867 fDeviceCMDirty = true;
868 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000869 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 return fMCRec->fMatrix->preTranslate(dx, dy);
871}
872
873bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
874 fDeviceCMDirty = true;
875 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000876 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 return fMCRec->fMatrix->preScale(sx, sy);
878}
879
880bool SkCanvas::rotate(SkScalar degrees) {
881 fDeviceCMDirty = true;
882 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000883 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884 return fMCRec->fMatrix->preRotate(degrees);
885}
886
887bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
888 fDeviceCMDirty = true;
889 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000890 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 return fMCRec->fMatrix->preSkew(sx, sy);
892}
893
894bool SkCanvas::concat(const SkMatrix& matrix) {
895 fDeviceCMDirty = true;
896 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000897 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000898 return fMCRec->fMatrix->preConcat(matrix);
899}
900
901void SkCanvas::setMatrix(const SkMatrix& matrix) {
902 fDeviceCMDirty = true;
903 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000904 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 *fMCRec->fMatrix = matrix;
906}
907
908// this is not virtual, so it must call a virtual method so that subclasses
909// will see its action
910void SkCanvas::resetMatrix() {
911 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000912
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 matrix.reset();
914 this->setMatrix(matrix);
915}
916
917//////////////////////////////////////////////////////////////////////////////
918
919bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000920 AutoValidateClip avc(this);
921
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 fDeviceCMDirty = true;
923 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000924 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000925
926 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000927 // for these simpler matrices, we can stay a rect ever after applying
928 // the matrix. This means we don't have to a) make a path, and b) tell
929 // the region code to scan-convert the path, only to discover that it
930 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 SkRect r;
932 SkIRect ir;
933
934 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000935 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 r.round(&ir);
937 return fMCRec->fRegion->op(ir, op);
938 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000939 // since we're rotate or some such thing, we convert the rect to a path
940 // and clip against that, since it can handle any matrix. However, to
941 // avoid recursion in the case where we are subclassed (e.g. Pictures)
942 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 SkPath path;
944
945 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000946 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000947 }
948}
949
reed@google.com819c9212011-02-23 18:56:55 +0000950static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
951 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000952 // base is used to limit the size (and therefore memory allocation) of the
953 // region that results from scan converting devPath.
954 SkRegion base;
955
reed@google.com819c9212011-02-23 18:56:55 +0000956 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000957 // since we are intersect, we can do better (tighter) with currRgn's
958 // bounds, than just using the device. However, if currRgn is complex,
959 // our region blitter may hork, so we do that case in two steps.
960 if (currRgn->isRect()) {
961 return currRgn->setPath(devPath, *currRgn);
962 } else {
963 base.setRect(currRgn->getBounds());
964 SkRegion rgn;
965 rgn.setPath(devPath, base);
966 return currRgn->op(rgn, op);
967 }
reed@google.com819c9212011-02-23 18:56:55 +0000968 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000969 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
970 base.setRect(0, 0, bm.width(), bm.height());
971
972 if (SkRegion::kReplace_Op == op) {
973 return currRgn->setPath(devPath, base);
974 } else {
975 SkRegion rgn;
976 rgn.setPath(devPath, base);
977 return currRgn->op(rgn, op);
978 }
979 }
980}
981
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000983 AutoValidateClip avc(this);
984
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 fDeviceCMDirty = true;
986 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000987 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988
989 SkPath devPath;
990 path.transform(*fMCRec->fMatrix, &devPath);
991
reed@google.com5c3d1472011-02-22 19:12:23 +0000992 // if we called path.swap() we could avoid a deep copy of this path
993 fClipStack.clipDevPath(devPath, op);
994
reed@google.com819c9212011-02-23 18:56:55 +0000995 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000996}
997
998bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000999 AutoValidateClip avc(this);
1000
reed@android.com8a1c16f2008-12-17 15:59:43 +00001001 fDeviceCMDirty = true;
1002 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001003 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004
reed@google.com5c3d1472011-02-22 19:12:23 +00001005 // todo: signal fClipStack that we have a region, and therefore (I guess)
1006 // we have to ignore it, and use the region directly?
1007 fClipStack.clipDevRect(rgn.getBounds());
1008
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 return fMCRec->fRegion->op(rgn, op);
1010}
1011
reed@google.com819c9212011-02-23 18:56:55 +00001012#ifdef SK_DEBUG
1013void SkCanvas::validateClip() const {
1014 // construct clipRgn from the clipstack
1015 const SkDevice* device = this->getDevice();
1016 SkIRect ir;
1017 ir.set(0, 0, device->width(), device->height());
1018 SkRegion clipRgn(ir);
1019
1020 SkClipStack::B2FIter iter(fClipStack);
1021 const SkClipStack::B2FIter::Clip* clip;
1022 while ((clip = iter.next()) != NULL) {
1023 if (clip->fPath) {
1024 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1025 } else if (clip->fRect) {
1026 clip->fRect->round(&ir);
1027 clipRgn.op(ir, clip->fOp);
1028 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001029 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001030 }
1031 }
1032
reed@google.com6f8f2922011-03-04 22:27:10 +00001033#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001034 // now compare against the current rgn
1035 const SkRegion& rgn = this->getTotalClip();
1036 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001037#endif
reed@google.com819c9212011-02-23 18:56:55 +00001038}
1039#endif
1040
reed@google.com5c3d1472011-02-22 19:12:23 +00001041///////////////////////////////////////////////////////////////////////////////
1042
reed@android.comba09de42010-02-05 20:46:05 +00001043void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001045 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1046 fLocalBoundsCompareTypeBW;
1047
1048 if (!this->getClipBounds(&r, et)) {
1049 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001051 rCompare.set(SkScalarToCompareType(r.fLeft),
1052 SkScalarToCompareType(r.fTop),
1053 SkScalarToCompareType(r.fRight),
1054 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001055 }
1056}
1057
reed@android.comd252db02009-04-01 18:31:44 +00001058/* current impl ignores edgetype, and relies on
1059 getLocalClipBoundsCompareType(), which always returns a value assuming
1060 antialiasing (worst case)
1061 */
reed@android.comba09de42010-02-05 20:46:05 +00001062bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001063
1064 if (!rect.hasValidCoordinates())
1065 return true;
1066
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067 if (fMCRec->fRegion->isEmpty()) {
1068 return true;
1069 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070
reed@android.coma380ae42009-07-21 01:17:02 +00001071 if (fMCRec->fMatrix->getType() & SkMatrix::kPerspective_Mask) {
1072 SkRect dst;
1073 fMCRec->fMatrix->mapRect(&dst, rect);
1074 SkIRect idst;
1075 dst.roundOut(&idst);
1076 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1077 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001078 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001079
reed@android.coma380ae42009-07-21 01:17:02 +00001080 // for speed, do the most likely reject compares first
1081 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1082 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1083 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1084 return true;
1085 }
1086 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1087 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1088 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1089 return true;
1090 }
1091 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093}
1094
1095bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001096 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097}
1098
1099bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1100 /* current impl ignores edgetype, and relies on
1101 getLocalClipBoundsCompareType(), which always returns a value assuming
1102 antialiasing (worst case)
1103 */
1104
1105 if (fMCRec->fRegion->isEmpty()) {
1106 return true;
1107 }
reed@google.com4b226022011-01-11 18:32:13 +00001108
reed@android.comaefd2bc2009-03-30 21:02:14 +00001109 SkScalarCompareType userT = SkScalarToCompareType(top);
1110 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001111
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001113 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114 if (userT >= userB) {
1115 return true;
1116 }
reed@google.com4b226022011-01-11 18:32:13 +00001117
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 // check if we are above or below the local clip bounds
1119 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1120 return userT >= clipR.fBottom || userB <= clipR.fTop;
1121}
1122
1123bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
1124 const SkRegion& clip = *fMCRec->fRegion;
1125 if (clip.isEmpty()) {
1126 if (bounds) {
1127 bounds->setEmpty();
1128 }
1129 return false;
1130 }
1131
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001132 SkMatrix inverse;
1133 // if we can't invert the CTM, we can't return local clip bounds
1134 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001135 if (bounds) {
1136 bounds->setEmpty();
1137 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001138 return false;
1139 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001141 if (NULL != bounds) {
1142 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 // get the clip's bounds
1144 const SkIRect& ibounds = clip.getBounds();
1145 // adjust it outwards if we are antialiasing
1146 int inset = (kAA_EdgeType == et);
1147 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1148 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@google.com4b226022011-01-11 18:32:13 +00001149
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 // invert into local coordinates
1151 inverse.mapRect(bounds, r);
1152 }
1153 return true;
1154}
1155
1156const SkMatrix& SkCanvas::getTotalMatrix() const {
1157 return *fMCRec->fMatrix;
1158}
1159
1160const SkRegion& SkCanvas::getTotalClip() const {
1161 return *fMCRec->fRegion;
1162}
1163
reed@google.com7d7ca792011-02-23 22:39:18 +00001164const SkClipStack& SkCanvas::getTotalClipStack() const {
1165 return fClipStack;
1166}
1167
reed@android.comf2b98d62010-12-20 18:26:13 +00001168void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1169 if (NULL == matrix || matrix->isIdentity()) {
1170 if (fUseExternalMatrix) {
1171 fDeviceCMDirty = true;
1172 }
1173 fUseExternalMatrix = false;
1174 } else {
1175 fUseExternalMatrix = true;
1176 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001177
reed@android.comf2b98d62010-12-20 18:26:13 +00001178 fExternalMatrix = *matrix;
1179 matrix->invert(&fExternalInverse);
1180 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001181}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182
reed@android.comf2b98d62010-12-20 18:26:13 +00001183SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width, int height,
1184 bool isOpaque, bool forLayer) {
1185 return fDeviceFactory->newDevice(this, config, width, height, isOpaque, forLayer);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186}
1187
1188//////////////////////////////////////////////////////////////////////////////
1189// These are the virtual drawing methods
1190//////////////////////////////////////////////////////////////////////////////
1191
1192void SkCanvas::drawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001193 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194
1195 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001196 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197 }
1198
reed@google.com4e2b3d32011-04-07 14:18:59 +00001199 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200}
1201
1202void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1203 const SkPaint& paint) {
1204 if ((long)count <= 0) {
1205 return;
1206 }
1207
1208 SkASSERT(pts != NULL);
1209
reed@google.com4e2b3d32011-04-07 14:18:59 +00001210 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001211
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001213 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214 }
reed@google.com4b226022011-01-11 18:32:13 +00001215
reed@google.com4e2b3d32011-04-07 14:18:59 +00001216 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217}
1218
1219void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1220 if (paint.canComputeFastBounds()) {
1221 SkRect storage;
1222 if (this->quickReject(paint.computeFastBounds(r, &storage),
1223 paint2EdgeType(&paint))) {
1224 return;
1225 }
1226 }
reed@google.com4b226022011-01-11 18:32:13 +00001227
reed@google.com4e2b3d32011-04-07 14:18:59 +00001228 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229
1230 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001231 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 }
1233
reed@google.com4e2b3d32011-04-07 14:18:59 +00001234 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235}
1236
1237void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1238 if (paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001239 SkRect storage;
1240 const SkRect& bounds = path.getBounds();
1241 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 paint2EdgeType(&paint))) {
1243 return;
1244 }
1245 }
1246
reed@google.com4e2b3d32011-04-07 14:18:59 +00001247 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248
1249 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001250 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 }
1252
reed@google.com4e2b3d32011-04-07 14:18:59 +00001253 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254}
1255
1256void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1257 const SkPaint* paint) {
1258 SkDEBUGCODE(bitmap.validate();)
1259
1260 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1261 SkRect fastBounds;
1262 fastBounds.set(x, y,
1263 x + SkIntToScalar(bitmap.width()),
1264 y + SkIntToScalar(bitmap.height()));
1265 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1266 return;
1267 }
1268 }
reed@google.com4b226022011-01-11 18:32:13 +00001269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 SkMatrix matrix;
1271 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001272 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273}
1274
1275void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1276 const SkRect& dst, const SkPaint* paint) {
1277 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1278 return;
1279 }
reed@google.com4b226022011-01-11 18:32:13 +00001280
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 // do this now, to avoid the cost of calling extract for RLE bitmaps
1282 if (this->quickReject(dst, paint2EdgeType(paint))) {
1283 return;
1284 }
reed@google.com4b226022011-01-11 18:32:13 +00001285
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com4b226022011-01-11 18:32:13 +00001287
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001289 SkRect tmpSrc;
1290 if (src) {
1291 tmpSrc.set(*src);
1292 // if the extract process clipped off the top or left of the
1293 // original, we adjust for that here to get the position right.
1294 if (tmpSrc.fLeft > 0) {
1295 tmpSrc.fRight -= tmpSrc.fLeft;
1296 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001297 }
reed@android.com87899992009-10-16 14:48:38 +00001298 if (tmpSrc.fTop > 0) {
1299 tmpSrc.fBottom -= tmpSrc.fTop;
1300 tmpSrc.fTop = 0;
1301 }
1302 } else {
1303 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1304 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 }
reed@android.com87899992009-10-16 14:48:38 +00001306 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@android.comf2b98d62010-12-20 18:26:13 +00001307
1308 // ensure that src is "valid" before we pass it to our internal routines
1309 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1310 SkIRect tmpISrc;
1311 if (src) {
1312 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001313 if (!tmpISrc.intersect(*src)) {
1314 return;
1315 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001316 src = &tmpISrc;
1317 }
1318 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319}
1320
1321void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1322 const SkPaint* paint) {
1323 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001324 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325}
1326
reed@android.comf2b98d62010-12-20 18:26:13 +00001327void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1328 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001330
reed@google.com4e2b3d32011-04-07 14:18:59 +00001331 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001332
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001334 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 }
reed@android.com9b039062009-02-11 15:09:58 +00001336
reed@google.com4e2b3d32011-04-07 14:18:59 +00001337 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338}
1339
1340void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1341 const SkPaint* paint) {
1342 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001343
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344 if (reject_bitmap(bitmap)) {
1345 return;
1346 }
reed@google.com4b226022011-01-11 18:32:13 +00001347
reed@android.com8a1c16f2008-12-17 15:59:43 +00001348 SkPaint tmp;
1349 if (NULL == paint) {
1350 paint = &tmp;
1351 }
reed@google.com4b226022011-01-11 18:32:13 +00001352
reed@google.com4e2b3d32011-04-07 14:18:59 +00001353 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001354
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355 while (iter.next()) {
1356 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001357 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001359 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360}
1361
reed@google.comf67e4cf2011-03-15 20:56:58 +00001362class SkDeviceFilteredPaint {
1363public:
1364 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1365 SkDevice::TextFlags flags;
1366 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001367 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001368 newPaint->setFlags(flags.fFlags);
1369 newPaint->setHinting(flags.fHinting);
1370 fPaint = newPaint;
1371 } else {
1372 fPaint = &paint;
1373 }
1374 }
1375
reed@google.comf67e4cf2011-03-15 20:56:58 +00001376 const SkPaint& paint() const { return *fPaint; }
1377
1378private:
reed@google.coma076e9b2011-04-06 20:17:29 +00001379 const SkPaint* fPaint;
1380 SkTLazy<SkPaint> fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001381};
1382
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383void SkCanvas::drawText(const void* text, size_t byteLength,
1384 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001385 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386
1387 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001388 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001389 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 }
1391
reed@google.com4e2b3d32011-04-07 14:18:59 +00001392 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393}
1394
1395void SkCanvas::drawPosText(const void* text, size_t byteLength,
1396 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001397 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001398
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001400 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001402 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 }
reed@google.com4b226022011-01-11 18:32:13 +00001404
reed@google.com4e2b3d32011-04-07 14:18:59 +00001405 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406}
1407
1408void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1409 const SkScalar xpos[], SkScalar constY,
1410 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001411 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001412
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001414 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001416 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417 }
reed@google.com4b226022011-01-11 18:32:13 +00001418
reed@google.com4e2b3d32011-04-07 14:18:59 +00001419 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420}
1421
1422void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1423 const SkPath& path, const SkMatrix* matrix,
1424 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001425 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426
1427 while (iter.next()) {
1428 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001429 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 }
1431
reed@google.com4e2b3d32011-04-07 14:18:59 +00001432 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433}
1434
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001435#ifdef ANDROID
1436void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1437 const SkPoint pos[], const SkPaint& paint,
1438 const SkPath& path, const SkMatrix* matrix) {
1439
reed@google.com4e2b3d32011-04-07 14:18:59 +00001440 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001441
1442 while (iter.next()) {
1443 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001444 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001445 }
1446
reed@google.com4e2b3d32011-04-07 14:18:59 +00001447 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001448}
1449#endif
1450
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1452 const SkPoint verts[], const SkPoint texs[],
1453 const SkColor colors[], SkXfermode* xmode,
1454 const uint16_t indices[], int indexCount,
1455 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001456 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 while (iter.next()) {
1459 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001460 colors, xmode, indices, indexCount,
1461 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462 }
reed@google.com4b226022011-01-11 18:32:13 +00001463
reed@google.com4e2b3d32011-04-07 14:18:59 +00001464 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465}
1466
reed@android.comcb608442009-12-04 21:32:27 +00001467void SkCanvas::drawData(const void* data, size_t length) {
1468 // do nothing. Subclasses may do something with the data
1469}
1470
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471//////////////////////////////////////////////////////////////////////////////
1472// These methods are NOT virtual, and therefore must call back into virtual
1473// methods, rather than actually drawing themselves.
1474//////////////////////////////////////////////////////////////////////////////
1475
1476void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001477 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 SkPaint paint;
1479
1480 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001481 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001482 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 }
1484 this->drawPaint(paint);
1485}
1486
reed@android.com845fdac2009-06-23 03:01:32 +00001487void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 SkPaint paint;
1489
1490 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001491 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001492 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493 }
1494 this->drawPaint(paint);
1495}
1496
1497void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1498 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001499
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500 pt.set(x, y);
1501 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1502}
1503
1504void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1505 SkPoint pt;
1506 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001507
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508 pt.set(x, y);
1509 paint.setColor(color);
1510 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1511}
1512
1513void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1514 const SkPaint& paint) {
1515 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001516
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517 pts[0].set(x0, y0);
1518 pts[1].set(x1, y1);
1519 this->drawPoints(kLines_PointMode, 2, pts, paint);
1520}
1521
1522void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1523 SkScalar right, SkScalar bottom,
1524 const SkPaint& paint) {
1525 SkRect r;
1526
1527 r.set(left, top, right, bottom);
1528 this->drawRect(r, paint);
1529}
1530
1531void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1532 const SkPaint& paint) {
1533 if (radius < 0) {
1534 radius = 0;
1535 }
1536
1537 SkRect r;
1538 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001539
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540 if (paint.canComputeFastBounds()) {
1541 SkRect storage;
1542 if (this->quickReject(paint.computeFastBounds(r, &storage),
1543 paint2EdgeType(&paint))) {
1544 return;
1545 }
1546 }
reed@google.com4b226022011-01-11 18:32:13 +00001547
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548 SkPath path;
1549 path.addOval(r);
1550 this->drawPath(path, paint);
1551}
1552
1553void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1554 const SkPaint& paint) {
1555 if (rx > 0 && ry > 0) {
1556 if (paint.canComputeFastBounds()) {
1557 SkRect storage;
1558 if (this->quickReject(paint.computeFastBounds(r, &storage),
1559 paint2EdgeType(&paint))) {
1560 return;
1561 }
1562 }
1563
1564 SkPath path;
1565 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1566 this->drawPath(path, paint);
1567 } else {
1568 this->drawRect(r, paint);
1569 }
1570}
1571
1572void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1573 if (paint.canComputeFastBounds()) {
1574 SkRect storage;
1575 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1576 paint2EdgeType(&paint))) {
1577 return;
1578 }
1579 }
1580
1581 SkPath path;
1582 path.addOval(oval);
1583 this->drawPath(path, paint);
1584}
1585
1586void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1587 SkScalar sweepAngle, bool useCenter,
1588 const SkPaint& paint) {
1589 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1590 this->drawOval(oval, paint);
1591 } else {
1592 SkPath path;
1593 if (useCenter) {
1594 path.moveTo(oval.centerX(), oval.centerY());
1595 }
1596 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1597 if (useCenter) {
1598 path.close();
1599 }
1600 this->drawPath(path, paint);
1601 }
1602}
1603
1604void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1605 const SkPath& path, SkScalar hOffset,
1606 SkScalar vOffset, const SkPaint& paint) {
1607 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001608
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609 matrix.setTranslate(hOffset, vOffset);
1610 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1611}
1612
reed@android.comf76bacf2009-05-13 14:00:33 +00001613///////////////////////////////////////////////////////////////////////////////
1614
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615void SkCanvas::drawPicture(SkPicture& picture) {
1616 int saveCount = save();
1617 picture.draw(this);
1618 restoreToCount(saveCount);
1619}
1620
reed@android.comf76bacf2009-05-13 14:00:33 +00001621void SkCanvas::drawShape(SkShape* shape) {
1622 // shape baseclass takes care of save/restore
1623 shape->draw(this);
1624}
1625
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626///////////////////////////////////////////////////////////////////////////////
1627///////////////////////////////////////////////////////////////////////////////
1628
1629SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001630 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631
1632 SkASSERT(canvas);
1633
1634 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1635 fDone = !fImpl->next();
1636}
1637
1638SkCanvas::LayerIter::~LayerIter() {
1639 fImpl->~SkDrawIter();
1640}
1641
1642void SkCanvas::LayerIter::next() {
1643 fDone = !fImpl->next();
1644}
1645
1646SkDevice* SkCanvas::LayerIter::device() const {
1647 return fImpl->getDevice();
1648}
1649
1650const SkMatrix& SkCanvas::LayerIter::matrix() const {
1651 return fImpl->getMatrix();
1652}
1653
1654const SkPaint& SkCanvas::LayerIter::paint() const {
1655 const SkPaint* paint = fImpl->getPaint();
1656 if (NULL == paint) {
1657 paint = &fDefaultPaint;
1658 }
1659 return *paint;
1660}
1661
1662const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1663int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1664int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
1665