blob: 70b53fe9949bd8782057b34c15dc83e0ccc870c2 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
11#include "SkBounder.h"
12#include "SkDevice.h"
13#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
16#include "SkPicture.h"
17#include "SkScalarCompare.h"
18#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000019#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000020#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022
23//#define SK_TRACE_SAVERESTORE
24
25#ifdef SK_TRACE_SAVERESTORE
26 static int gLayerCounter;
27 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
28 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
29
30 static int gRecCounter;
31 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
32 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
33
34 static int gCanvasCounter;
35 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
36 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
37#else
38 #define inc_layer()
39 #define dec_layer()
40 #define inc_rec()
41 #define dec_rec()
42 #define inc_canvas()
43 #define dec_canvas()
44#endif
45
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000046typedef SkTLazy<SkPaint> SkLazyPaint;
47
reed@android.com8a1c16f2008-12-17 15:59:43 +000048///////////////////////////////////////////////////////////////////////////////
49// Helpers for computing fast bounds for quickReject tests
50
51static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
52 return paint != NULL && paint->isAntiAlias() ?
53 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
54}
55
reed@google.com675dd882011-10-04 15:37:22 +000056#if 0
57 #define CHECK_NOTHING_TO_DRAW(paint) \
58 do { if ((paint).nothingToDraw()) return; } while (0)
59#else
60 #define CHECK_NOTHING_TO_DRAW(paint) do {} while (0)
61#endif
reed@google.comdcd0f3a2011-10-04 01:17:15 +000062
reed@android.com8a1c16f2008-12-17 15:59:43 +000063///////////////////////////////////////////////////////////////////////////////
64
65/* This is the record we keep for each SkDevice that the user installs.
66 The clip/matrix/proc are fields that reflect the top of the save/restore
67 stack. Whenever the canvas changes, it marks a dirty flag, and then before
68 these are used (assuming we're not on a layer) we rebuild these cache
69 values: they reflect the top of the save stack, but translated and clipped
70 by the device's XY offset and bitmap-bounds.
71*/
72struct DeviceCM {
73 DeviceCM* fNext;
74 SkDevice* fDevice;
75 SkRegion fClip;
76 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000077 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000078 // optional, related to canvas' external matrix
79 const SkMatrix* fMVMatrix;
80 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
bungeman@google.com88edf1e2011-08-08 19:41:56 +000082 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 : fNext(NULL) {
84 if (NULL != device) {
85 device->ref();
86 device->lockPixels();
87 }
reed@google.com4b226022011-01-11 18:32:13 +000088 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000090 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000091
bungeman@google.com88edf1e2011-08-08 19:41:56 +000092 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 if (NULL != fDevice) {
94 fDevice->unlockPixels();
95 fDevice->unref();
96 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000097 SkDELETE(fPaint);
98 }
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +0000101 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000102 int x = fDevice->getOrigin().x();
103 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 int width = fDevice->width();
105 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 if ((x | y) == 0) {
108 fMatrix = &totalMatrix;
109 fClip = totalClip;
110 } else {
111 fMatrixStorage = totalMatrix;
112 fMatrixStorage.postTranslate(SkIntToScalar(-x),
113 SkIntToScalar(-y));
114 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 totalClip.translate(-x, -y, &fClip);
117 }
118
119 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
120
121 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000122
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 if (updateClip) {
124 updateClip->op(x, y, x + width, y + height,
125 SkRegion::kDifference_Op);
126 }
reed@google.com4b226022011-01-11 18:32:13 +0000127
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000128 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129
130#ifdef SK_DEBUG
131 if (!fClip.isEmpty()) {
132 SkIRect deviceR;
133 deviceR.set(0, 0, width, height);
134 SkASSERT(deviceR.contains(fClip.getBounds()));
135 }
136#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000137 // default is to assume no external matrix
138 fMVMatrix = NULL;
139 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000141
142 // can only be called after calling updateMC()
143 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
144 fMVMatrixStorage.setConcat(extI, *fMatrix);
145 fMVMatrix = &fMVMatrixStorage;
146 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
147 }
148
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000150 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151};
152
153/* This is the record we keep for each save/restore level in the stack.
154 Since a level optionally copies the matrix and/or stack, we have pointers
155 for these fields. If the value is copied for this level, the copy is
156 stored in the ...Storage field, and the pointer points to that. If the
157 value is not copied for this level, we ignore ...Storage, and just point
158 at the corresponding value in the previous level in the stack.
159*/
160class SkCanvas::MCRec {
161public:
162 MCRec* fNext;
163 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
164 SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
165 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 DeviceCM* fLayer;
168 /* If there are any layers in the stack, this points to the top-most
169 one that is at or below this level in the stack (so we know what
170 bitmap/device to draw into from this level. This value is NOT
171 reference counted, since the real owner is either our fLayer field,
172 or a previous one in a lower level.)
173 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000174 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175
176 MCRec(const MCRec* prev, int flags) {
177 if (NULL != prev) {
178 if (flags & SkCanvas::kMatrix_SaveFlag) {
179 fMatrixStorage = *prev->fMatrix;
180 fMatrix = &fMatrixStorage;
181 } else {
182 fMatrix = prev->fMatrix;
183 }
reed@google.com4b226022011-01-11 18:32:13 +0000184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 if (flags & SkCanvas::kClip_SaveFlag) {
186 fRegionStorage = *prev->fRegion;
187 fRegion = &fRegionStorage;
188 } else {
189 fRegion = prev->fRegion;
190 }
191
192 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000193 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
195 fTopLayer = prev->fTopLayer;
196 } else { // no prev
197 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000198
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 fMatrix = &fMatrixStorage;
200 fRegion = &fRegionStorage;
201 fFilter = NULL;
202 fTopLayer = NULL;
203 }
204 fLayer = NULL;
205
206 // don't bother initializing fNext
207 inc_rec();
208 }
209 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000210 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 SkDELETE(fLayer);
212 dec_rec();
213 }
reed@google.com4b226022011-01-11 18:32:13 +0000214
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215private:
216 SkMatrix fMatrixStorage;
217 SkRegion fRegionStorage;
218};
219
220class SkDrawIter : public SkDraw {
221public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000222 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
223 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 canvas->updateDeviceCMCache();
225
reed@google.com7d7ca792011-02-23 22:39:18 +0000226 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 fBounder = canvas->getBounder();
228 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000229 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 }
reed@google.com4b226022011-01-11 18:32:13 +0000231
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 bool next() {
233 // skip over recs with empty clips
234 if (fSkipEmptyClips) {
235 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
236 fCurrLayer = fCurrLayer->fNext;
237 }
238 }
239
240 if (NULL != fCurrLayer) {
241 const DeviceCM* rec = fCurrLayer;
242
243 fMatrix = rec->fMatrix;
244 fClip = &rec->fClip;
245 fDevice = rec->fDevice;
246 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000248 fMVMatrix = rec->fMVMatrix;
249 fExtMatrix = rec->fExtMatrix;
250 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251
252 fCurrLayer = rec->fNext;
253 if (fBounder) {
254 fBounder->setClip(fClip);
255 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000257
bsalomon@google.comd302f142011-03-03 13:54:13 +0000258 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 return true;
260 }
261 return false;
262 }
reed@google.com4b226022011-01-11 18:32:13 +0000263
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000265 int getX() const { return fDevice->getOrigin().x(); }
266 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 const SkMatrix& getMatrix() const { return *fMatrix; }
268 const SkRegion& getClip() const { return *fClip; }
269 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000270
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271private:
272 SkCanvas* fCanvas;
273 const DeviceCM* fCurrLayer;
274 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 SkBool8 fSkipEmptyClips;
276
277 typedef SkDraw INHERITED;
278};
279
280/////////////////////////////////////////////////////////////////////////////
281
282class AutoDrawLooper {
283public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000284 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
285 fCanvas = canvas;
286 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 fPaint = NULL;
289 fSaveCount = canvas->getSaveCount();
290 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291
reed@google.com4e2b3d32011-04-07 14:18:59 +0000292 if (fLooper) {
293 fLooper->init(canvas);
294 }
295 }
296
reed@android.com8a1c16f2008-12-17 15:59:43 +0000297 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000298 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000300
301 const SkPaint& paint() const {
302 SkASSERT(fPaint);
303 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000305
306 bool next(SkDrawFilter::Type drawType);
307
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000309 SkLazyPaint fLazyPaint;
310 SkCanvas* fCanvas;
311 const SkPaint& fOrigPaint;
312 SkDrawLooper* fLooper;
313 SkDrawFilter* fFilter;
314 const SkPaint* fPaint;
315 int fSaveCount;
316 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317};
318
reed@google.com4e2b3d32011-04-07 14:18:59 +0000319bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
320 if (fDone) {
321 fPaint = NULL;
322 return false;
323 }
324 if (!fLooper && !fFilter) {
325 fDone = true;
326 fPaint = &fOrigPaint;
327 return true;
328 }
329
330 SkPaint* paint = fLazyPaint.set(fOrigPaint);
331 if (fLooper && !fLooper->next(fCanvas, paint)) {
332 fDone = true;
333 fPaint = NULL;
334 return false;
335 }
336 if (fFilter) {
337 fFilter->filter(paint, drawType);
mike@reedtribe.org53e3bed2011-04-08 00:37:03 +0000338 if (NULL == fLooper) {
339 // no looper means we only draw once
340 fDone = true;
341 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 }
343 fPaint = paint;
344 return true;
345}
346
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347/* Stack helper for managing a SkBounder. In the destructor, if we were
348 given a bounder, we call its commit() method, signifying that we are
349 done accumulating bounds for that draw.
350*/
351class SkAutoBounderCommit {
352public:
353 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
354 ~SkAutoBounderCommit() {
355 if (NULL != fBounder) {
356 fBounder->commit();
357 }
358 }
359private:
360 SkBounder* fBounder;
361};
362
363#include "SkColorPriv.h"
364
365class AutoValidator {
366public:
367 AutoValidator(SkDevice* device) : fDevice(device) {}
368 ~AutoValidator() {
369#ifdef SK_DEBUG
370 const SkBitmap& bm = fDevice->accessBitmap(false);
371 if (bm.config() == SkBitmap::kARGB_4444_Config) {
372 for (int y = 0; y < bm.height(); y++) {
373 const SkPMColor16* p = bm.getAddr16(0, y);
374 for (int x = 0; x < bm.width(); x++) {
375 SkPMColor16 c = p[x];
376 SkPMColor16Assert(c);
377 }
378 }
379 }
380#endif
381 }
382private:
383 SkDevice* fDevice;
384};
385
386////////// macros to place around the internal draw calls //////////////////
387
reed@google.com4e2b3d32011-04-07 14:18:59 +0000388#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000389/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000390 AutoDrawLooper looper(this, paint); \
391 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392 SkAutoBounderCommit ac(fBounder); \
393 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000394
reed@google.com4e2b3d32011-04-07 14:18:59 +0000395#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396
397////////////////////////////////////////////////////////////////////////////
398
399SkDevice* SkCanvas::init(SkDevice* device) {
400 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000401 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000403 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000404 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000405 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000406 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407
408 fMCRec = (MCRec*)fMCStack.push_back();
409 new (fMCRec) MCRec(NULL, 0);
410
411 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
412 fMCRec->fTopLayer = fMCRec->fLayer;
413 fMCRec->fNext = NULL;
414
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000415 fExternalMatrix.reset();
416 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000417 fUseExternalMatrix = false;
418
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 return this->setDevice(device);
420}
421
reed@google.comcde92112011-07-06 20:00:52 +0000422SkCanvas::SkCanvas()
423: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000424 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000425
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000426 this->init(NULL);
427}
428
reed@android.com8a1c16f2008-12-17 15:59:43 +0000429SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000430 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 inc_canvas();
432
433 this->init(device);
434}
435
436SkCanvas::SkCanvas(const SkBitmap& bitmap)
437 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
438 inc_canvas();
439
reed@google.comcde92112011-07-06 20:00:52 +0000440 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441}
442
443SkCanvas::~SkCanvas() {
444 // free up the contents of our deque
445 this->restoreToCount(1); // restore everything but the last
446 this->internalRestore(); // restore the last, since we're going away
447
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000448 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 dec_canvas();
451}
452
453SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
454 SkRefCnt_SafeAssign(fBounder, bounder);
455 return bounder;
456}
457
458SkDrawFilter* SkCanvas::getDrawFilter() const {
459 return fMCRec->fFilter;
460}
461
462SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
463 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
464 return filter;
465}
466
467///////////////////////////////////////////////////////////////////////////////
468
469SkDevice* SkCanvas::getDevice() const {
470 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000471 SkDeque::F2BIter iter(fMCStack);
472 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473 SkASSERT(rec && rec->fLayer);
474 return rec->fLayer->fDevice;
475}
476
reed@google.com9266fed2011-03-30 00:18:03 +0000477SkDevice* SkCanvas::getTopDevice() const {
478 return fMCRec->fTopLayer->fDevice;
479}
480
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481SkDevice* SkCanvas::setDevice(SkDevice* device) {
482 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000483 SkDeque::F2BIter iter(fMCStack);
484 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 SkASSERT(rec && rec->fLayer);
486 SkDevice* rootDevice = rec->fLayer->fDevice;
487
488 if (rootDevice == device) {
489 return device;
490 }
reed@google.com4b226022011-01-11 18:32:13 +0000491
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 /* Notify the devices that they are going in/out of scope, so they can do
493 things like lock/unlock their pixels, etc.
494 */
495 if (device) {
496 device->lockPixels();
497 }
498 if (rootDevice) {
499 rootDevice->unlockPixels();
500 }
501
502 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
503 rootDevice = device;
504
505 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 /* Now we update our initial region to have the bounds of the new device,
508 and then intersect all of the clips in our stack with these bounds,
509 to ensure that we can't draw outside of the device's bounds (and trash
510 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 NOTE: this is only a partial-fix, since if the new device is larger than
513 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000514 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
516 reconstruct the correct clips, so this approximation will have to do.
517 The caller really needs to restore() back to the base if they want to
518 accurately take advantage of the new device bounds.
519 */
520
521 if (NULL == device) {
522 rec->fRegion->setEmpty();
523 while ((rec = (MCRec*)iter.next()) != NULL) {
524 (void)rec->fRegion->setEmpty();
525 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000526 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 } else {
528 // compute our total bounds for all devices
529 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000530
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 bounds.set(0, 0, device->width(), device->height());
532
533 // now jam our 1st clip to be bounds, and intersect the rest with that
534 rec->fRegion->setRect(bounds);
535 while ((rec = (MCRec*)iter.next()) != NULL) {
536 (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
537 }
538 }
539 return device;
540}
541
reed@google.comaf951c92011-06-16 19:10:39 +0000542SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
543 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
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 +0000556//////////////////////////////////////////////////////////////////////////////
557
reed@google.com51df9e32010-12-23 19:29:18 +0000558bool SkCanvas::readPixels(SkBitmap* bitmap) {
559 SkDevice* device = this->getDevice();
560 if (!device) {
561 return false;
562 }
563 SkIRect bounds;
564 bounds.set(0, 0, device->width(), device->height());
565 return this->readPixels(bounds, bitmap);
566}
567
568void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
569 SkDevice* device = this->getDevice();
570 if (device) {
571 device->writePixels(bitmap, x, y);
572 }
573}
574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575//////////////////////////////////////////////////////////////////////////////
576
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577void SkCanvas::updateDeviceCMCache() {
578 if (fDeviceCMDirty) {
579 const SkMatrix& totalMatrix = this->getTotalMatrix();
580 const SkRegion& totalClip = this->getTotalClip();
581 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000582
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000584 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000585 if (fUseExternalMatrix) {
586 layer->updateExternalMatrix(fExternalMatrix,
587 fExternalInverse);
588 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 } else {
590 SkRegion clip;
591 clip = totalClip; // make a copy
592 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000593 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000594 if (fUseExternalMatrix) {
595 layer->updateExternalMatrix(fExternalMatrix,
596 fExternalInverse);
597 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 } while ((layer = layer->fNext) != NULL);
599 }
600 fDeviceCMDirty = false;
601 }
602}
603
reed@android.comf2b98d62010-12-20 18:26:13 +0000604void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000605 const SkRegion& clip,
606 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000608 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000609 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000610 fLastDeviceToGainFocus = device;
611 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612}
613
614///////////////////////////////////////////////////////////////////////////////
615
616int SkCanvas::internalSave(SaveFlags flags) {
617 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000618
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 MCRec* newTop = (MCRec*)fMCStack.push_back();
620 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000621
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 newTop->fNext = fMCRec;
623 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000624
reed@google.com5c3d1472011-02-22 19:12:23 +0000625 fClipStack.save();
626 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
627
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 return saveCount;
629}
630
631int SkCanvas::save(SaveFlags flags) {
632 // call shared impl
633 return this->internalSave(flags);
634}
635
636#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
637#define C16MASK (1 << SkBitmap::kRGB_565_Config)
638#define C8MASK (1 << SkBitmap::kA8_Config)
639
640static SkBitmap::Config resolve_config(SkCanvas* canvas,
641 const SkIRect& bounds,
642 SkCanvas::SaveFlags flags,
643 bool* isOpaque) {
644 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
645
646#if 0
647 // loop through and union all the configs we may draw into
648 uint32_t configMask = 0;
649 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
650 {
651 SkDevice* device = canvas->getLayerDevice(i);
652 if (device->intersects(bounds))
653 configMask |= 1 << device->config();
654 }
655
656 // if the caller wants alpha or fullcolor, we can't return 565
657 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
658 SkCanvas::kHasAlphaLayer_SaveFlag))
659 configMask &= ~C16MASK;
660
661 switch (configMask) {
662 case C8MASK: // if we only have A8, return that
663 return SkBitmap::kA8_Config;
664
665 case C16MASK: // if we only have 565, return that
666 return SkBitmap::kRGB_565_Config;
667
668 default:
669 return SkBitmap::kARGB_8888_Config; // default answer
670 }
671#else
672 return SkBitmap::kARGB_8888_Config; // default answer
673#endif
674}
675
676static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
677 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
678}
679
680int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
681 SaveFlags flags) {
682 // do this before we create the layer. We don't call the public save() since
683 // that would invoke a possibly overridden virtual
684 int count = this->internalSave(flags);
685
686 fDeviceCMDirty = true;
687
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000688 SkIRect clipBounds;
689 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000690 return count;
691 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000693 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694 if (NULL != bounds) {
695 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697 this->getTotalMatrix().mapRect(&r, *bounds);
698 r.roundOut(&ir);
699 // early exit if the layer's bounds are clipped out
700 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000701 if (bounds_affects_clip(flags)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000702 fMCRec->fRegion->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000703 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000704 return count;
705 }
706 } else { // no user bounds, so just use the clip
707 ir = clipBounds;
708 }
709
reed@google.com5c3d1472011-02-22 19:12:23 +0000710 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711 // early exit if the clip is now empty
712 if (bounds_affects_clip(flags) &&
713 !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
714 return count;
715 }
716
717 bool isOpaque;
718 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
719
bsalomon@google.come97f0852011-06-17 13:10:25 +0000720 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
721 isOpaque);
bungeman@google.come25c6842011-08-17 14:53:54 +0000722 if (NULL == device) {
723 SkDebugf("Unable to create device for layer.");
724 return count;
725 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000726
reed@google.com6f8f2922011-03-04 22:27:10 +0000727 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
729 device->unref();
730
731 layer->fNext = fMCRec->fTopLayer;
732 fMCRec->fLayer = layer;
733 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
734
735 return count;
736}
737
738int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
739 SaveFlags flags) {
740 if (0xFF == alpha) {
741 return this->saveLayer(bounds, NULL, flags);
742 } else {
743 SkPaint tmpPaint;
744 tmpPaint.setAlpha(alpha);
745 return this->saveLayer(bounds, &tmpPaint, flags);
746 }
747}
748
749void SkCanvas::restore() {
750 // check for underflow
751 if (fMCStack.count() > 1) {
752 this->internalRestore();
753 }
754}
755
756void SkCanvas::internalRestore() {
757 SkASSERT(fMCStack.count() != 0);
758
759 fDeviceCMDirty = true;
760 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000761 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762
reed@google.com5c3d1472011-02-22 19:12:23 +0000763 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000764 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 DeviceCM* layer = fMCRec->fLayer; // may be null
766 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
767 fMCRec->fLayer = NULL;
768
769 // now do the normal restore()
770 fMCRec->~MCRec(); // balanced in save()
771 fMCStack.pop_back();
772 fMCRec = (MCRec*)fMCStack.back();
773
774 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
775 since if we're being recorded, we don't want to record this (the
776 recorder will have already recorded the restore).
777 */
778 if (NULL != layer) {
779 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000780 const SkIPoint& origin = layer->fDevice->getOrigin();
781 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 layer->fPaint);
783 // reset this, since drawDevice will have set it to true
784 fDeviceCMDirty = true;
785 }
786 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000787 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000788
789 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790}
791
792int SkCanvas::getSaveCount() const {
793 return fMCStack.count();
794}
795
796void SkCanvas::restoreToCount(int count) {
797 // sanity check
798 if (count < 1) {
799 count = 1;
800 }
801 while (fMCStack.count() > count) {
802 this->restore();
803 }
804}
805
806/////////////////////////////////////////////////////////////////////////////
807
808// can't draw it if its empty, or its too big for a fixed-point width or height
809static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000810 return bitmap.width() <= 0 || bitmap.height() <= 0
811#ifndef SK_ALLOW_OVER_32K_BITMAPS
812 || bitmap.width() > 32767 || bitmap.height() > 32767
813#endif
814 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815}
816
reed@android.comf2b98d62010-12-20 18:26:13 +0000817void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 const SkMatrix& matrix, const SkPaint* paint) {
819 if (reject_bitmap(bitmap)) {
820 return;
821 }
822
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000823 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000825 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000827 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828}
829
830void SkCanvas::drawDevice(SkDevice* device, int x, int y,
831 const SkPaint* paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +0000832 if (paint) {
833 CHECK_NOTHING_TO_DRAW(*paint);
834 }
835
reed@android.com8a1c16f2008-12-17 15:59:43 +0000836 SkPaint tmp;
837 if (NULL == paint) {
838 tmp.setDither(true);
839 paint = &tmp;
840 }
reed@google.com4b226022011-01-11 18:32:13 +0000841
reed@google.com4e2b3d32011-04-07 14:18:59 +0000842 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 while (iter.next()) {
844 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000845 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000847 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000848}
849
850/////////////////////////////////////////////////////////////////////////////
851
852bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
853 fDeviceCMDirty = true;
854 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000855 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 return fMCRec->fMatrix->preTranslate(dx, dy);
857}
858
859bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
860 fDeviceCMDirty = true;
861 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000862 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000863 return fMCRec->fMatrix->preScale(sx, sy);
864}
865
866bool SkCanvas::rotate(SkScalar degrees) {
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->preRotate(degrees);
871}
872
873bool SkCanvas::skew(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->preSkew(sx, sy);
878}
879
880bool SkCanvas::concat(const SkMatrix& matrix) {
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->preConcat(matrix);
885}
886
887void SkCanvas::setMatrix(const SkMatrix& matrix) {
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 *fMCRec->fMatrix = matrix;
892}
893
894// this is not virtual, so it must call a virtual method so that subclasses
895// will see its action
896void SkCanvas::resetMatrix() {
897 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000898
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 matrix.reset();
900 this->setMatrix(matrix);
901}
902
903//////////////////////////////////////////////////////////////////////////////
904
905bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000906 AutoValidateClip avc(this);
907
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 fDeviceCMDirty = true;
909 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000910 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911
912 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000913 // for these simpler matrices, we can stay a rect ever after applying
914 // the matrix. This means we don't have to a) make a path, and b) tell
915 // the region code to scan-convert the path, only to discover that it
916 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 SkRect r;
918 SkIRect ir;
919
920 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com5c3d1472011-02-22 19:12:23 +0000921 fClipStack.clipDevRect(r, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 r.round(&ir);
923 return fMCRec->fRegion->op(ir, op);
924 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000925 // since we're rotate or some such thing, we convert the rect to a path
926 // and clip against that, since it can handle any matrix. However, to
927 // avoid recursion in the case where we are subclassed (e.g. Pictures)
928 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 SkPath path;
930
931 path.addRect(rect);
reed@android.com98de2bd2009-03-02 19:41:36 +0000932 return this->SkCanvas::clipPath(path, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 }
934}
935
reed@google.com819c9212011-02-23 18:56:55 +0000936static bool clipPathHelper(const SkCanvas* canvas, SkRegion* currRgn,
937 const SkPath& devPath, SkRegion::Op op) {
reed@google.com759876a2011-03-03 14:32:51 +0000938 // base is used to limit the size (and therefore memory allocation) of the
939 // region that results from scan converting devPath.
940 SkRegion base;
941
reed@google.com819c9212011-02-23 18:56:55 +0000942 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000943 // since we are intersect, we can do better (tighter) with currRgn's
944 // bounds, than just using the device. However, if currRgn is complex,
945 // our region blitter may hork, so we do that case in two steps.
946 if (currRgn->isRect()) {
947 return currRgn->setPath(devPath, *currRgn);
948 } else {
949 base.setRect(currRgn->getBounds());
950 SkRegion rgn;
951 rgn.setPath(devPath, base);
952 return currRgn->op(rgn, op);
953 }
reed@google.com819c9212011-02-23 18:56:55 +0000954 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000955 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
956 base.setRect(0, 0, bm.width(), bm.height());
957
958 if (SkRegion::kReplace_Op == op) {
959 return currRgn->setPath(devPath, base);
960 } else {
961 SkRegion rgn;
962 rgn.setPath(devPath, base);
963 return currRgn->op(rgn, op);
964 }
965 }
966}
967
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000969 AutoValidateClip avc(this);
970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 fDeviceCMDirty = true;
972 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000973 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974
975 SkPath devPath;
976 path.transform(*fMCRec->fMatrix, &devPath);
977
reed@google.com5c3d1472011-02-22 19:12:23 +0000978 // if we called path.swap() we could avoid a deep copy of this path
979 fClipStack.clipDevPath(devPath, op);
980
reed@google.com819c9212011-02-23 18:56:55 +0000981 return clipPathHelper(this, fMCRec->fRegion, devPath, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982}
983
984bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000985 AutoValidateClip avc(this);
986
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 fDeviceCMDirty = true;
988 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000989 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990
reed@google.com5c3d1472011-02-22 19:12:23 +0000991 // todo: signal fClipStack that we have a region, and therefore (I guess)
992 // we have to ignore it, and use the region directly?
993 fClipStack.clipDevRect(rgn.getBounds());
994
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995 return fMCRec->fRegion->op(rgn, op);
996}
997
reed@google.com819c9212011-02-23 18:56:55 +0000998#ifdef SK_DEBUG
999void SkCanvas::validateClip() const {
1000 // construct clipRgn from the clipstack
1001 const SkDevice* device = this->getDevice();
1002 SkIRect ir;
1003 ir.set(0, 0, device->width(), device->height());
1004 SkRegion clipRgn(ir);
1005
1006 SkClipStack::B2FIter iter(fClipStack);
1007 const SkClipStack::B2FIter::Clip* clip;
1008 while ((clip = iter.next()) != NULL) {
1009 if (clip->fPath) {
1010 clipPathHelper(this, &clipRgn, *clip->fPath, clip->fOp);
1011 } else if (clip->fRect) {
1012 clip->fRect->round(&ir);
1013 clipRgn.op(ir, clip->fOp);
1014 } else {
reed@google.comdca7acb2011-02-23 21:47:37 +00001015 clipRgn.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001016 }
1017 }
1018
reed@google.com6f8f2922011-03-04 22:27:10 +00001019#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001020 // now compare against the current rgn
1021 const SkRegion& rgn = this->getTotalClip();
1022 SkASSERT(rgn == clipRgn);
reed@google.com02878b82011-02-24 13:04:42 +00001023#endif
reed@google.com819c9212011-02-23 18:56:55 +00001024}
1025#endif
1026
reed@google.com5c3d1472011-02-22 19:12:23 +00001027///////////////////////////////////////////////////////////////////////////////
1028
reed@android.comba09de42010-02-05 20:46:05 +00001029void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001031 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1032 fLocalBoundsCompareTypeBW;
1033
1034 if (!this->getClipBounds(&r, et)) {
1035 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001036 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001037 rCompare.set(SkScalarToCompareType(r.fLeft),
1038 SkScalarToCompareType(r.fTop),
1039 SkScalarToCompareType(r.fRight),
1040 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 }
1042}
1043
reed@android.comd252db02009-04-01 18:31:44 +00001044/* current impl ignores edgetype, and relies on
1045 getLocalClipBoundsCompareType(), which always returns a value assuming
1046 antialiasing (worst case)
1047 */
reed@android.comba09de42010-02-05 20:46:05 +00001048bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001049
1050 if (!rect.hasValidCoordinates())
1051 return true;
1052
reed@android.com8a1c16f2008-12-17 15:59:43 +00001053 if (fMCRec->fRegion->isEmpty()) {
1054 return true;
1055 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056
tomhudson@google.com8d430182011-06-06 19:11:19 +00001057 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001058 SkRect dst;
1059 fMCRec->fMatrix->mapRect(&dst, rect);
1060 SkIRect idst;
1061 dst.roundOut(&idst);
1062 return !SkIRect::Intersects(idst, fMCRec->fRegion->getBounds());
1063 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001064 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001065
reed@android.coma380ae42009-07-21 01:17:02 +00001066 // for speed, do the most likely reject compares first
1067 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1068 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1069 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1070 return true;
1071 }
1072 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1073 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1074 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1075 return true;
1076 }
1077 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001079}
1080
1081bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001082 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001083}
1084
1085bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1086 /* current impl ignores edgetype, and relies on
1087 getLocalClipBoundsCompareType(), which always returns a value assuming
1088 antialiasing (worst case)
1089 */
1090
1091 if (fMCRec->fRegion->isEmpty()) {
1092 return true;
1093 }
reed@google.com4b226022011-01-11 18:32:13 +00001094
reed@android.comaefd2bc2009-03-30 21:02:14 +00001095 SkScalarCompareType userT = SkScalarToCompareType(top);
1096 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001097
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001099 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 if (userT >= userB) {
1101 return true;
1102 }
reed@google.com4b226022011-01-11 18:32:13 +00001103
reed@android.com8a1c16f2008-12-17 15:59:43 +00001104 // check if we are above or below the local clip bounds
1105 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1106 return userT >= clipR.fBottom || userB <= clipR.fTop;
1107}
1108
1109bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001110 SkIRect ibounds;
1111 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 return false;
1113 }
1114
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001115 SkMatrix inverse;
1116 // if we can't invert the CTM, we can't return local clip bounds
1117 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001118 if (bounds) {
1119 bounds->setEmpty();
1120 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001121 return false;
1122 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001124 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001125 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 // adjust it outwards if we are antialiasing
1127 int inset = (kAA_EdgeType == et);
1128 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1129 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 inverse.mapRect(bounds, r);
1131 }
1132 return true;
1133}
1134
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001135bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
1136 const SkRegion& clip = *fMCRec->fRegion;
1137 if (clip.isEmpty()) {
1138 if (bounds) {
1139 bounds->setEmpty();
1140 }
1141 return false;
1142 }
1143
1144 if (NULL != bounds) {
1145 *bounds = clip.getBounds();
1146 }
1147 return true;
1148}
1149
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150const SkMatrix& SkCanvas::getTotalMatrix() const {
1151 return *fMCRec->fMatrix;
1152}
1153
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001154SkCanvas::ClipType SkCanvas::getClipType() const {
1155 if (fMCRec->fRegion->isEmpty()) return kEmpty_ClipType;
1156 if (fMCRec->fRegion->isRect()) return kRect_ClipType;
1157 return kComplex_ClipType;
1158}
1159
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160const 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
bsalomon@google.come97f0852011-06-17 13:10:25 +00001183SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1184 int width, int height,
1185 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001186 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001187 if (device) {
1188 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1189 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001190 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001191 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001192 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193}
1194
bsalomon@google.come97f0852011-06-17 13:10:25 +00001195SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1196 int width, int height,
1197 bool isOpaque) {
1198 SkDevice* device = this->getDevice();
1199 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001200 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001201 } else {
1202 return NULL;
1203 }
1204}
1205
1206
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207//////////////////////////////////////////////////////////////////////////////
1208// These are the virtual drawing methods
1209//////////////////////////////////////////////////////////////////////////////
1210
reed@google.com2a981812011-04-14 18:59:28 +00001211void SkCanvas::clear(SkColor color) {
1212 SkDrawIter iter(this);
1213
1214 while (iter.next()) {
1215 iter.fDevice->clear(color);
1216 }
1217}
1218
reed@android.com8a1c16f2008-12-17 15:59:43 +00001219void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001220 this->internalDrawPaint(paint);
1221}
1222
1223void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001224 CHECK_NOTHING_TO_DRAW(paint);
1225
reed@google.com4e2b3d32011-04-07 14:18:59 +00001226 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227
1228 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001229 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 }
1231
reed@google.com4e2b3d32011-04-07 14:18:59 +00001232 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233}
1234
1235void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1236 const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001237 CHECK_NOTHING_TO_DRAW(paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238 if ((long)count <= 0) {
1239 return;
1240 }
1241
1242 SkASSERT(pts != NULL);
1243
reed@google.com4e2b3d32011-04-07 14:18:59 +00001244 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001245
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001247 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 }
reed@google.com4b226022011-01-11 18:32:13 +00001249
reed@google.com4e2b3d32011-04-07 14:18:59 +00001250 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251}
1252
1253void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001254 CHECK_NOTHING_TO_DRAW(paint);
1255
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 if (paint.canComputeFastBounds()) {
1257 SkRect storage;
1258 if (this->quickReject(paint.computeFastBounds(r, &storage),
1259 paint2EdgeType(&paint))) {
1260 return;
1261 }
1262 }
reed@google.com4b226022011-01-11 18:32:13 +00001263
reed@google.com4e2b3d32011-04-07 14:18:59 +00001264 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001265
1266 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001267 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268 }
1269
reed@google.com4e2b3d32011-04-07 14:18:59 +00001270 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271}
1272
1273void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001274 CHECK_NOTHING_TO_DRAW(paint);
1275
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001276 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001277 SkRect storage;
1278 const SkRect& bounds = path.getBounds();
1279 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 paint2EdgeType(&paint))) {
1281 return;
1282 }
1283 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001284 if (path.isEmpty()) {
1285 if (path.isInverseFillType()) {
1286 this->internalDrawPaint(paint);
1287 }
1288 return;
1289 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290
reed@google.com4e2b3d32011-04-07 14:18:59 +00001291 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292
1293 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001294 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 }
1296
reed@google.com4e2b3d32011-04-07 14:18:59 +00001297 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298}
1299
1300void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1301 const SkPaint* paint) {
1302 SkDEBUGCODE(bitmap.validate();)
1303
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001304 if (paint) {
1305 CHECK_NOTHING_TO_DRAW(*paint);
1306 }
1307
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1309 SkRect fastBounds;
1310 fastBounds.set(x, y,
1311 x + SkIntToScalar(bitmap.width()),
1312 y + SkIntToScalar(bitmap.height()));
1313 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1314 return;
1315 }
1316 }
reed@google.com4b226022011-01-11 18:32:13 +00001317
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 SkMatrix matrix;
1319 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001320 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321}
1322
reed@google.com9987ec32011-09-07 11:57:52 +00001323// this one is non-virtual, so it can be called safely by other canvas apis
1324void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1325 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1327 return;
1328 }
reed@google.com9987ec32011-09-07 11:57:52 +00001329
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 // do this now, to avoid the cost of calling extract for RLE bitmaps
1331 if (this->quickReject(dst, paint2EdgeType(paint))) {
1332 return;
1333 }
reed@google.com9987ec32011-09-07 11:57:52 +00001334
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001336
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001338 SkRect tmpSrc;
1339 if (src) {
1340 tmpSrc.set(*src);
1341 // if the extract process clipped off the top or left of the
1342 // original, we adjust for that here to get the position right.
1343 if (tmpSrc.fLeft > 0) {
1344 tmpSrc.fRight -= tmpSrc.fLeft;
1345 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001346 }
reed@android.com87899992009-10-16 14:48:38 +00001347 if (tmpSrc.fTop > 0) {
1348 tmpSrc.fBottom -= tmpSrc.fTop;
1349 tmpSrc.fTop = 0;
1350 }
1351 } else {
1352 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1353 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354 }
reed@android.com87899992009-10-16 14:48:38 +00001355 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001356
reed@android.comf2b98d62010-12-20 18:26:13 +00001357 // ensure that src is "valid" before we pass it to our internal routines
1358 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1359 SkIRect tmpISrc;
1360 if (src) {
1361 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001362 if (!tmpISrc.intersect(*src)) {
1363 return;
1364 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001365 src = &tmpISrc;
1366 }
1367 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368}
1369
reed@google.com9987ec32011-09-07 11:57:52 +00001370void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1371 const SkRect& dst, const SkPaint* paint) {
1372 SkDEBUGCODE(bitmap.validate();)
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001373 if (paint) {
1374 CHECK_NOTHING_TO_DRAW(*paint);
1375 }
reed@google.com9987ec32011-09-07 11:57:52 +00001376 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1377}
1378
reed@android.com8a1c16f2008-12-17 15:59:43 +00001379void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1380 const SkPaint* paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001381 if (paint) {
1382 CHECK_NOTHING_TO_DRAW(*paint);
1383 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001384 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001385 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386}
1387
reed@android.comf2b98d62010-12-20 18:26:13 +00001388void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1389 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001391
reed@google.com4e2b3d32011-04-07 14:18:59 +00001392 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001393
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001395 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 }
reed@android.com9b039062009-02-11 15:09:58 +00001397
reed@google.com4e2b3d32011-04-07 14:18:59 +00001398 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399}
1400
reed@google.com9987ec32011-09-07 11:57:52 +00001401void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1402 const SkIRect& center, const SkRect& dst,
1403 const SkPaint* paint) {
1404 const int32_t w = bitmap.width();
1405 const int32_t h = bitmap.height();
1406
1407 SkIRect c = center;
1408 // pin center to the bounds of the bitmap
1409 c.fLeft = SkMax32(0, center.fLeft);
1410 c.fTop = SkMax32(0, center.fTop);
1411 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1412 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1413
1414 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1415 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1416 SkScalar dstX[4] = {
1417 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1418 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1419 };
1420 SkScalar dstY[4] = {
1421 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1422 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1423 };
1424
1425 if (dstX[1] > dstX[2]) {
1426 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1427 dstX[2] = dstX[1];
1428 }
1429
1430 if (dstY[1] > dstY[2]) {
1431 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1432 dstY[2] = dstY[1];
1433 }
1434
1435 SkIRect s;
1436 SkRect d;
1437 for (int y = 0; y < 3; y++) {
1438 s.fTop = srcY[y];
1439 s.fBottom = srcY[y+1];
1440 d.fTop = dstY[y];
1441 d.fBottom = dstY[y+1];
1442 for (int x = 0; x < 3; x++) {
1443 s.fLeft = srcX[x];
1444 s.fRight = srcX[x+1];
1445 d.fLeft = dstX[x];
1446 d.fRight = dstX[x+1];
1447 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1448 }
1449 }
1450}
1451
1452void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1453 const SkRect& dst, const SkPaint* paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001454 if (paint) {
1455 CHECK_NOTHING_TO_DRAW(*paint);
1456 }
reed@google.com9987ec32011-09-07 11:57:52 +00001457 SkDEBUGCODE(bitmap.validate();)
1458
1459 // Need a device entry-point, so gpu can use a mesh
1460 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1461}
1462
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1464 const SkPaint* paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001465 if (paint) {
1466 CHECK_NOTHING_TO_DRAW(*paint);
1467 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001469
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470 if (reject_bitmap(bitmap)) {
1471 return;
1472 }
reed@google.com4b226022011-01-11 18:32:13 +00001473
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474 SkPaint tmp;
1475 if (NULL == paint) {
1476 paint = &tmp;
1477 }
reed@google.com4b226022011-01-11 18:32:13 +00001478
reed@google.com4e2b3d32011-04-07 14:18:59 +00001479 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001480
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481 while (iter.next()) {
1482 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001483 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001485 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486}
1487
reed@google.comf67e4cf2011-03-15 20:56:58 +00001488class SkDeviceFilteredPaint {
1489public:
1490 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1491 SkDevice::TextFlags flags;
1492 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001493 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001494 newPaint->setFlags(flags.fFlags);
1495 newPaint->setHinting(flags.fHinting);
1496 fPaint = newPaint;
1497 } else {
1498 fPaint = &paint;
1499 }
1500 }
1501
reed@google.comf67e4cf2011-03-15 20:56:58 +00001502 const SkPaint& paint() const { return *fPaint; }
1503
1504private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001505 const SkPaint* fPaint;
1506 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001507};
1508
bungeman@google.com52c748b2011-08-22 21:30:43 +00001509void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1510 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001511 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001512 draw.fDevice->drawRect(draw, r, paint);
1513 } else {
1514 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001515 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001516 draw.fDevice->drawRect(draw, r, p);
1517 }
1518}
1519
1520void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1521 const char text[], size_t byteLength,
1522 SkScalar x, SkScalar y) {
1523 SkASSERT(byteLength == 0 || text != NULL);
1524
1525 // nothing to draw
1526 if (text == NULL || byteLength == 0 ||
1527 draw.fClip->isEmpty() ||
1528 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1529 return;
1530 }
1531
1532 SkScalar width = 0;
1533 SkPoint start;
1534
1535 start.set(0, 0); // to avoid warning
1536 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1537 SkPaint::kStrikeThruText_Flag)) {
1538 width = paint.measureText(text, byteLength);
1539
1540 SkScalar offsetX = 0;
1541 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1542 offsetX = SkScalarHalf(width);
1543 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1544 offsetX = width;
1545 }
1546 start.set(x - offsetX, y);
1547 }
1548
1549 if (0 == width) {
1550 return;
1551 }
1552
1553 uint32_t flags = paint.getFlags();
1554
1555 if (flags & (SkPaint::kUnderlineText_Flag |
1556 SkPaint::kStrikeThruText_Flag)) {
1557 SkScalar textSize = paint.getTextSize();
1558 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1559 SkRect r;
1560
1561 r.fLeft = start.fX;
1562 r.fRight = start.fX + width;
1563
1564 if (flags & SkPaint::kUnderlineText_Flag) {
1565 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1566 start.fY);
1567 r.fTop = offset;
1568 r.fBottom = offset + height;
1569 DrawRect(draw, paint, r, textSize);
1570 }
1571 if (flags & SkPaint::kStrikeThruText_Flag) {
1572 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1573 start.fY);
1574 r.fTop = offset;
1575 r.fBottom = offset + height;
1576 DrawRect(draw, paint, r, textSize);
1577 }
1578 }
1579}
1580
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581void SkCanvas::drawText(const void* text, size_t byteLength,
1582 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001583 CHECK_NOTHING_TO_DRAW(paint);
1584
reed@google.com4e2b3d32011-04-07 14:18:59 +00001585 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586
1587 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001588 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001589 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001590 DrawTextDecorations(iter, dfp.paint(),
1591 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592 }
1593
reed@google.com4e2b3d32011-04-07 14:18:59 +00001594 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001595}
1596
1597void SkCanvas::drawPosText(const void* text, size_t byteLength,
1598 const SkPoint pos[], const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001599 CHECK_NOTHING_TO_DRAW(paint);
1600
reed@google.com4e2b3d32011-04-07 14:18:59 +00001601 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001602
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001604 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001606 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 }
reed@google.com4b226022011-01-11 18:32:13 +00001608
reed@google.com4e2b3d32011-04-07 14:18:59 +00001609 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610}
1611
1612void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1613 const SkScalar xpos[], SkScalar constY,
1614 const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001615 CHECK_NOTHING_TO_DRAW(paint);
1616
reed@google.com4e2b3d32011-04-07 14:18:59 +00001617 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001618
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001620 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001622 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623 }
reed@google.com4b226022011-01-11 18:32:13 +00001624
reed@google.com4e2b3d32011-04-07 14:18:59 +00001625 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626}
1627
1628void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1629 const SkPath& path, const SkMatrix* matrix,
1630 const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001631 CHECK_NOTHING_TO_DRAW(paint);
1632
reed@google.com4e2b3d32011-04-07 14:18:59 +00001633 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634
1635 while (iter.next()) {
1636 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001637 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638 }
1639
reed@google.com4e2b3d32011-04-07 14:18:59 +00001640 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641}
1642
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001643#ifdef ANDROID
1644void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1645 const SkPoint pos[], const SkPaint& paint,
1646 const SkPath& path, const SkMatrix* matrix) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001647 CHECK_NOTHING_TO_DRAW(paint);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001648
reed@google.com4e2b3d32011-04-07 14:18:59 +00001649 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001650
1651 while (iter.next()) {
1652 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001653 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001654 }
1655
reed@google.com4e2b3d32011-04-07 14:18:59 +00001656 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001657}
1658#endif
1659
reed@android.com8a1c16f2008-12-17 15:59:43 +00001660void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1661 const SkPoint verts[], const SkPoint texs[],
1662 const SkColor colors[], SkXfermode* xmode,
1663 const uint16_t indices[], int indexCount,
1664 const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001665 CHECK_NOTHING_TO_DRAW(paint);
1666
reed@google.com4e2b3d32011-04-07 14:18:59 +00001667 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001668
reed@android.com8a1c16f2008-12-17 15:59:43 +00001669 while (iter.next()) {
1670 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001671 colors, xmode, indices, indexCount,
1672 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673 }
reed@google.com4b226022011-01-11 18:32:13 +00001674
reed@google.com4e2b3d32011-04-07 14:18:59 +00001675 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676}
1677
reed@android.comcb608442009-12-04 21:32:27 +00001678void SkCanvas::drawData(const void* data, size_t length) {
1679 // do nothing. Subclasses may do something with the data
1680}
1681
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682//////////////////////////////////////////////////////////////////////////////
1683// These methods are NOT virtual, and therefore must call back into virtual
1684// methods, rather than actually drawing themselves.
1685//////////////////////////////////////////////////////////////////////////////
1686
1687void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001688 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689 SkPaint paint;
1690
1691 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001692 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001693 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694 }
1695 this->drawPaint(paint);
1696}
1697
reed@android.com845fdac2009-06-23 03:01:32 +00001698void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001699 SkPaint paint;
1700
1701 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001702 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001703 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 }
1705 this->drawPaint(paint);
1706}
1707
1708void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1709 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001710
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711 pt.set(x, y);
1712 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1713}
1714
1715void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1716 SkPoint pt;
1717 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001718
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719 pt.set(x, y);
1720 paint.setColor(color);
1721 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1722}
1723
1724void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1725 const SkPaint& paint) {
1726 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001727
reed@android.com8a1c16f2008-12-17 15:59:43 +00001728 pts[0].set(x0, y0);
1729 pts[1].set(x1, y1);
1730 this->drawPoints(kLines_PointMode, 2, pts, paint);
1731}
1732
1733void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1734 SkScalar right, SkScalar bottom,
1735 const SkPaint& paint) {
1736 SkRect r;
1737
1738 r.set(left, top, right, bottom);
1739 this->drawRect(r, paint);
1740}
1741
1742void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1743 const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001744 CHECK_NOTHING_TO_DRAW(paint);
1745
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746 if (radius < 0) {
1747 radius = 0;
1748 }
1749
1750 SkRect r;
1751 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001752
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753 if (paint.canComputeFastBounds()) {
1754 SkRect storage;
1755 if (this->quickReject(paint.computeFastBounds(r, &storage),
1756 paint2EdgeType(&paint))) {
1757 return;
1758 }
1759 }
reed@google.com4b226022011-01-11 18:32:13 +00001760
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761 SkPath path;
1762 path.addOval(r);
1763 this->drawPath(path, paint);
1764}
1765
1766void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1767 const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001768 CHECK_NOTHING_TO_DRAW(paint);
1769
reed@android.com8a1c16f2008-12-17 15:59:43 +00001770 if (rx > 0 && ry > 0) {
1771 if (paint.canComputeFastBounds()) {
1772 SkRect storage;
1773 if (this->quickReject(paint.computeFastBounds(r, &storage),
1774 paint2EdgeType(&paint))) {
1775 return;
1776 }
1777 }
1778
1779 SkPath path;
1780 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1781 this->drawPath(path, paint);
1782 } else {
1783 this->drawRect(r, paint);
1784 }
1785}
1786
1787void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001788 CHECK_NOTHING_TO_DRAW(paint);
1789
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790 if (paint.canComputeFastBounds()) {
1791 SkRect storage;
1792 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1793 paint2EdgeType(&paint))) {
1794 return;
1795 }
1796 }
1797
1798 SkPath path;
1799 path.addOval(oval);
1800 this->drawPath(path, paint);
1801}
1802
1803void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1804 SkScalar sweepAngle, bool useCenter,
1805 const SkPaint& paint) {
reed@google.comdcd0f3a2011-10-04 01:17:15 +00001806 CHECK_NOTHING_TO_DRAW(paint);
1807
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1809 this->drawOval(oval, paint);
1810 } else {
1811 SkPath path;
1812 if (useCenter) {
1813 path.moveTo(oval.centerX(), oval.centerY());
1814 }
1815 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1816 if (useCenter) {
1817 path.close();
1818 }
1819 this->drawPath(path, paint);
1820 }
1821}
1822
1823void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1824 const SkPath& path, SkScalar hOffset,
1825 SkScalar vOffset, const SkPaint& paint) {
1826 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001827
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828 matrix.setTranslate(hOffset, vOffset);
1829 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1830}
1831
reed@android.comf76bacf2009-05-13 14:00:33 +00001832///////////////////////////////////////////////////////////////////////////////
1833
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834void SkCanvas::drawPicture(SkPicture& picture) {
1835 int saveCount = save();
1836 picture.draw(this);
1837 restoreToCount(saveCount);
1838}
1839
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840///////////////////////////////////////////////////////////////////////////////
1841///////////////////////////////////////////////////////////////////////////////
1842
1843SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001844 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845
1846 SkASSERT(canvas);
1847
1848 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1849 fDone = !fImpl->next();
1850}
1851
1852SkCanvas::LayerIter::~LayerIter() {
1853 fImpl->~SkDrawIter();
1854}
1855
1856void SkCanvas::LayerIter::next() {
1857 fDone = !fImpl->next();
1858}
1859
1860SkDevice* SkCanvas::LayerIter::device() const {
1861 return fImpl->getDevice();
1862}
1863
1864const SkMatrix& SkCanvas::LayerIter::matrix() const {
1865 return fImpl->getMatrix();
1866}
1867
1868const SkPaint& SkCanvas::LayerIter::paint() const {
1869 const SkPaint* paint = fImpl->getPaint();
1870 if (NULL == paint) {
1871 paint = &fDefaultPaint;
1872 }
1873 return *paint;
1874}
1875
1876const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1877int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1878int SkCanvas::LayerIter::y() const { return fImpl->getY(); }