blob: a77d5d943e2c35b2465d9415ab5066284a3b6581 [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"
reed@google.com00177082011-10-12 14:34:30 +000017#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkScalarCompare.h"
19#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000020#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000021#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023
24//#define SK_TRACE_SAVERESTORE
25
26#ifdef SK_TRACE_SAVERESTORE
27 static int gLayerCounter;
28 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
29 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
30
31 static int gRecCounter;
32 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
33 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
34
35 static int gCanvasCounter;
36 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
37 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
38#else
39 #define inc_layer()
40 #define dec_layer()
41 #define inc_rec()
42 #define dec_rec()
43 #define inc_canvas()
44 #define dec_canvas()
45#endif
46
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000047typedef SkTLazy<SkPaint> SkLazyPaint;
48
reed@android.com8a1c16f2008-12-17 15:59:43 +000049///////////////////////////////////////////////////////////////////////////////
50// Helpers for computing fast bounds for quickReject tests
51
52static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
53 return paint != NULL && paint->isAntiAlias() ?
54 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
55}
56
57///////////////////////////////////////////////////////////////////////////////
58
59/* This is the record we keep for each SkDevice that the user installs.
60 The clip/matrix/proc are fields that reflect the top of the save/restore
61 stack. Whenever the canvas changes, it marks a dirty flag, and then before
62 these are used (assuming we're not on a layer) we rebuild these cache
63 values: they reflect the top of the save stack, but translated and clipped
64 by the device's XY offset and bitmap-bounds.
65*/
66struct DeviceCM {
67 DeviceCM* fNext;
68 SkDevice* fDevice;
69 SkRegion fClip;
70 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000071 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000072 // optional, related to canvas' external matrix
73 const SkMatrix* fMVMatrix;
74 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000075
bungeman@google.com88edf1e2011-08-08 19:41:56 +000076 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 : fNext(NULL) {
78 if (NULL != device) {
79 device->ref();
80 device->lockPixels();
81 }
reed@google.com4b226022011-01-11 18:32:13 +000082 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000084 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000085
bungeman@google.com88edf1e2011-08-08 19:41:56 +000086 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 if (NULL != fDevice) {
88 fDevice->unlockPixels();
89 fDevice->unref();
90 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 SkDELETE(fPaint);
92 }
reed@google.com4b226022011-01-11 18:32:13 +000093
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
reed@google.com46799cd2011-02-22 20:56:26 +000095 const SkClipStack& clipStack, SkRegion* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +000096 int x = fDevice->getOrigin().x();
97 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +000098 int width = fDevice->width();
99 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000100
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 if ((x | y) == 0) {
102 fMatrix = &totalMatrix;
103 fClip = totalClip;
104 } else {
105 fMatrixStorage = totalMatrix;
106 fMatrixStorage.postTranslate(SkIntToScalar(-x),
107 SkIntToScalar(-y));
108 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000109
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 totalClip.translate(-x, -y, &fClip);
111 }
112
113 fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
114
115 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 if (updateClip) {
118 updateClip->op(x, y, x + width, y + height,
119 SkRegion::kDifference_Op);
120 }
reed@google.com4b226022011-01-11 18:32:13 +0000121
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000122 fDevice->setMatrixClip(*fMatrix, fClip, clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123
124#ifdef SK_DEBUG
125 if (!fClip.isEmpty()) {
126 SkIRect deviceR;
127 deviceR.set(0, 0, width, height);
128 SkASSERT(deviceR.contains(fClip.getBounds()));
129 }
130#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000131 // default is to assume no external matrix
132 fMVMatrix = NULL;
133 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000135
136 // can only be called after calling updateMC()
137 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
138 fMVMatrixStorage.setConcat(extI, *fMatrix);
139 fMVMatrix = &fMVMatrixStorage;
140 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
141 }
142
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000144 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145};
146
147/* This is the record we keep for each save/restore level in the stack.
148 Since a level optionally copies the matrix and/or stack, we have pointers
149 for these fields. If the value is copied for this level, the copy is
150 stored in the ...Storage field, and the pointer points to that. If the
151 value is not copied for this level, we ignore ...Storage, and just point
152 at the corresponding value in the previous level in the stack.
153*/
154class SkCanvas::MCRec {
155public:
156 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000157 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
158 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
159 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000160
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 DeviceCM* fLayer;
162 /* If there are any layers in the stack, this points to the top-most
163 one that is at or below this level in the stack (so we know what
164 bitmap/device to draw into from this level. This value is NOT
165 reference counted, since the real owner is either our fLayer field,
166 or a previous one in a lower level.)
167 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000168 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169
170 MCRec(const MCRec* prev, int flags) {
171 if (NULL != prev) {
172 if (flags & SkCanvas::kMatrix_SaveFlag) {
173 fMatrixStorage = *prev->fMatrix;
174 fMatrix = &fMatrixStorage;
175 } else {
176 fMatrix = prev->fMatrix;
177 }
reed@google.com4b226022011-01-11 18:32:13 +0000178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000180 fRasterClipStorage = *prev->fRasterClip;
181 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000182 } else {
reed@google.com00177082011-10-12 14:34:30 +0000183 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 }
185
186 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000187 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188
189 fTopLayer = prev->fTopLayer;
190 } else { // no prev
191 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000192
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000194 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 fFilter = NULL;
196 fTopLayer = NULL;
197 }
198 fLayer = NULL;
199
200 // don't bother initializing fNext
201 inc_rec();
202 }
203 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000204 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 SkDELETE(fLayer);
206 dec_rec();
207 }
reed@google.com4b226022011-01-11 18:32:13 +0000208
reed@android.com8a1c16f2008-12-17 15:59:43 +0000209private:
reed@google.com00177082011-10-12 14:34:30 +0000210 SkMatrix fMatrixStorage;
211 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212};
213
214class SkDrawIter : public SkDraw {
215public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000216 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
217 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 canvas->updateDeviceCMCache();
219
reed@google.com7d7ca792011-02-23 22:39:18 +0000220 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 fBounder = canvas->getBounder();
222 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000223 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 }
reed@google.com4b226022011-01-11 18:32:13 +0000225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 bool next() {
227 // skip over recs with empty clips
228 if (fSkipEmptyClips) {
229 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
230 fCurrLayer = fCurrLayer->fNext;
231 }
232 }
233
234 if (NULL != fCurrLayer) {
235 const DeviceCM* rec = fCurrLayer;
236
237 fMatrix = rec->fMatrix;
238 fClip = &rec->fClip;
239 fDevice = rec->fDevice;
240 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000242 fMVMatrix = rec->fMVMatrix;
243 fExtMatrix = rec->fExtMatrix;
244 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245
246 fCurrLayer = rec->fNext;
247 if (fBounder) {
248 fBounder->setClip(fClip);
249 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000251
bsalomon@google.comd302f142011-03-03 13:54:13 +0000252 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 return true;
254 }
255 return false;
256 }
reed@google.com4b226022011-01-11 18:32:13 +0000257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000259 int getX() const { return fDevice->getOrigin().x(); }
260 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 const SkMatrix& getMatrix() const { return *fMatrix; }
262 const SkRegion& getClip() const { return *fClip; }
263 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000264
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265private:
266 SkCanvas* fCanvas;
267 const DeviceCM* fCurrLayer;
268 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 SkBool8 fSkipEmptyClips;
270
271 typedef SkDraw INHERITED;
272};
273
274/////////////////////////////////////////////////////////////////////////////
275
276class AutoDrawLooper {
277public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000278 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
279 fCanvas = canvas;
280 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000282 fPaint = NULL;
283 fSaveCount = canvas->getSaveCount();
284 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285
reed@google.com4e2b3d32011-04-07 14:18:59 +0000286 if (fLooper) {
287 fLooper->init(canvas);
288 }
289 }
290
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000292 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000294
295 const SkPaint& paint() const {
296 SkASSERT(fPaint);
297 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000299
300 bool next(SkDrawFilter::Type drawType);
301
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000303 SkLazyPaint fLazyPaint;
304 SkCanvas* fCanvas;
305 const SkPaint& fOrigPaint;
306 SkDrawLooper* fLooper;
307 SkDrawFilter* fFilter;
308 const SkPaint* fPaint;
309 int fSaveCount;
310 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311};
312
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000314 fPaint = NULL;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000315 if (fDone) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000316 return false;
317 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000318
reed@google.com632e1a22011-10-06 12:37:00 +0000319 if (fLooper || fFilter) {
320 SkPaint* paint = fLazyPaint.set(fOrigPaint);
321 if (fLooper && !fLooper->next(fCanvas, paint)) {
322 fDone = true;
323 return false;
324 }
325 if (fFilter) {
326 fFilter->filter(paint, drawType);
327 if (NULL == fLooper) {
328 // no looper means we only draw once
329 fDone = true;
330 }
331 }
332 fPaint = paint;
333 } else {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000334 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000335 fPaint = &fOrigPaint;
336 }
337
338 // call this after any possible paint modifiers
339 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000340 fPaint = NULL;
341 return false;
342 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000343 return true;
344}
345
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346/* Stack helper for managing a SkBounder. In the destructor, if we were
347 given a bounder, we call its commit() method, signifying that we are
348 done accumulating bounds for that draw.
349*/
350class SkAutoBounderCommit {
351public:
352 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
353 ~SkAutoBounderCommit() {
354 if (NULL != fBounder) {
355 fBounder->commit();
356 }
357 }
358private:
359 SkBounder* fBounder;
360};
361
362#include "SkColorPriv.h"
363
364class AutoValidator {
365public:
366 AutoValidator(SkDevice* device) : fDevice(device) {}
367 ~AutoValidator() {
368#ifdef SK_DEBUG
369 const SkBitmap& bm = fDevice->accessBitmap(false);
370 if (bm.config() == SkBitmap::kARGB_4444_Config) {
371 for (int y = 0; y < bm.height(); y++) {
372 const SkPMColor16* p = bm.getAddr16(0, y);
373 for (int x = 0; x < bm.width(); x++) {
374 SkPMColor16 c = p[x];
375 SkPMColor16Assert(c);
376 }
377 }
378 }
379#endif
380 }
381private:
382 SkDevice* fDevice;
383};
384
385////////// macros to place around the internal draw calls //////////////////
386
reed@google.com4e2b3d32011-04-07 14:18:59 +0000387#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000389 AutoDrawLooper looper(this, paint); \
390 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391 SkAutoBounderCommit ac(fBounder); \
392 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000393
reed@google.com4e2b3d32011-04-07 14:18:59 +0000394#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395
396////////////////////////////////////////////////////////////////////////////
397
398SkDevice* SkCanvas::init(SkDevice* device) {
399 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000400 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000402 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000403 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000404 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000405 fDeviceCMDirty = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406
407 fMCRec = (MCRec*)fMCStack.push_back();
408 new (fMCRec) MCRec(NULL, 0);
409
410 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
411 fMCRec->fTopLayer = fMCRec->fLayer;
412 fMCRec->fNext = NULL;
413
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000414 fExternalMatrix.reset();
415 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000416 fUseExternalMatrix = false;
417
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418 return this->setDevice(device);
419}
420
reed@google.comcde92112011-07-06 20:00:52 +0000421SkCanvas::SkCanvas()
422: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000423 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000424
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000425 this->init(NULL);
426}
427
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000429 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 inc_canvas();
431
432 this->init(device);
433}
434
435SkCanvas::SkCanvas(const SkBitmap& bitmap)
436 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
437 inc_canvas();
438
reed@google.comcde92112011-07-06 20:00:52 +0000439 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000440}
441
442SkCanvas::~SkCanvas() {
443 // free up the contents of our deque
444 this->restoreToCount(1); // restore everything but the last
445 this->internalRestore(); // restore the last, since we're going away
446
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000447 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000448
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) {
reed@google.com00177082011-10-12 14:34:30 +0000521 rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000523 (void)rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 }
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
reed@google.com00177082011-10-12 14:34:30 +0000533 rec->fRasterClip->setRect(bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000535 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 }
537 }
538 return device;
539}
540
reed@google.comaf951c92011-06-16 19:10:39 +0000541SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
542 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 device->unref();
544 return device;
545}
546
reed@google.com51df9e32010-12-23 19:29:18 +0000547bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
548 SkDevice* device = this->getDevice();
549 if (!device) {
550 return false;
551 }
552 return device->readPixels(srcRect, bitmap);
553}
554
reed@google.com4b226022011-01-11 18:32:13 +0000555//////////////////////////////////////////////////////////////////////////////
556
reed@google.com51df9e32010-12-23 19:29:18 +0000557bool SkCanvas::readPixels(SkBitmap* bitmap) {
558 SkDevice* device = this->getDevice();
559 if (!device) {
560 return false;
561 }
562 SkIRect bounds;
563 bounds.set(0, 0, device->width(), device->height());
564 return this->readPixels(bounds, bitmap);
565}
566
567void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
568 SkDevice* device = this->getDevice();
569 if (device) {
570 device->writePixels(bitmap, x, y);
571 }
572}
573
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574//////////////////////////////////////////////////////////////////////////////
575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576void SkCanvas::updateDeviceCMCache() {
577 if (fDeviceCMDirty) {
578 const SkMatrix& totalMatrix = this->getTotalMatrix();
579 const SkRegion& totalClip = this->getTotalClip();
580 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000581
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000583 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000584 if (fUseExternalMatrix) {
585 layer->updateExternalMatrix(fExternalMatrix,
586 fExternalInverse);
587 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 } else {
589 SkRegion clip;
590 clip = totalClip; // make a copy
591 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000592 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000593 if (fUseExternalMatrix) {
594 layer->updateExternalMatrix(fExternalMatrix,
595 fExternalInverse);
596 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 } while ((layer = layer->fNext) != NULL);
598 }
599 fDeviceCMDirty = false;
600 }
601}
602
reed@android.comf2b98d62010-12-20 18:26:13 +0000603void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000604 const SkRegion& clip,
605 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000607 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000608 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000609 fLastDeviceToGainFocus = device;
610 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611}
612
613///////////////////////////////////////////////////////////////////////////////
614
615int SkCanvas::internalSave(SaveFlags flags) {
616 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000617
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 MCRec* newTop = (MCRec*)fMCStack.push_back();
619 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000620
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 newTop->fNext = fMCRec;
622 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000623
reed@google.com5c3d1472011-02-22 19:12:23 +0000624 fClipStack.save();
625 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
626
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 return saveCount;
628}
629
630int SkCanvas::save(SaveFlags flags) {
631 // call shared impl
632 return this->internalSave(flags);
633}
634
635#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
636#define C16MASK (1 << SkBitmap::kRGB_565_Config)
637#define C8MASK (1 << SkBitmap::kA8_Config)
638
639static SkBitmap::Config resolve_config(SkCanvas* canvas,
640 const SkIRect& bounds,
641 SkCanvas::SaveFlags flags,
642 bool* isOpaque) {
643 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
644
645#if 0
646 // loop through and union all the configs we may draw into
647 uint32_t configMask = 0;
648 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
649 {
650 SkDevice* device = canvas->getLayerDevice(i);
651 if (device->intersects(bounds))
652 configMask |= 1 << device->config();
653 }
654
655 // if the caller wants alpha or fullcolor, we can't return 565
656 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
657 SkCanvas::kHasAlphaLayer_SaveFlag))
658 configMask &= ~C16MASK;
659
660 switch (configMask) {
661 case C8MASK: // if we only have A8, return that
662 return SkBitmap::kA8_Config;
663
664 case C16MASK: // if we only have 565, return that
665 return SkBitmap::kRGB_565_Config;
666
667 default:
668 return SkBitmap::kARGB_8888_Config; // default answer
669 }
670#else
671 return SkBitmap::kARGB_8888_Config; // default answer
672#endif
673}
674
675static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
676 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
677}
678
679int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
680 SaveFlags flags) {
681 // do this before we create the layer. We don't call the public save() since
682 // that would invoke a possibly overridden virtual
683 int count = this->internalSave(flags);
684
685 fDeviceCMDirty = true;
686
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000687 SkIRect clipBounds;
688 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000689 return count;
690 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000692 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693 if (NULL != bounds) {
694 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000695
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696 this->getTotalMatrix().mapRect(&r, *bounds);
697 r.roundOut(&ir);
698 // early exit if the layer's bounds are clipped out
699 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000700 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000701 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000702 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 return count;
704 }
705 } else { // no user bounds, so just use the clip
706 ir = clipBounds;
707 }
708
reed@google.com5c3d1472011-02-22 19:12:23 +0000709 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710 // early exit if the clip is now empty
711 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000712 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 return count;
714 }
715
716 bool isOpaque;
717 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
718
bsalomon@google.come97f0852011-06-17 13:10:25 +0000719 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
720 isOpaque);
bungeman@google.come25c6842011-08-17 14:53:54 +0000721 if (NULL == device) {
722 SkDebugf("Unable to create device for layer.");
723 return count;
724 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000725
reed@google.com6f8f2922011-03-04 22:27:10 +0000726 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
728 device->unref();
729
730 layer->fNext = fMCRec->fTopLayer;
731 fMCRec->fLayer = layer;
732 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
733
734 return count;
735}
736
737int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
738 SaveFlags flags) {
739 if (0xFF == alpha) {
740 return this->saveLayer(bounds, NULL, flags);
741 } else {
742 SkPaint tmpPaint;
743 tmpPaint.setAlpha(alpha);
744 return this->saveLayer(bounds, &tmpPaint, flags);
745 }
746}
747
748void SkCanvas::restore() {
749 // check for underflow
750 if (fMCStack.count() > 1) {
751 this->internalRestore();
752 }
753}
754
755void SkCanvas::internalRestore() {
756 SkASSERT(fMCStack.count() != 0);
757
758 fDeviceCMDirty = true;
759 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000760 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761
reed@google.com5c3d1472011-02-22 19:12:23 +0000762 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000763 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 DeviceCM* layer = fMCRec->fLayer; // may be null
765 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
766 fMCRec->fLayer = NULL;
767
768 // now do the normal restore()
769 fMCRec->~MCRec(); // balanced in save()
770 fMCStack.pop_back();
771 fMCRec = (MCRec*)fMCStack.back();
772
773 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
774 since if we're being recorded, we don't want to record this (the
775 recorder will have already recorded the restore).
776 */
777 if (NULL != layer) {
778 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000779 const SkIPoint& origin = layer->fDevice->getOrigin();
780 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 layer->fPaint);
782 // reset this, since drawDevice will have set it to true
783 fDeviceCMDirty = true;
784 }
785 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000786 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000787
788 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789}
790
791int SkCanvas::getSaveCount() const {
792 return fMCStack.count();
793}
794
795void SkCanvas::restoreToCount(int count) {
796 // sanity check
797 if (count < 1) {
798 count = 1;
799 }
800 while (fMCStack.count() > count) {
801 this->restore();
802 }
803}
804
805/////////////////////////////////////////////////////////////////////////////
806
807// can't draw it if its empty, or its too big for a fixed-point width or height
808static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000809 return bitmap.width() <= 0 || bitmap.height() <= 0
810#ifndef SK_ALLOW_OVER_32K_BITMAPS
811 || bitmap.width() > 32767 || bitmap.height() > 32767
812#endif
813 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814}
815
reed@android.comf2b98d62010-12-20 18:26:13 +0000816void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 const SkMatrix& matrix, const SkPaint* paint) {
818 if (reject_bitmap(bitmap)) {
819 return;
820 }
821
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000822 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000824 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000826 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827}
828
829void SkCanvas::drawDevice(SkDevice* device, int x, int y,
830 const SkPaint* paint) {
831 SkPaint tmp;
832 if (NULL == paint) {
833 tmp.setDither(true);
834 paint = &tmp;
835 }
reed@google.com4b226022011-01-11 18:32:13 +0000836
reed@google.com4e2b3d32011-04-07 14:18:59 +0000837 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 while (iter.next()) {
839 iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +0000840 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000842 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843}
844
845/////////////////////////////////////////////////////////////////////////////
846
847bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
848 fDeviceCMDirty = true;
849 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000850 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 return fMCRec->fMatrix->preTranslate(dx, dy);
852}
853
854bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
855 fDeviceCMDirty = true;
856 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000857 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 return fMCRec->fMatrix->preScale(sx, sy);
859}
860
861bool SkCanvas::rotate(SkScalar degrees) {
862 fDeviceCMDirty = true;
863 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000864 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865 return fMCRec->fMatrix->preRotate(degrees);
866}
867
868bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
869 fDeviceCMDirty = true;
870 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000871 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872 return fMCRec->fMatrix->preSkew(sx, sy);
873}
874
875bool SkCanvas::concat(const SkMatrix& matrix) {
876 fDeviceCMDirty = true;
877 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000878 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000879 return fMCRec->fMatrix->preConcat(matrix);
880}
881
882void SkCanvas::setMatrix(const SkMatrix& matrix) {
883 fDeviceCMDirty = true;
884 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000885 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886 *fMCRec->fMatrix = matrix;
887}
888
889// this is not virtual, so it must call a virtual method so that subclasses
890// will see its action
891void SkCanvas::resetMatrix() {
892 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000893
reed@android.com8a1c16f2008-12-17 15:59:43 +0000894 matrix.reset();
895 this->setMatrix(matrix);
896}
897
898//////////////////////////////////////////////////////////////////////////////
899
reed@google.comc42d35d2011-10-12 11:57:42 +0000900bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000901 AutoValidateClip avc(this);
902
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 fDeviceCMDirty = true;
904 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000905 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906
907 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000908 // for these simpler matrices, we can stay a rect ever after applying
909 // the matrix. This means we don't have to a) make a path, and b) tell
910 // the region code to scan-convert the path, only to discover that it
911 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913
914 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +0000915 fClipStack.clipDevRect(r, op, doAA);
916 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000918 // since we're rotate or some such thing, we convert the rect to a path
919 // and clip against that, since it can handle any matrix. However, to
920 // avoid recursion in the case where we are subclassed (e.g. Pictures)
921 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 SkPath path;
923
924 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +0000925 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 }
927}
928
reed@google.com00177082011-10-12 14:34:30 +0000929static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +0000930 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +0000931 // base is used to limit the size (and therefore memory allocation) of the
932 // region that results from scan converting devPath.
933 SkRegion base;
934
reed@google.com819c9212011-02-23 18:56:55 +0000935 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000936 // since we are intersect, we can do better (tighter) with currRgn's
937 // bounds, than just using the device. However, if currRgn is complex,
938 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +0000939 if (currClip->isRect()) {
940 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +0000941 } else {
reed@google.com00177082011-10-12 14:34:30 +0000942 base.setRect(currClip->getBounds());
943 SkRasterClip clip;
944 clip.setPath(devPath, base, doAA);
945 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +0000946 }
reed@google.com819c9212011-02-23 18:56:55 +0000947 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000948 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
949 base.setRect(0, 0, bm.width(), bm.height());
950
951 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +0000952 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +0000953 } else {
reed@google.com00177082011-10-12 14:34:30 +0000954 SkRasterClip clip;
955 clip.setPath(devPath, base, doAA);
956 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +0000957 }
958 }
959}
960
reed@google.comc42d35d2011-10-12 11:57:42 +0000961bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000962 AutoValidateClip avc(this);
963
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 fDeviceCMDirty = true;
965 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000966 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967
968 SkPath devPath;
969 path.transform(*fMCRec->fMatrix, &devPath);
970
reed@google.com5c3d1472011-02-22 19:12:23 +0000971 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +0000972 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +0000973
reed@google.com00177082011-10-12 14:34:30 +0000974 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975}
976
977bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000978 AutoValidateClip avc(this);
979
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 fDeviceCMDirty = true;
981 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000982 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983
reed@google.com5c3d1472011-02-22 19:12:23 +0000984 // todo: signal fClipStack that we have a region, and therefore (I guess)
985 // we have to ignore it, and use the region directly?
986 fClipStack.clipDevRect(rgn.getBounds());
987
reed@google.com00177082011-10-12 14:34:30 +0000988 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989}
990
reed@google.com819c9212011-02-23 18:56:55 +0000991#ifdef SK_DEBUG
992void SkCanvas::validateClip() const {
993 // construct clipRgn from the clipstack
994 const SkDevice* device = this->getDevice();
995 SkIRect ir;
996 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +0000997 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +0000998
999 SkClipStack::B2FIter iter(fClipStack);
1000 const SkClipStack::B2FIter::Clip* clip;
1001 while ((clip = iter.next()) != NULL) {
1002 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001003 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001004 } else if (clip->fRect) {
1005 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001006 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001007 } else {
reed@google.com00177082011-10-12 14:34:30 +00001008 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001009 }
1010 }
1011
reed@google.com6f8f2922011-03-04 22:27:10 +00001012#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001013 // now compare against the current rgn
1014 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001015 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001016#endif
reed@google.com819c9212011-02-23 18:56:55 +00001017}
1018#endif
1019
reed@google.com5c3d1472011-02-22 19:12:23 +00001020///////////////////////////////////////////////////////////////////////////////
1021
reed@android.comba09de42010-02-05 20:46:05 +00001022void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001023 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001024 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1025 fLocalBoundsCompareTypeBW;
1026
1027 if (!this->getClipBounds(&r, et)) {
1028 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001030 rCompare.set(SkScalarToCompareType(r.fLeft),
1031 SkScalarToCompareType(r.fTop),
1032 SkScalarToCompareType(r.fRight),
1033 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 }
1035}
1036
reed@android.comd252db02009-04-01 18:31:44 +00001037/* current impl ignores edgetype, and relies on
1038 getLocalClipBoundsCompareType(), which always returns a value assuming
1039 antialiasing (worst case)
1040 */
reed@android.comba09de42010-02-05 20:46:05 +00001041bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001042
1043 if (!rect.hasValidCoordinates())
1044 return true;
1045
reed@google.com00177082011-10-12 14:34:30 +00001046 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001047 return true;
1048 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049
tomhudson@google.com8d430182011-06-06 19:11:19 +00001050 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001051 SkRect dst;
1052 fMCRec->fMatrix->mapRect(&dst, rect);
1053 SkIRect idst;
1054 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001055 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001056 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001057 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001058
reed@android.coma380ae42009-07-21 01:17:02 +00001059 // for speed, do the most likely reject compares first
1060 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1061 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1062 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1063 return true;
1064 }
1065 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1066 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1067 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1068 return true;
1069 }
1070 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072}
1073
1074bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001075 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076}
1077
1078bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1079 /* current impl ignores edgetype, and relies on
1080 getLocalClipBoundsCompareType(), which always returns a value assuming
1081 antialiasing (worst case)
1082 */
1083
reed@google.com00177082011-10-12 14:34:30 +00001084 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 return true;
1086 }
reed@google.com4b226022011-01-11 18:32:13 +00001087
reed@android.comaefd2bc2009-03-30 21:02:14 +00001088 SkScalarCompareType userT = SkScalarToCompareType(top);
1089 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001090
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001092 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 if (userT >= userB) {
1094 return true;
1095 }
reed@google.com4b226022011-01-11 18:32:13 +00001096
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 // check if we are above or below the local clip bounds
1098 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1099 return userT >= clipR.fBottom || userB <= clipR.fTop;
1100}
1101
1102bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001103 SkIRect ibounds;
1104 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105 return false;
1106 }
1107
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001108 SkMatrix inverse;
1109 // if we can't invert the CTM, we can't return local clip bounds
1110 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001111 if (bounds) {
1112 bounds->setEmpty();
1113 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001114 return false;
1115 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001117 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001118 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 // adjust it outwards if we are antialiasing
1120 int inset = (kAA_EdgeType == et);
1121 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1122 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123 inverse.mapRect(bounds, r);
1124 }
1125 return true;
1126}
1127
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001128bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001129 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001130 if (clip.isEmpty()) {
1131 if (bounds) {
1132 bounds->setEmpty();
1133 }
1134 return false;
1135 }
1136
1137 if (NULL != bounds) {
1138 *bounds = clip.getBounds();
1139 }
1140 return true;
1141}
1142
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143const SkMatrix& SkCanvas::getTotalMatrix() const {
1144 return *fMCRec->fMatrix;
1145}
1146
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001147SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001148 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1149 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001150 return kComplex_ClipType;
1151}
1152
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001154 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155}
1156
reed@google.com7d7ca792011-02-23 22:39:18 +00001157const SkClipStack& SkCanvas::getTotalClipStack() const {
1158 return fClipStack;
1159}
1160
reed@android.comf2b98d62010-12-20 18:26:13 +00001161void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1162 if (NULL == matrix || matrix->isIdentity()) {
1163 if (fUseExternalMatrix) {
1164 fDeviceCMDirty = true;
1165 }
1166 fUseExternalMatrix = false;
1167 } else {
1168 fUseExternalMatrix = true;
1169 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001170
reed@android.comf2b98d62010-12-20 18:26:13 +00001171 fExternalMatrix = *matrix;
1172 matrix->invert(&fExternalInverse);
1173 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001174}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175
bsalomon@google.come97f0852011-06-17 13:10:25 +00001176SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1177 int width, int height,
1178 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001179 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001180 if (device) {
1181 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1182 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001183 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001184 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001185 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001186}
1187
bsalomon@google.come97f0852011-06-17 13:10:25 +00001188SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1189 int width, int height,
1190 bool isOpaque) {
1191 SkDevice* device = this->getDevice();
1192 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001193 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001194 } else {
1195 return NULL;
1196 }
1197}
1198
1199
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200//////////////////////////////////////////////////////////////////////////////
1201// These are the virtual drawing methods
1202//////////////////////////////////////////////////////////////////////////////
1203
reed@google.com2a981812011-04-14 18:59:28 +00001204void SkCanvas::clear(SkColor color) {
1205 SkDrawIter iter(this);
1206
1207 while (iter.next()) {
1208 iter.fDevice->clear(color);
1209 }
1210}
1211
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001213 this->internalDrawPaint(paint);
1214}
1215
1216void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001217 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218
1219 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001220 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 }
1222
reed@google.com4e2b3d32011-04-07 14:18:59 +00001223 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224}
1225
1226void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1227 const SkPaint& paint) {
1228 if ((long)count <= 0) {
1229 return;
1230 }
1231
1232 SkASSERT(pts != NULL);
1233
reed@google.com4e2b3d32011-04-07 14:18:59 +00001234 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001235
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001237 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238 }
reed@google.com4b226022011-01-11 18:32:13 +00001239
reed@google.com4e2b3d32011-04-07 14:18:59 +00001240 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241}
1242
1243void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1244 if (paint.canComputeFastBounds()) {
1245 SkRect storage;
1246 if (this->quickReject(paint.computeFastBounds(r, &storage),
1247 paint2EdgeType(&paint))) {
1248 return;
1249 }
1250 }
reed@google.com4b226022011-01-11 18:32:13 +00001251
reed@google.com4e2b3d32011-04-07 14:18:59 +00001252 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001253
1254 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001255 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256 }
1257
reed@google.com4e2b3d32011-04-07 14:18:59 +00001258 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001259}
1260
1261void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001262 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001263 SkRect storage;
1264 const SkRect& bounds = path.getBounds();
1265 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 paint2EdgeType(&paint))) {
1267 return;
1268 }
1269 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001270 if (path.isEmpty()) {
1271 if (path.isInverseFillType()) {
1272 this->internalDrawPaint(paint);
1273 }
1274 return;
1275 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276
reed@google.com4e2b3d32011-04-07 14:18:59 +00001277 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278
1279 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001280 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 }
1282
reed@google.com4e2b3d32011-04-07 14:18:59 +00001283 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284}
1285
1286void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1287 const SkPaint* paint) {
1288 SkDEBUGCODE(bitmap.validate();)
1289
1290 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1291 SkRect fastBounds;
1292 fastBounds.set(x, y,
1293 x + SkIntToScalar(bitmap.width()),
1294 y + SkIntToScalar(bitmap.height()));
1295 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1296 return;
1297 }
1298 }
reed@google.com4b226022011-01-11 18:32:13 +00001299
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 SkMatrix matrix;
1301 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001302 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303}
1304
reed@google.com9987ec32011-09-07 11:57:52 +00001305// this one is non-virtual, so it can be called safely by other canvas apis
1306void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1307 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1309 return;
1310 }
reed@google.com9987ec32011-09-07 11:57:52 +00001311
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 // do this now, to avoid the cost of calling extract for RLE bitmaps
1313 if (this->quickReject(dst, paint2EdgeType(paint))) {
1314 return;
1315 }
reed@google.com9987ec32011-09-07 11:57:52 +00001316
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001318
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001320 SkRect tmpSrc;
1321 if (src) {
1322 tmpSrc.set(*src);
1323 // if the extract process clipped off the top or left of the
1324 // original, we adjust for that here to get the position right.
1325 if (tmpSrc.fLeft > 0) {
1326 tmpSrc.fRight -= tmpSrc.fLeft;
1327 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001328 }
reed@android.com87899992009-10-16 14:48:38 +00001329 if (tmpSrc.fTop > 0) {
1330 tmpSrc.fBottom -= tmpSrc.fTop;
1331 tmpSrc.fTop = 0;
1332 }
1333 } else {
1334 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1335 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001336 }
reed@android.com87899992009-10-16 14:48:38 +00001337 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001338
reed@android.comf2b98d62010-12-20 18:26:13 +00001339 // ensure that src is "valid" before we pass it to our internal routines
1340 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1341 SkIRect tmpISrc;
1342 if (src) {
1343 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001344 if (!tmpISrc.intersect(*src)) {
1345 return;
1346 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001347 src = &tmpISrc;
1348 }
1349 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350}
1351
reed@google.com9987ec32011-09-07 11:57:52 +00001352void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1353 const SkRect& dst, const SkPaint* paint) {
1354 SkDEBUGCODE(bitmap.validate();)
1355 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1356}
1357
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1359 const SkPaint* paint) {
1360 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001361 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362}
1363
reed@android.comf2b98d62010-12-20 18:26:13 +00001364void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1365 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001367
reed@google.com4e2b3d32011-04-07 14:18:59 +00001368 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001369
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001371 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 }
reed@android.com9b039062009-02-11 15:09:58 +00001373
reed@google.com4e2b3d32011-04-07 14:18:59 +00001374 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375}
1376
reed@google.com9987ec32011-09-07 11:57:52 +00001377void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1378 const SkIRect& center, const SkRect& dst,
1379 const SkPaint* paint) {
1380 const int32_t w = bitmap.width();
1381 const int32_t h = bitmap.height();
1382
1383 SkIRect c = center;
1384 // pin center to the bounds of the bitmap
1385 c.fLeft = SkMax32(0, center.fLeft);
1386 c.fTop = SkMax32(0, center.fTop);
1387 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1388 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1389
1390 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1391 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1392 SkScalar dstX[4] = {
1393 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1394 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1395 };
1396 SkScalar dstY[4] = {
1397 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1398 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1399 };
1400
1401 if (dstX[1] > dstX[2]) {
1402 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1403 dstX[2] = dstX[1];
1404 }
1405
1406 if (dstY[1] > dstY[2]) {
1407 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1408 dstY[2] = dstY[1];
1409 }
1410
1411 SkIRect s;
1412 SkRect d;
1413 for (int y = 0; y < 3; y++) {
1414 s.fTop = srcY[y];
1415 s.fBottom = srcY[y+1];
1416 d.fTop = dstY[y];
1417 d.fBottom = dstY[y+1];
1418 for (int x = 0; x < 3; x++) {
1419 s.fLeft = srcX[x];
1420 s.fRight = srcX[x+1];
1421 d.fLeft = dstX[x];
1422 d.fRight = dstX[x+1];
1423 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1424 }
1425 }
1426}
1427
1428void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1429 const SkRect& dst, const SkPaint* paint) {
1430 SkDEBUGCODE(bitmap.validate();)
1431
1432 // Need a device entry-point, so gpu can use a mesh
1433 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1434}
1435
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1437 const SkPaint* paint) {
1438 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001439
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 if (reject_bitmap(bitmap)) {
1441 return;
1442 }
reed@google.com4b226022011-01-11 18:32:13 +00001443
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 SkPaint tmp;
1445 if (NULL == paint) {
1446 paint = &tmp;
1447 }
reed@google.com4b226022011-01-11 18:32:13 +00001448
reed@google.com4e2b3d32011-04-07 14:18:59 +00001449 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001450
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 while (iter.next()) {
1452 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001453 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001455 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456}
1457
reed@google.comf67e4cf2011-03-15 20:56:58 +00001458class SkDeviceFilteredPaint {
1459public:
1460 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1461 SkDevice::TextFlags flags;
1462 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001463 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001464 newPaint->setFlags(flags.fFlags);
1465 newPaint->setHinting(flags.fHinting);
1466 fPaint = newPaint;
1467 } else {
1468 fPaint = &paint;
1469 }
1470 }
1471
reed@google.comf67e4cf2011-03-15 20:56:58 +00001472 const SkPaint& paint() const { return *fPaint; }
1473
1474private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001475 const SkPaint* fPaint;
1476 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001477};
1478
bungeman@google.com52c748b2011-08-22 21:30:43 +00001479void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1480 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001481 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001482 draw.fDevice->drawRect(draw, r, paint);
1483 } else {
1484 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001485 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001486 draw.fDevice->drawRect(draw, r, p);
1487 }
1488}
1489
1490void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1491 const char text[], size_t byteLength,
1492 SkScalar x, SkScalar y) {
1493 SkASSERT(byteLength == 0 || text != NULL);
1494
1495 // nothing to draw
1496 if (text == NULL || byteLength == 0 ||
1497 draw.fClip->isEmpty() ||
1498 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1499 return;
1500 }
1501
1502 SkScalar width = 0;
1503 SkPoint start;
1504
1505 start.set(0, 0); // to avoid warning
1506 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1507 SkPaint::kStrikeThruText_Flag)) {
1508 width = paint.measureText(text, byteLength);
1509
1510 SkScalar offsetX = 0;
1511 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1512 offsetX = SkScalarHalf(width);
1513 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1514 offsetX = width;
1515 }
1516 start.set(x - offsetX, y);
1517 }
1518
1519 if (0 == width) {
1520 return;
1521 }
1522
1523 uint32_t flags = paint.getFlags();
1524
1525 if (flags & (SkPaint::kUnderlineText_Flag |
1526 SkPaint::kStrikeThruText_Flag)) {
1527 SkScalar textSize = paint.getTextSize();
1528 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1529 SkRect r;
1530
1531 r.fLeft = start.fX;
1532 r.fRight = start.fX + width;
1533
1534 if (flags & SkPaint::kUnderlineText_Flag) {
1535 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1536 start.fY);
1537 r.fTop = offset;
1538 r.fBottom = offset + height;
1539 DrawRect(draw, paint, r, textSize);
1540 }
1541 if (flags & SkPaint::kStrikeThruText_Flag) {
1542 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1543 start.fY);
1544 r.fTop = offset;
1545 r.fBottom = offset + height;
1546 DrawRect(draw, paint, r, textSize);
1547 }
1548 }
1549}
1550
reed@android.com8a1c16f2008-12-17 15:59:43 +00001551void SkCanvas::drawText(const void* text, size_t byteLength,
1552 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001553 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554
1555 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001556 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001557 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001558 DrawTextDecorations(iter, dfp.paint(),
1559 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001560 }
1561
reed@google.com4e2b3d32011-04-07 14:18:59 +00001562 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001563}
1564
1565void SkCanvas::drawPosText(const void* text, size_t byteLength,
1566 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001567 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001568
reed@android.com8a1c16f2008-12-17 15:59:43 +00001569 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001570 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001571 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001572 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573 }
reed@google.com4b226022011-01-11 18:32:13 +00001574
reed@google.com4e2b3d32011-04-07 14:18:59 +00001575 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576}
1577
1578void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1579 const SkScalar xpos[], SkScalar constY,
1580 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001581 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001582
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001584 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001586 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001587 }
reed@google.com4b226022011-01-11 18:32:13 +00001588
reed@google.com4e2b3d32011-04-07 14:18:59 +00001589 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590}
1591
1592void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1593 const SkPath& path, const SkMatrix* matrix,
1594 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001595 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001596
1597 while (iter.next()) {
1598 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001599 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600 }
1601
reed@google.com4e2b3d32011-04-07 14:18:59 +00001602 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603}
1604
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001605#ifdef ANDROID
1606void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1607 const SkPoint pos[], const SkPaint& paint,
1608 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001609 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001610
1611 while (iter.next()) {
1612 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001613 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001614 }
1615
reed@google.com4e2b3d32011-04-07 14:18:59 +00001616 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001617}
1618#endif
1619
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1621 const SkPoint verts[], const SkPoint texs[],
1622 const SkColor colors[], SkXfermode* xmode,
1623 const uint16_t indices[], int indexCount,
1624 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001625 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001626
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627 while (iter.next()) {
1628 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001629 colors, xmode, indices, indexCount,
1630 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631 }
reed@google.com4b226022011-01-11 18:32:13 +00001632
reed@google.com4e2b3d32011-04-07 14:18:59 +00001633 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634}
1635
reed@android.comcb608442009-12-04 21:32:27 +00001636void SkCanvas::drawData(const void* data, size_t length) {
1637 // do nothing. Subclasses may do something with the data
1638}
1639
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640//////////////////////////////////////////////////////////////////////////////
1641// These methods are NOT virtual, and therefore must call back into virtual
1642// methods, rather than actually drawing themselves.
1643//////////////////////////////////////////////////////////////////////////////
1644
1645void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001646 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001647 SkPaint paint;
1648
1649 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001650 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001651 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652 }
1653 this->drawPaint(paint);
1654}
1655
reed@android.com845fdac2009-06-23 03:01:32 +00001656void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657 SkPaint paint;
1658
1659 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001660 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001661 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001662 }
1663 this->drawPaint(paint);
1664}
1665
1666void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1667 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001668
reed@android.com8a1c16f2008-12-17 15:59:43 +00001669 pt.set(x, y);
1670 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1671}
1672
1673void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1674 SkPoint pt;
1675 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001676
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677 pt.set(x, y);
1678 paint.setColor(color);
1679 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1680}
1681
1682void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1683 const SkPaint& paint) {
1684 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001685
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 pts[0].set(x0, y0);
1687 pts[1].set(x1, y1);
1688 this->drawPoints(kLines_PointMode, 2, pts, paint);
1689}
1690
1691void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1692 SkScalar right, SkScalar bottom,
1693 const SkPaint& paint) {
1694 SkRect r;
1695
1696 r.set(left, top, right, bottom);
1697 this->drawRect(r, paint);
1698}
1699
1700void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1701 const SkPaint& paint) {
1702 if (radius < 0) {
1703 radius = 0;
1704 }
1705
1706 SkRect r;
1707 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001708
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709 if (paint.canComputeFastBounds()) {
1710 SkRect storage;
1711 if (this->quickReject(paint.computeFastBounds(r, &storage),
1712 paint2EdgeType(&paint))) {
1713 return;
1714 }
1715 }
reed@google.com4b226022011-01-11 18:32:13 +00001716
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717 SkPath path;
1718 path.addOval(r);
1719 this->drawPath(path, paint);
1720}
1721
1722void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1723 const SkPaint& paint) {
1724 if (rx > 0 && ry > 0) {
1725 if (paint.canComputeFastBounds()) {
1726 SkRect storage;
1727 if (this->quickReject(paint.computeFastBounds(r, &storage),
1728 paint2EdgeType(&paint))) {
1729 return;
1730 }
1731 }
1732
1733 SkPath path;
1734 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1735 this->drawPath(path, paint);
1736 } else {
1737 this->drawRect(r, paint);
1738 }
1739}
1740
1741void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1742 if (paint.canComputeFastBounds()) {
1743 SkRect storage;
1744 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1745 paint2EdgeType(&paint))) {
1746 return;
1747 }
1748 }
1749
1750 SkPath path;
1751 path.addOval(oval);
1752 this->drawPath(path, paint);
1753}
1754
1755void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1756 SkScalar sweepAngle, bool useCenter,
1757 const SkPaint& paint) {
1758 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1759 this->drawOval(oval, paint);
1760 } else {
1761 SkPath path;
1762 if (useCenter) {
1763 path.moveTo(oval.centerX(), oval.centerY());
1764 }
1765 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1766 if (useCenter) {
1767 path.close();
1768 }
1769 this->drawPath(path, paint);
1770 }
1771}
1772
1773void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1774 const SkPath& path, SkScalar hOffset,
1775 SkScalar vOffset, const SkPaint& paint) {
1776 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001777
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778 matrix.setTranslate(hOffset, vOffset);
1779 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1780}
1781
reed@android.comf76bacf2009-05-13 14:00:33 +00001782///////////////////////////////////////////////////////////////////////////////
1783
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784void SkCanvas::drawPicture(SkPicture& picture) {
1785 int saveCount = save();
1786 picture.draw(this);
1787 restoreToCount(saveCount);
1788}
1789
reed@android.com8a1c16f2008-12-17 15:59:43 +00001790///////////////////////////////////////////////////////////////////////////////
1791///////////////////////////////////////////////////////////////////////////////
1792
1793SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001794 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795
1796 SkASSERT(canvas);
1797
1798 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1799 fDone = !fImpl->next();
1800}
1801
1802SkCanvas::LayerIter::~LayerIter() {
1803 fImpl->~SkDrawIter();
1804}
1805
1806void SkCanvas::LayerIter::next() {
1807 fDone = !fImpl->next();
1808}
1809
1810SkDevice* SkCanvas::LayerIter::device() const {
1811 return fImpl->getDevice();
1812}
1813
1814const SkMatrix& SkCanvas::LayerIter::matrix() const {
1815 return fImpl->getMatrix();
1816}
1817
1818const SkPaint& SkCanvas::LayerIter::paint() const {
1819 const SkPaint* paint = fImpl->getPaint();
1820 if (NULL == paint) {
1821 paint = &fDefaultPaint;
1822 }
1823 return *paint;
1824}
1825
1826const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1827int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1828int SkCanvas::LayerIter::y() const { return fImpl->getY(); }