blob: 3ea9a9c36ba125ef0a1bee67ebb12bbe65d0f8a9 [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;
reed@google.com045e62d2011-10-24 12:19:46 +000069 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 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@google.com045e62d2011-10-24 12:19:46 +000094 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
95 const SkClipStack& clipStack, SkRasterClip* 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
reed@google.com045e62d2011-10-24 12:19:46 +0000113 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114
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) {
reed@google.com045e62d2011-10-24 12:19:46 +0000118 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119 SkRegion::kDifference_Op);
120 }
reed@google.com4b226022011-01-11 18:32:13 +0000121
reed@google.com045e62d2011-10-24 12:19:46 +0000122 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), 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;
reed@google.com045e62d2011-10-24 12:19:46 +0000238 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
239 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 fDevice = rec->fDevice;
241 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000243 fMVMatrix = rec->fMVMatrix;
244 fExtMatrix = rec->fExtMatrix;
245 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246
247 fCurrLayer = rec->fNext;
248 if (fBounder) {
249 fBounder->setClip(fClip);
250 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000252
bsalomon@google.comd302f142011-03-03 13:54:13 +0000253 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 return true;
255 }
256 return false;
257 }
reed@google.com4b226022011-01-11 18:32:13 +0000258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000260 int getX() const { return fDevice->getOrigin().x(); }
261 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 const SkMatrix& getMatrix() const { return *fMatrix; }
263 const SkRegion& getClip() const { return *fClip; }
264 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000265
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266private:
267 SkCanvas* fCanvas;
268 const DeviceCM* fCurrLayer;
269 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 SkBool8 fSkipEmptyClips;
271
272 typedef SkDraw INHERITED;
273};
274
275/////////////////////////////////////////////////////////////////////////////
276
277class AutoDrawLooper {
278public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000279 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
280 fCanvas = canvas;
281 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000283 fPaint = NULL;
284 fSaveCount = canvas->getSaveCount();
285 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286
reed@google.com4e2b3d32011-04-07 14:18:59 +0000287 if (fLooper) {
288 fLooper->init(canvas);
289 }
290 }
291
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000295
296 const SkPaint& paint() const {
297 SkASSERT(fPaint);
298 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000300
301 bool next(SkDrawFilter::Type drawType);
302
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000304 SkLazyPaint fLazyPaint;
305 SkCanvas* fCanvas;
306 const SkPaint& fOrigPaint;
307 SkDrawLooper* fLooper;
308 SkDrawFilter* fFilter;
309 const SkPaint* fPaint;
310 int fSaveCount;
311 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312};
313
reed@google.com4e2b3d32011-04-07 14:18:59 +0000314bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000315 fPaint = NULL;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000316 if (fDone) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000317 return false;
318 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000319
reed@google.com632e1a22011-10-06 12:37:00 +0000320 if (fLooper || fFilter) {
321 SkPaint* paint = fLazyPaint.set(fOrigPaint);
322 if (fLooper && !fLooper->next(fCanvas, paint)) {
323 fDone = true;
324 return false;
325 }
326 if (fFilter) {
327 fFilter->filter(paint, drawType);
328 if (NULL == fLooper) {
329 // no looper means we only draw once
330 fDone = true;
331 }
332 }
333 fPaint = paint;
334 } else {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000336 fPaint = &fOrigPaint;
337 }
338
339 // call this after any possible paint modifiers
340 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000341 fPaint = NULL;
342 return false;
343 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000344 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
reed@google.com210ce002011-11-01 14:24:23 +0000469SkISize SkCanvas::getDeviceSize() const {
470 SkDevice* d = this->getDevice();
471 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
472}
473
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474SkDevice* SkCanvas::getDevice() const {
475 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000476 SkDeque::F2BIter iter(fMCStack);
477 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478 SkASSERT(rec && rec->fLayer);
479 return rec->fLayer->fDevice;
480}
481
reed@google.com9266fed2011-03-30 00:18:03 +0000482SkDevice* SkCanvas::getTopDevice() const {
483 return fMCRec->fTopLayer->fDevice;
484}
485
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486SkDevice* SkCanvas::setDevice(SkDevice* device) {
487 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000488 SkDeque::F2BIter iter(fMCStack);
489 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490 SkASSERT(rec && rec->fLayer);
491 SkDevice* rootDevice = rec->fLayer->fDevice;
492
493 if (rootDevice == device) {
494 return device;
495 }
reed@google.com4b226022011-01-11 18:32:13 +0000496
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497 /* Notify the devices that they are going in/out of scope, so they can do
498 things like lock/unlock their pixels, etc.
499 */
500 if (device) {
501 device->lockPixels();
502 }
503 if (rootDevice) {
504 rootDevice->unlockPixels();
505 }
506
507 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
508 rootDevice = device;
509
510 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 /* Now we update our initial region to have the bounds of the new device,
513 and then intersect all of the clips in our stack with these bounds,
514 to ensure that we can't draw outside of the device's bounds (and trash
515 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 NOTE: this is only a partial-fix, since if the new device is larger than
518 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000519 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
521 reconstruct the correct clips, so this approximation will have to do.
522 The caller really needs to restore() back to the base if they want to
523 accurately take advantage of the new device bounds.
524 */
525
526 if (NULL == device) {
reed@google.com00177082011-10-12 14:34:30 +0000527 rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000529 (void)rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000531 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 } else {
533 // compute our total bounds for all devices
534 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 bounds.set(0, 0, device->width(), device->height());
537
538 // now jam our 1st clip to be bounds, and intersect the rest with that
reed@google.com00177082011-10-12 14:34:30 +0000539 rec->fRasterClip->setRect(bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000541 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 }
543 }
544 return device;
545}
546
reed@google.comaf951c92011-06-16 19:10:39 +0000547SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
548 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549 device->unref();
550 return device;
551}
552
reed@google.com51df9e32010-12-23 19:29:18 +0000553bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
554 SkDevice* device = this->getDevice();
555 if (!device) {
556 return false;
557 }
558 return device->readPixels(srcRect, bitmap);
559}
560
reed@google.com4b226022011-01-11 18:32:13 +0000561//////////////////////////////////////////////////////////////////////////////
562
reed@google.com51df9e32010-12-23 19:29:18 +0000563bool SkCanvas::readPixels(SkBitmap* bitmap) {
564 SkDevice* device = this->getDevice();
565 if (!device) {
566 return false;
567 }
568 SkIRect bounds;
569 bounds.set(0, 0, device->width(), device->height());
570 return this->readPixels(bounds, bitmap);
571}
572
573void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
574 SkDevice* device = this->getDevice();
575 if (device) {
576 device->writePixels(bitmap, x, y);
577 }
578}
579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580//////////////////////////////////////////////////////////////////////////////
581
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582void SkCanvas::updateDeviceCMCache() {
583 if (fDeviceCMDirty) {
584 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000585 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000587
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000589 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000590 if (fUseExternalMatrix) {
591 layer->updateExternalMatrix(fExternalMatrix,
592 fExternalInverse);
593 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000595 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000597 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000598 if (fUseExternalMatrix) {
599 layer->updateExternalMatrix(fExternalMatrix,
600 fExternalInverse);
601 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 } while ((layer = layer->fNext) != NULL);
603 }
604 fDeviceCMDirty = false;
605 }
606}
607
reed@android.comf2b98d62010-12-20 18:26:13 +0000608void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000609 const SkRegion& clip,
610 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000612 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000613 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000614 fLastDeviceToGainFocus = device;
615 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616}
617
618///////////////////////////////////////////////////////////////////////////////
619
620int SkCanvas::internalSave(SaveFlags flags) {
621 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000622
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623 MCRec* newTop = (MCRec*)fMCStack.push_back();
624 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000625
reed@android.com8a1c16f2008-12-17 15:59:43 +0000626 newTop->fNext = fMCRec;
627 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000628
reed@google.com5c3d1472011-02-22 19:12:23 +0000629 fClipStack.save();
630 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
631
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 return saveCount;
633}
634
635int SkCanvas::save(SaveFlags flags) {
636 // call shared impl
637 return this->internalSave(flags);
638}
639
640#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
641#define C16MASK (1 << SkBitmap::kRGB_565_Config)
642#define C8MASK (1 << SkBitmap::kA8_Config)
643
644static SkBitmap::Config resolve_config(SkCanvas* canvas,
645 const SkIRect& bounds,
646 SkCanvas::SaveFlags flags,
647 bool* isOpaque) {
648 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
649
650#if 0
651 // loop through and union all the configs we may draw into
652 uint32_t configMask = 0;
653 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
654 {
655 SkDevice* device = canvas->getLayerDevice(i);
656 if (device->intersects(bounds))
657 configMask |= 1 << device->config();
658 }
659
660 // if the caller wants alpha or fullcolor, we can't return 565
661 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
662 SkCanvas::kHasAlphaLayer_SaveFlag))
663 configMask &= ~C16MASK;
664
665 switch (configMask) {
666 case C8MASK: // if we only have A8, return that
667 return SkBitmap::kA8_Config;
668
669 case C16MASK: // if we only have 565, return that
670 return SkBitmap::kRGB_565_Config;
671
672 default:
673 return SkBitmap::kARGB_8888_Config; // default answer
674 }
675#else
676 return SkBitmap::kARGB_8888_Config; // default answer
677#endif
678}
679
680static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
681 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
682}
683
684int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
685 SaveFlags flags) {
686 // do this before we create the layer. We don't call the public save() since
687 // that would invoke a possibly overridden virtual
688 int count = this->internalSave(flags);
689
690 fDeviceCMDirty = true;
691
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000692 SkIRect clipBounds;
693 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000694 return count;
695 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000697 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000698 if (NULL != bounds) {
699 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000700
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 this->getTotalMatrix().mapRect(&r, *bounds);
702 r.roundOut(&ir);
703 // early exit if the layer's bounds are clipped out
704 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000705 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000706 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000707 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 return count;
709 }
710 } else { // no user bounds, so just use the clip
711 ir = clipBounds;
712 }
713
reed@google.com5c3d1472011-02-22 19:12:23 +0000714 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 // early exit if the clip is now empty
716 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000717 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 return count;
719 }
720
721 bool isOpaque;
722 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
723
bsalomon@google.come97f0852011-06-17 13:10:25 +0000724 SkDevice* device = this->createLayerDevice(config, ir.width(), ir.height(),
725 isOpaque);
bungeman@google.come25c6842011-08-17 14:53:54 +0000726 if (NULL == device) {
727 SkDebugf("Unable to create device for layer.");
728 return count;
729 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000730
reed@google.com6f8f2922011-03-04 22:27:10 +0000731 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
733 device->unref();
734
735 layer->fNext = fMCRec->fTopLayer;
736 fMCRec->fLayer = layer;
737 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
738
739 return count;
740}
741
742int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
743 SaveFlags flags) {
744 if (0xFF == alpha) {
745 return this->saveLayer(bounds, NULL, flags);
746 } else {
747 SkPaint tmpPaint;
748 tmpPaint.setAlpha(alpha);
749 return this->saveLayer(bounds, &tmpPaint, flags);
750 }
751}
752
753void SkCanvas::restore() {
754 // check for underflow
755 if (fMCStack.count() > 1) {
756 this->internalRestore();
757 }
758}
759
760void SkCanvas::internalRestore() {
761 SkASSERT(fMCStack.count() != 0);
762
763 fDeviceCMDirty = true;
764 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000765 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766
reed@google.com5c3d1472011-02-22 19:12:23 +0000767 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000768 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 DeviceCM* layer = fMCRec->fLayer; // may be null
770 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
771 fMCRec->fLayer = NULL;
772
773 // now do the normal restore()
774 fMCRec->~MCRec(); // balanced in save()
775 fMCStack.pop_back();
776 fMCRec = (MCRec*)fMCStack.back();
777
778 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
779 since if we're being recorded, we don't want to record this (the
780 recorder will have already recorded the restore).
781 */
782 if (NULL != layer) {
783 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000784 const SkIPoint& origin = layer->fDevice->getOrigin();
785 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 layer->fPaint);
787 // reset this, since drawDevice will have set it to true
788 fDeviceCMDirty = true;
789 }
790 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000791 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000792
793 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794}
795
796int SkCanvas::getSaveCount() const {
797 return fMCStack.count();
798}
799
800void SkCanvas::restoreToCount(int count) {
801 // sanity check
802 if (count < 1) {
803 count = 1;
804 }
805 while (fMCStack.count() > count) {
806 this->restore();
807 }
808}
809
810/////////////////////////////////////////////////////////////////////////////
811
812// can't draw it if its empty, or its too big for a fixed-point width or height
813static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000814 return bitmap.width() <= 0 || bitmap.height() <= 0
815#ifndef SK_ALLOW_OVER_32K_BITMAPS
816 || bitmap.width() > 32767 || bitmap.height() > 32767
817#endif
818 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819}
820
reed@android.comf2b98d62010-12-20 18:26:13 +0000821void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 const SkMatrix& matrix, const SkPaint* paint) {
823 if (reject_bitmap(bitmap)) {
824 return;
825 }
826
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000827 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000829 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000831 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832}
833
834void SkCanvas::drawDevice(SkDevice* device, int x, int y,
835 const SkPaint* paint) {
836 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
reed@google.comc42d35d2011-10-12 11:57:42 +0000905bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
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;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918
919 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +0000920 fClipStack.clipDevRect(r, op, doAA);
921 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000923 // since we're rotate or some such thing, we convert the rect to a path
924 // and clip against that, since it can handle any matrix. However, to
925 // avoid recursion in the case where we are subclassed (e.g. Pictures)
926 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 SkPath path;
928
929 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +0000930 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 }
932}
933
reed@google.com00177082011-10-12 14:34:30 +0000934static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +0000935 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +0000936 // base is used to limit the size (and therefore memory allocation) of the
937 // region that results from scan converting devPath.
938 SkRegion base;
939
reed@google.com819c9212011-02-23 18:56:55 +0000940 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +0000941 // since we are intersect, we can do better (tighter) with currRgn's
942 // bounds, than just using the device. However, if currRgn is complex,
943 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +0000944 if (currClip->isRect()) {
945 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +0000946 } else {
reed@google.com00177082011-10-12 14:34:30 +0000947 base.setRect(currClip->getBounds());
948 SkRasterClip clip;
949 clip.setPath(devPath, base, doAA);
950 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +0000951 }
reed@google.com819c9212011-02-23 18:56:55 +0000952 } else {
reed@google.com819c9212011-02-23 18:56:55 +0000953 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
954 base.setRect(0, 0, bm.width(), bm.height());
955
956 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +0000957 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +0000958 } else {
reed@google.com00177082011-10-12 14:34:30 +0000959 SkRasterClip clip;
960 clip.setPath(devPath, base, doAA);
961 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +0000962 }
963 }
964}
965
reed@google.comc42d35d2011-10-12 11:57:42 +0000966bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000967 AutoValidateClip avc(this);
968
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969 fDeviceCMDirty = true;
970 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000971 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972
973 SkPath devPath;
974 path.transform(*fMCRec->fMatrix, &devPath);
975
reed@google.com5c3d1472011-02-22 19:12:23 +0000976 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +0000977 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +0000978
reed@google.com00177082011-10-12 14:34:30 +0000979 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980}
981
982bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000983 AutoValidateClip avc(this);
984
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 fDeviceCMDirty = true;
986 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000987 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988
reed@google.com5c3d1472011-02-22 19:12:23 +0000989 // todo: signal fClipStack that we have a region, and therefore (I guess)
990 // we have to ignore it, and use the region directly?
991 fClipStack.clipDevRect(rgn.getBounds());
992
reed@google.com00177082011-10-12 14:34:30 +0000993 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994}
995
reed@google.com819c9212011-02-23 18:56:55 +0000996#ifdef SK_DEBUG
997void SkCanvas::validateClip() const {
998 // construct clipRgn from the clipstack
999 const SkDevice* device = this->getDevice();
1000 SkIRect ir;
1001 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001002 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001003
1004 SkClipStack::B2FIter iter(fClipStack);
1005 const SkClipStack::B2FIter::Clip* clip;
1006 while ((clip = iter.next()) != NULL) {
1007 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001008 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001009 } else if (clip->fRect) {
1010 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001011 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001012 } else {
reed@google.com00177082011-10-12 14:34:30 +00001013 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001014 }
1015 }
1016
reed@google.com6f8f2922011-03-04 22:27:10 +00001017#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001018 // now compare against the current rgn
1019 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001020 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001021#endif
reed@google.com819c9212011-02-23 18:56:55 +00001022}
1023#endif
1024
reed@google.com5c3d1472011-02-22 19:12:23 +00001025///////////////////////////////////////////////////////////////////////////////
1026
reed@android.comba09de42010-02-05 20:46:05 +00001027void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001028 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001029 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1030 fLocalBoundsCompareTypeBW;
1031
1032 if (!this->getClipBounds(&r, et)) {
1033 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001035 rCompare.set(SkScalarToCompareType(r.fLeft),
1036 SkScalarToCompareType(r.fTop),
1037 SkScalarToCompareType(r.fRight),
1038 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001039 }
1040}
1041
reed@android.comd252db02009-04-01 18:31:44 +00001042/* current impl ignores edgetype, and relies on
1043 getLocalClipBoundsCompareType(), which always returns a value assuming
1044 antialiasing (worst case)
1045 */
reed@android.comba09de42010-02-05 20:46:05 +00001046bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001047
1048 if (!rect.hasValidCoordinates())
1049 return true;
1050
reed@google.com00177082011-10-12 14:34:30 +00001051 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001052 return true;
1053 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054
tomhudson@google.com8d430182011-06-06 19:11:19 +00001055 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001056 SkRect dst;
1057 fMCRec->fMatrix->mapRect(&dst, rect);
1058 SkIRect idst;
1059 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001060 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001061 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001062 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001063
reed@android.coma380ae42009-07-21 01:17:02 +00001064 // for speed, do the most likely reject compares first
1065 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1066 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1067 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1068 return true;
1069 }
1070 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1071 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1072 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1073 return true;
1074 }
1075 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077}
1078
1079bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001080 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001081}
1082
1083bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1084 /* current impl ignores edgetype, and relies on
1085 getLocalClipBoundsCompareType(), which always returns a value assuming
1086 antialiasing (worst case)
1087 */
1088
reed@google.com00177082011-10-12 14:34:30 +00001089 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 return true;
1091 }
reed@google.com4b226022011-01-11 18:32:13 +00001092
reed@android.comaefd2bc2009-03-30 21:02:14 +00001093 SkScalarCompareType userT = SkScalarToCompareType(top);
1094 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001095
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001097 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 if (userT >= userB) {
1099 return true;
1100 }
reed@google.com4b226022011-01-11 18:32:13 +00001101
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 // check if we are above or below the local clip bounds
1103 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1104 return userT >= clipR.fBottom || userB <= clipR.fTop;
1105}
1106
1107bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001108 SkIRect ibounds;
1109 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 return false;
1111 }
1112
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001113 SkMatrix inverse;
1114 // if we can't invert the CTM, we can't return local clip bounds
1115 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001116 if (bounds) {
1117 bounds->setEmpty();
1118 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001119 return false;
1120 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001122 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001123 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 // adjust it outwards if we are antialiasing
1125 int inset = (kAA_EdgeType == et);
1126 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1127 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 inverse.mapRect(bounds, r);
1129 }
1130 return true;
1131}
1132
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001133bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001134 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001135 if (clip.isEmpty()) {
1136 if (bounds) {
1137 bounds->setEmpty();
1138 }
1139 return false;
1140 }
1141
1142 if (NULL != bounds) {
1143 *bounds = clip.getBounds();
1144 }
1145 return true;
1146}
1147
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148const SkMatrix& SkCanvas::getTotalMatrix() const {
1149 return *fMCRec->fMatrix;
1150}
1151
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001152SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001153 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1154 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001155 return kComplex_ClipType;
1156}
1157
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001159 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001160}
1161
reed@google.com7d7ca792011-02-23 22:39:18 +00001162const SkClipStack& SkCanvas::getTotalClipStack() const {
1163 return fClipStack;
1164}
1165
reed@android.comf2b98d62010-12-20 18:26:13 +00001166void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1167 if (NULL == matrix || matrix->isIdentity()) {
1168 if (fUseExternalMatrix) {
1169 fDeviceCMDirty = true;
1170 }
1171 fUseExternalMatrix = false;
1172 } else {
1173 fUseExternalMatrix = true;
1174 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001175
reed@android.comf2b98d62010-12-20 18:26:13 +00001176 fExternalMatrix = *matrix;
1177 matrix->invert(&fExternalInverse);
1178 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001179}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180
bsalomon@google.come97f0852011-06-17 13:10:25 +00001181SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1182 int width, int height,
1183 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001184 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001185 if (device) {
1186 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1187 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001188 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001189 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001190 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191}
1192
bsalomon@google.come97f0852011-06-17 13:10:25 +00001193SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1194 int width, int height,
1195 bool isOpaque) {
1196 SkDevice* device = this->getDevice();
1197 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001198 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001199 } else {
1200 return NULL;
1201 }
1202}
1203
1204
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205//////////////////////////////////////////////////////////////////////////////
1206// These are the virtual drawing methods
1207//////////////////////////////////////////////////////////////////////////////
1208
reed@google.com2a981812011-04-14 18:59:28 +00001209void SkCanvas::clear(SkColor color) {
1210 SkDrawIter iter(this);
1211
1212 while (iter.next()) {
1213 iter.fDevice->clear(color);
1214 }
1215}
1216
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001218 this->internalDrawPaint(paint);
1219}
1220
1221void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001222 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223
1224 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001225 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001226 }
1227
reed@google.com4e2b3d32011-04-07 14:18:59 +00001228 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229}
1230
1231void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1232 const SkPaint& paint) {
1233 if ((long)count <= 0) {
1234 return;
1235 }
1236
1237 SkASSERT(pts != NULL);
1238
reed@google.com4e2b3d32011-04-07 14:18:59 +00001239 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001240
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001242 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243 }
reed@google.com4b226022011-01-11 18:32:13 +00001244
reed@google.com4e2b3d32011-04-07 14:18:59 +00001245 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246}
1247
1248void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1249 if (paint.canComputeFastBounds()) {
1250 SkRect storage;
1251 if (this->quickReject(paint.computeFastBounds(r, &storage),
1252 paint2EdgeType(&paint))) {
1253 return;
1254 }
1255 }
reed@google.com4b226022011-01-11 18:32:13 +00001256
reed@google.com4e2b3d32011-04-07 14:18:59 +00001257 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001258
1259 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001260 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261 }
1262
reed@google.com4e2b3d32011-04-07 14:18:59 +00001263 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264}
1265
1266void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001267 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001268 SkRect storage;
1269 const SkRect& bounds = path.getBounds();
1270 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 paint2EdgeType(&paint))) {
1272 return;
1273 }
1274 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001275 if (path.isEmpty()) {
1276 if (path.isInverseFillType()) {
1277 this->internalDrawPaint(paint);
1278 }
1279 return;
1280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281
reed@google.com4e2b3d32011-04-07 14:18:59 +00001282 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283
1284 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001285 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 }
1287
reed@google.com4e2b3d32011-04-07 14:18:59 +00001288 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289}
1290
1291void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1292 const SkPaint* paint) {
1293 SkDEBUGCODE(bitmap.validate();)
1294
1295 if (NULL == paint || (paint->getMaskFilter() == NULL)) {
1296 SkRect fastBounds;
1297 fastBounds.set(x, y,
1298 x + SkIntToScalar(bitmap.width()),
1299 y + SkIntToScalar(bitmap.height()));
1300 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1301 return;
1302 }
1303 }
reed@google.com4b226022011-01-11 18:32:13 +00001304
reed@android.com8a1c16f2008-12-17 15:59:43 +00001305 SkMatrix matrix;
1306 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001307 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001308}
1309
reed@google.com9987ec32011-09-07 11:57:52 +00001310// this one is non-virtual, so it can be called safely by other canvas apis
1311void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1312 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1314 return;
1315 }
reed@google.com9987ec32011-09-07 11:57:52 +00001316
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 // do this now, to avoid the cost of calling extract for RLE bitmaps
1318 if (this->quickReject(dst, paint2EdgeType(paint))) {
1319 return;
1320 }
reed@google.com9987ec32011-09-07 11:57:52 +00001321
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001323
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001325 SkRect tmpSrc;
1326 if (src) {
1327 tmpSrc.set(*src);
1328 // if the extract process clipped off the top or left of the
1329 // original, we adjust for that here to get the position right.
1330 if (tmpSrc.fLeft > 0) {
1331 tmpSrc.fRight -= tmpSrc.fLeft;
1332 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001333 }
reed@android.com87899992009-10-16 14:48:38 +00001334 if (tmpSrc.fTop > 0) {
1335 tmpSrc.fBottom -= tmpSrc.fTop;
1336 tmpSrc.fTop = 0;
1337 }
1338 } else {
1339 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1340 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001341 }
reed@android.com87899992009-10-16 14:48:38 +00001342 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001343
reed@android.comf2b98d62010-12-20 18:26:13 +00001344 // ensure that src is "valid" before we pass it to our internal routines
1345 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1346 SkIRect tmpISrc;
1347 if (src) {
1348 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001349 if (!tmpISrc.intersect(*src)) {
1350 return;
1351 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001352 src = &tmpISrc;
1353 }
1354 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355}
1356
reed@google.com9987ec32011-09-07 11:57:52 +00001357void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1358 const SkRect& dst, const SkPaint* paint) {
1359 SkDEBUGCODE(bitmap.validate();)
1360 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1361}
1362
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1364 const SkPaint* paint) {
1365 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001366 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367}
1368
reed@android.comf2b98d62010-12-20 18:26:13 +00001369void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1370 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001372
reed@google.com4e2b3d32011-04-07 14:18:59 +00001373 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001374
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001376 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 }
reed@android.com9b039062009-02-11 15:09:58 +00001378
reed@google.com4e2b3d32011-04-07 14:18:59 +00001379 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380}
1381
reed@google.com9987ec32011-09-07 11:57:52 +00001382void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1383 const SkIRect& center, const SkRect& dst,
1384 const SkPaint* paint) {
1385 const int32_t w = bitmap.width();
1386 const int32_t h = bitmap.height();
1387
1388 SkIRect c = center;
1389 // pin center to the bounds of the bitmap
1390 c.fLeft = SkMax32(0, center.fLeft);
1391 c.fTop = SkMax32(0, center.fTop);
1392 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1393 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1394
1395 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1396 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1397 SkScalar dstX[4] = {
1398 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1399 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1400 };
1401 SkScalar dstY[4] = {
1402 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1403 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1404 };
1405
1406 if (dstX[1] > dstX[2]) {
1407 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1408 dstX[2] = dstX[1];
1409 }
1410
1411 if (dstY[1] > dstY[2]) {
1412 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1413 dstY[2] = dstY[1];
1414 }
1415
1416 SkIRect s;
1417 SkRect d;
1418 for (int y = 0; y < 3; y++) {
1419 s.fTop = srcY[y];
1420 s.fBottom = srcY[y+1];
1421 d.fTop = dstY[y];
1422 d.fBottom = dstY[y+1];
1423 for (int x = 0; x < 3; x++) {
1424 s.fLeft = srcX[x];
1425 s.fRight = srcX[x+1];
1426 d.fLeft = dstX[x];
1427 d.fRight = dstX[x+1];
1428 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1429 }
1430 }
1431}
1432
1433void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1434 const SkRect& dst, const SkPaint* paint) {
1435 SkDEBUGCODE(bitmap.validate();)
1436
1437 // Need a device entry-point, so gpu can use a mesh
1438 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1439}
1440
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1442 const SkPaint* paint) {
1443 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001444
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 if (reject_bitmap(bitmap)) {
1446 return;
1447 }
reed@google.com4b226022011-01-11 18:32:13 +00001448
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449 SkPaint tmp;
1450 if (NULL == paint) {
1451 paint = &tmp;
1452 }
reed@google.com4b226022011-01-11 18:32:13 +00001453
reed@google.com4e2b3d32011-04-07 14:18:59 +00001454 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001455
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456 while (iter.next()) {
1457 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001458 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001460 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461}
1462
reed@google.comf67e4cf2011-03-15 20:56:58 +00001463class SkDeviceFilteredPaint {
1464public:
1465 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1466 SkDevice::TextFlags flags;
1467 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001468 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001469 newPaint->setFlags(flags.fFlags);
1470 newPaint->setHinting(flags.fHinting);
1471 fPaint = newPaint;
1472 } else {
1473 fPaint = &paint;
1474 }
1475 }
1476
reed@google.comf67e4cf2011-03-15 20:56:58 +00001477 const SkPaint& paint() const { return *fPaint; }
1478
1479private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001480 const SkPaint* fPaint;
1481 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001482};
1483
bungeman@google.com52c748b2011-08-22 21:30:43 +00001484void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1485 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001486 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001487 draw.fDevice->drawRect(draw, r, paint);
1488 } else {
1489 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001490 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001491 draw.fDevice->drawRect(draw, r, p);
1492 }
1493}
1494
1495void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1496 const char text[], size_t byteLength,
1497 SkScalar x, SkScalar y) {
1498 SkASSERT(byteLength == 0 || text != NULL);
1499
1500 // nothing to draw
1501 if (text == NULL || byteLength == 0 ||
1502 draw.fClip->isEmpty() ||
1503 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1504 return;
1505 }
1506
1507 SkScalar width = 0;
1508 SkPoint start;
1509
1510 start.set(0, 0); // to avoid warning
1511 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1512 SkPaint::kStrikeThruText_Flag)) {
1513 width = paint.measureText(text, byteLength);
1514
1515 SkScalar offsetX = 0;
1516 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1517 offsetX = SkScalarHalf(width);
1518 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1519 offsetX = width;
1520 }
1521 start.set(x - offsetX, y);
1522 }
1523
1524 if (0 == width) {
1525 return;
1526 }
1527
1528 uint32_t flags = paint.getFlags();
1529
1530 if (flags & (SkPaint::kUnderlineText_Flag |
1531 SkPaint::kStrikeThruText_Flag)) {
1532 SkScalar textSize = paint.getTextSize();
1533 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1534 SkRect r;
1535
1536 r.fLeft = start.fX;
1537 r.fRight = start.fX + width;
1538
1539 if (flags & SkPaint::kUnderlineText_Flag) {
1540 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1541 start.fY);
1542 r.fTop = offset;
1543 r.fBottom = offset + height;
1544 DrawRect(draw, paint, r, textSize);
1545 }
1546 if (flags & SkPaint::kStrikeThruText_Flag) {
1547 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1548 start.fY);
1549 r.fTop = offset;
1550 r.fBottom = offset + height;
1551 DrawRect(draw, paint, r, textSize);
1552 }
1553 }
1554}
1555
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556void SkCanvas::drawText(const void* text, size_t byteLength,
1557 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001558 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559
1560 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001561 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001562 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001563 DrawTextDecorations(iter, dfp.paint(),
1564 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565 }
1566
reed@google.com4e2b3d32011-04-07 14:18:59 +00001567 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568}
1569
1570void SkCanvas::drawPosText(const void* text, size_t byteLength,
1571 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001572 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001573
reed@android.com8a1c16f2008-12-17 15:59:43 +00001574 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001575 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001576 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001577 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578 }
reed@google.com4b226022011-01-11 18:32:13 +00001579
reed@google.com4e2b3d32011-04-07 14:18:59 +00001580 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581}
1582
1583void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1584 const SkScalar xpos[], SkScalar constY,
1585 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001586 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001587
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001589 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001591 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592 }
reed@google.com4b226022011-01-11 18:32:13 +00001593
reed@google.com4e2b3d32011-04-07 14:18:59 +00001594 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001595}
1596
1597void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1598 const SkPath& path, const SkMatrix* matrix,
1599 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001600 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601
1602 while (iter.next()) {
1603 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001604 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605 }
1606
reed@google.com4e2b3d32011-04-07 14:18:59 +00001607 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001610#ifdef ANDROID
1611void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1612 const SkPoint pos[], const SkPaint& paint,
1613 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001614 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001615
1616 while (iter.next()) {
1617 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001618 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001619 }
1620
reed@google.com4e2b3d32011-04-07 14:18:59 +00001621 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001622}
1623#endif
1624
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1626 const SkPoint verts[], const SkPoint texs[],
1627 const SkColor colors[], SkXfermode* xmode,
1628 const uint16_t indices[], int indexCount,
1629 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001630 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001631
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 while (iter.next()) {
1633 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001634 colors, xmode, indices, indexCount,
1635 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636 }
reed@google.com4b226022011-01-11 18:32:13 +00001637
reed@google.com4e2b3d32011-04-07 14:18:59 +00001638 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639}
1640
reed@android.comcb608442009-12-04 21:32:27 +00001641void SkCanvas::drawData(const void* data, size_t length) {
1642 // do nothing. Subclasses may do something with the data
1643}
1644
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645//////////////////////////////////////////////////////////////////////////////
1646// These methods are NOT virtual, and therefore must call back into virtual
1647// methods, rather than actually drawing themselves.
1648//////////////////////////////////////////////////////////////////////////////
1649
1650void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001651 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652 SkPaint paint;
1653
1654 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001655 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001656 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657 }
1658 this->drawPaint(paint);
1659}
1660
reed@android.com845fdac2009-06-23 03:01:32 +00001661void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001662 SkPaint paint;
1663
1664 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001665 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001666 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001667 }
1668 this->drawPaint(paint);
1669}
1670
1671void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1672 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001673
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674 pt.set(x, y);
1675 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1676}
1677
1678void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1679 SkPoint pt;
1680 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001681
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682 pt.set(x, y);
1683 paint.setColor(color);
1684 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1685}
1686
1687void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1688 const SkPaint& paint) {
1689 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001690
reed@android.com8a1c16f2008-12-17 15:59:43 +00001691 pts[0].set(x0, y0);
1692 pts[1].set(x1, y1);
1693 this->drawPoints(kLines_PointMode, 2, pts, paint);
1694}
1695
1696void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1697 SkScalar right, SkScalar bottom,
1698 const SkPaint& paint) {
1699 SkRect r;
1700
1701 r.set(left, top, right, bottom);
1702 this->drawRect(r, paint);
1703}
1704
1705void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1706 const SkPaint& paint) {
1707 if (radius < 0) {
1708 radius = 0;
1709 }
1710
1711 SkRect r;
1712 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001713
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 if (paint.canComputeFastBounds()) {
1715 SkRect storage;
1716 if (this->quickReject(paint.computeFastBounds(r, &storage),
1717 paint2EdgeType(&paint))) {
1718 return;
1719 }
1720 }
reed@google.com4b226022011-01-11 18:32:13 +00001721
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722 SkPath path;
1723 path.addOval(r);
1724 this->drawPath(path, paint);
1725}
1726
1727void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1728 const SkPaint& paint) {
1729 if (rx > 0 && ry > 0) {
1730 if (paint.canComputeFastBounds()) {
1731 SkRect storage;
1732 if (this->quickReject(paint.computeFastBounds(r, &storage),
1733 paint2EdgeType(&paint))) {
1734 return;
1735 }
1736 }
1737
1738 SkPath path;
1739 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1740 this->drawPath(path, paint);
1741 } else {
1742 this->drawRect(r, paint);
1743 }
1744}
1745
1746void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1747 if (paint.canComputeFastBounds()) {
1748 SkRect storage;
1749 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1750 paint2EdgeType(&paint))) {
1751 return;
1752 }
1753 }
1754
1755 SkPath path;
1756 path.addOval(oval);
1757 this->drawPath(path, paint);
1758}
1759
1760void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1761 SkScalar sweepAngle, bool useCenter,
1762 const SkPaint& paint) {
1763 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1764 this->drawOval(oval, paint);
1765 } else {
1766 SkPath path;
1767 if (useCenter) {
1768 path.moveTo(oval.centerX(), oval.centerY());
1769 }
1770 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1771 if (useCenter) {
1772 path.close();
1773 }
1774 this->drawPath(path, paint);
1775 }
1776}
1777
1778void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1779 const SkPath& path, SkScalar hOffset,
1780 SkScalar vOffset, const SkPaint& paint) {
1781 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001782
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 matrix.setTranslate(hOffset, vOffset);
1784 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1785}
1786
reed@android.comf76bacf2009-05-13 14:00:33 +00001787///////////////////////////////////////////////////////////////////////////////
1788
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789void SkCanvas::drawPicture(SkPicture& picture) {
1790 int saveCount = save();
1791 picture.draw(this);
1792 restoreToCount(saveCount);
1793}
1794
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795///////////////////////////////////////////////////////////////////////////////
1796///////////////////////////////////////////////////////////////////////////////
1797
1798SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001799 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800
1801 SkASSERT(canvas);
1802
1803 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1804 fDone = !fImpl->next();
1805}
1806
1807SkCanvas::LayerIter::~LayerIter() {
1808 fImpl->~SkDrawIter();
1809}
1810
1811void SkCanvas::LayerIter::next() {
1812 fDone = !fImpl->next();
1813}
1814
1815SkDevice* SkCanvas::LayerIter::device() const {
1816 return fImpl->getDevice();
1817}
1818
1819const SkMatrix& SkCanvas::LayerIter::matrix() const {
1820 return fImpl->getMatrix();
1821}
1822
1823const SkPaint& SkCanvas::LayerIter::paint() const {
1824 const SkPaint* paint = fImpl->getPaint();
1825 if (NULL == paint) {
1826 paint = &fDefaultPaint;
1827 }
1828 return *paint;
1829}
1830
1831const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1832int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1833int SkCanvas::LayerIter::y() const { return fImpl->getY(); }