blob: e9fcb57f01f56bfe50683a0b3f755b2a633a9f47 [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@google.com7c202932011-12-14 18:48:05 +0000407 fLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408
409 fMCRec = (MCRec*)fMCStack.push_back();
410 new (fMCRec) MCRec(NULL, 0);
411
412 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
413 fMCRec->fTopLayer = fMCRec->fLayer;
414 fMCRec->fNext = NULL;
415
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000416 fExternalMatrix.reset();
417 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000418 fUseExternalMatrix = false;
419
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420 return this->setDevice(device);
421}
422
reed@google.comcde92112011-07-06 20:00:52 +0000423SkCanvas::SkCanvas()
424: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000425 inc_canvas();
reed@google.comcde92112011-07-06 20:00:52 +0000426
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000427 this->init(NULL);
428}
429
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000431 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 inc_canvas();
433
434 this->init(device);
435}
436
437SkCanvas::SkCanvas(const SkBitmap& bitmap)
438 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
439 inc_canvas();
440
reed@google.comcde92112011-07-06 20:00:52 +0000441 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442}
443
444SkCanvas::~SkCanvas() {
445 // free up the contents of our deque
446 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000447 SkASSERT(0 == fLayerCount);
448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 this->internalRestore(); // restore the last, since we're going away
450
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000451 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000452
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 dec_canvas();
454}
455
456SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
457 SkRefCnt_SafeAssign(fBounder, bounder);
458 return bounder;
459}
460
461SkDrawFilter* SkCanvas::getDrawFilter() const {
462 return fMCRec->fFilter;
463}
464
465SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
466 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
467 return filter;
468}
469
470///////////////////////////////////////////////////////////////////////////////
471
reed@google.com210ce002011-11-01 14:24:23 +0000472SkISize SkCanvas::getDeviceSize() const {
473 SkDevice* d = this->getDevice();
474 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
475}
476
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477SkDevice* SkCanvas::getDevice() const {
478 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000479 SkDeque::F2BIter iter(fMCStack);
480 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000481 SkASSERT(rec && rec->fLayer);
482 return rec->fLayer->fDevice;
483}
484
reed@google.com9266fed2011-03-30 00:18:03 +0000485SkDevice* SkCanvas::getTopDevice() const {
486 return fMCRec->fTopLayer->fDevice;
487}
488
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489SkDevice* SkCanvas::setDevice(SkDevice* device) {
490 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000491 SkDeque::F2BIter iter(fMCStack);
492 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 SkASSERT(rec && rec->fLayer);
494 SkDevice* rootDevice = rec->fLayer->fDevice;
495
496 if (rootDevice == device) {
497 return device;
498 }
reed@google.com4b226022011-01-11 18:32:13 +0000499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 /* Notify the devices that they are going in/out of scope, so they can do
501 things like lock/unlock their pixels, etc.
502 */
503 if (device) {
504 device->lockPixels();
505 }
506 if (rootDevice) {
507 rootDevice->unlockPixels();
508 }
509
510 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
511 rootDevice = device;
512
513 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000514
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 /* Now we update our initial region to have the bounds of the new device,
516 and then intersect all of the clips in our stack with these bounds,
517 to ensure that we can't draw outside of the device's bounds (and trash
518 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000519
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 NOTE: this is only a partial-fix, since if the new device is larger than
521 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000522 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
524 reconstruct the correct clips, so this approximation will have to do.
525 The caller really needs to restore() back to the base if they want to
526 accurately take advantage of the new device bounds.
527 */
528
529 if (NULL == device) {
reed@google.com00177082011-10-12 14:34:30 +0000530 rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000532 (void)rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000534 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 } else {
536 // compute our total bounds for all devices
537 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000538
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 bounds.set(0, 0, device->width(), device->height());
540
541 // now jam our 1st clip to be bounds, and intersect the rest with that
reed@google.com00177082011-10-12 14:34:30 +0000542 rec->fRasterClip->setRect(bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000544 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 }
546 }
547 return device;
548}
549
reed@google.comaf951c92011-06-16 19:10:39 +0000550SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
551 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 device->unref();
553 return device;
554}
555
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000556bool SkCanvas::readPixels(SkBitmap* bitmap,
557 int x, int y,
558 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000559 SkDevice* device = this->getDevice();
560 if (!device) {
561 return false;
562 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000563 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000564}
565
bsalomon@google.comc6980972011-11-02 19:57:21 +0000566bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000567 SkDevice* device = this->getDevice();
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000568
569 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000570 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000571 if (!bounds.intersect(srcRect)) {
572 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000573 }
574
575 SkBitmap tmp;
576 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
577 bounds.height());
578 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
579 bitmap->swap(tmp);
580 return true;
581 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000582 return false;
583 }
reed@google.com51df9e32010-12-23 19:29:18 +0000584}
585
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000586void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
587 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000588 SkDevice* device = this->getDevice();
589 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000590 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000591 }
592}
593
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594//////////////////////////////////////////////////////////////////////////////
595
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596void SkCanvas::updateDeviceCMCache() {
597 if (fDeviceCMDirty) {
598 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000599 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000601
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000603 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000604 if (fUseExternalMatrix) {
605 layer->updateExternalMatrix(fExternalMatrix,
606 fExternalInverse);
607 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000609 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000611 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000612 if (fUseExternalMatrix) {
613 layer->updateExternalMatrix(fExternalMatrix,
614 fExternalInverse);
615 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 } while ((layer = layer->fNext) != NULL);
617 }
618 fDeviceCMDirty = false;
619 }
620}
621
reed@android.comf2b98d62010-12-20 18:26:13 +0000622void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000623 const SkRegion& clip,
624 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000625 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000626 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000627 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000628 fLastDeviceToGainFocus = device;
629 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630}
631
632///////////////////////////////////////////////////////////////////////////////
633
634int SkCanvas::internalSave(SaveFlags flags) {
635 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000636
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 MCRec* newTop = (MCRec*)fMCStack.push_back();
638 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000639
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640 newTop->fNext = fMCRec;
641 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000642
reed@google.com5c3d1472011-02-22 19:12:23 +0000643 fClipStack.save();
644 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
645
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 return saveCount;
647}
648
649int SkCanvas::save(SaveFlags flags) {
650 // call shared impl
651 return this->internalSave(flags);
652}
653
654#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
655#define C16MASK (1 << SkBitmap::kRGB_565_Config)
656#define C8MASK (1 << SkBitmap::kA8_Config)
657
658static SkBitmap::Config resolve_config(SkCanvas* canvas,
659 const SkIRect& bounds,
660 SkCanvas::SaveFlags flags,
661 bool* isOpaque) {
662 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
663
664#if 0
665 // loop through and union all the configs we may draw into
666 uint32_t configMask = 0;
667 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
668 {
669 SkDevice* device = canvas->getLayerDevice(i);
670 if (device->intersects(bounds))
671 configMask |= 1 << device->config();
672 }
673
674 // if the caller wants alpha or fullcolor, we can't return 565
675 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
676 SkCanvas::kHasAlphaLayer_SaveFlag))
677 configMask &= ~C16MASK;
678
679 switch (configMask) {
680 case C8MASK: // if we only have A8, return that
681 return SkBitmap::kA8_Config;
682
683 case C16MASK: // if we only have 565, return that
684 return SkBitmap::kRGB_565_Config;
685
686 default:
687 return SkBitmap::kARGB_8888_Config; // default answer
688 }
689#else
690 return SkBitmap::kARGB_8888_Config; // default answer
691#endif
692}
693
694static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
695 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
696}
697
698int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
699 SaveFlags flags) {
700 // do this before we create the layer. We don't call the public save() since
701 // that would invoke a possibly overridden virtual
702 int count = this->internalSave(flags);
703
704 fDeviceCMDirty = true;
705
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000706 SkIRect clipBounds;
707 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000708 return count;
709 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000711 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000712 if (NULL != bounds) {
713 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 this->getTotalMatrix().mapRect(&r, *bounds);
716 r.roundOut(&ir);
717 // early exit if the layer's bounds are clipped out
718 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000719 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000720 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000721 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 return count;
723 }
724 } else { // no user bounds, so just use the clip
725 ir = clipBounds;
726 }
727
reed@google.com5c3d1472011-02-22 19:12:23 +0000728 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 // early exit if the clip is now empty
730 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000731 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 return count;
733 }
734
735 bool isOpaque;
736 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
737
reed@google.com76dd2772012-01-05 21:15:07 +0000738 SkDevice* device;
739 if (paint && paint->getImageFilter()) {
740 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
741 isOpaque);
742 } else {
743 device = this->createLayerDevice(config, ir.width(), ir.height(),
744 isOpaque);
745 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000746 if (NULL == device) {
747 SkDebugf("Unable to create device for layer.");
748 return count;
749 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000750
reed@google.com6f8f2922011-03-04 22:27:10 +0000751 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
753 device->unref();
754
755 layer->fNext = fMCRec->fTopLayer;
756 fMCRec->fLayer = layer;
757 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
758
reed@google.com7c202932011-12-14 18:48:05 +0000759 fLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760 return count;
761}
762
763int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
764 SaveFlags flags) {
765 if (0xFF == alpha) {
766 return this->saveLayer(bounds, NULL, flags);
767 } else {
768 SkPaint tmpPaint;
769 tmpPaint.setAlpha(alpha);
770 return this->saveLayer(bounds, &tmpPaint, flags);
771 }
772}
773
774void SkCanvas::restore() {
775 // check for underflow
776 if (fMCStack.count() > 1) {
777 this->internalRestore();
778 }
779}
780
781void SkCanvas::internalRestore() {
782 SkASSERT(fMCStack.count() != 0);
783
784 fDeviceCMDirty = true;
785 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000786 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787
reed@google.com5c3d1472011-02-22 19:12:23 +0000788 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000789 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 DeviceCM* layer = fMCRec->fLayer; // may be null
791 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
792 fMCRec->fLayer = NULL;
793
794 // now do the normal restore()
795 fMCRec->~MCRec(); // balanced in save()
796 fMCStack.pop_back();
797 fMCRec = (MCRec*)fMCStack.back();
798
799 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
800 since if we're being recorded, we don't want to record this (the
801 recorder will have already recorded the restore).
802 */
803 if (NULL != layer) {
804 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000805 const SkIPoint& origin = layer->fDevice->getOrigin();
806 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807 layer->fPaint);
808 // reset this, since drawDevice will have set it to true
809 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000810
811 SkASSERT(fLayerCount > 0);
812 fLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 }
814 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000815 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000816
817 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818}
819
820int SkCanvas::getSaveCount() const {
821 return fMCStack.count();
822}
823
824void SkCanvas::restoreToCount(int count) {
825 // sanity check
826 if (count < 1) {
827 count = 1;
828 }
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000829
830 int n = this->getSaveCount() - count;
831 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832 this->restore();
833 }
834}
835
reed@google.com7c202932011-12-14 18:48:05 +0000836bool SkCanvas::isDrawingToLayer() const {
837 return fLayerCount > 0;
838}
839
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840/////////////////////////////////////////////////////////////////////////////
841
842// can't draw it if its empty, or its too big for a fixed-point width or height
843static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000844 return bitmap.width() <= 0 || bitmap.height() <= 0
845#ifndef SK_ALLOW_OVER_32K_BITMAPS
846 || bitmap.width() > 32767 || bitmap.height() > 32767
847#endif
848 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849}
850
reed@android.comf2b98d62010-12-20 18:26:13 +0000851void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 const SkMatrix& matrix, const SkPaint* paint) {
853 if (reject_bitmap(bitmap)) {
854 return;
855 }
856
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000857 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000859 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000861 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862}
863
reed@google.com76dd2772012-01-05 21:15:07 +0000864#include "SkImageFilter.h"
865
866class DeviceImageFilterProxy : public SkImageFilter::Proxy {
867public:
868 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
869
870 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE;
871 virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
872 const SkMatrix& ctm,
873 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
874
875private:
876 SkDevice* fDevice;
877};
878
879SkDevice* DeviceImageFilterProxy::createDevice(int w, int h) {
880 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
881 w, h, false);
882}
883
884bool DeviceImageFilterProxy::filterImage(SkImageFilter* filter,
885 const SkBitmap& src,
886 const SkMatrix& ctm,
887 SkBitmap* result,
888 SkIPoint* offset) {
889 return fDevice->filterImage(filter, src, ctm, result, offset);
890}
891
reed@android.com8a1c16f2008-12-17 15:59:43 +0000892void SkCanvas::drawDevice(SkDevice* device, int x, int y,
893 const SkPaint* paint) {
894 SkPaint tmp;
895 if (NULL == paint) {
896 tmp.setDither(true);
897 paint = &tmp;
898 }
reed@google.com4b226022011-01-11 18:32:13 +0000899
reed@google.com4e2b3d32011-04-07 14:18:59 +0000900 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 while (iter.next()) {
reed@google.com76dd2772012-01-05 21:15:07 +0000902 paint = &looper.paint();
903 SkImageFilter* filter = paint->getImageFilter();
904 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
905 if (filter) {
906 DeviceImageFilterProxy proxy(device);
907 SkBitmap dst;
908 const SkBitmap& src = device->accessBitmap(false);
909 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
910 SkPaint tmp(*paint);
911 tmp.setImageFilter(NULL);
912 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
913 }
914 } else {
915 iter.fDevice->drawDevice(iter, device, pos.x(), pos.y(), *paint);
916 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000918 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000919}
920
921/////////////////////////////////////////////////////////////////////////////
922
923bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
924 fDeviceCMDirty = true;
925 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000926 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 return fMCRec->fMatrix->preTranslate(dx, dy);
928}
929
930bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
931 fDeviceCMDirty = true;
932 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000933 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 return fMCRec->fMatrix->preScale(sx, sy);
935}
936
937bool SkCanvas::rotate(SkScalar degrees) {
938 fDeviceCMDirty = true;
939 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000940 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 return fMCRec->fMatrix->preRotate(degrees);
942}
943
944bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
945 fDeviceCMDirty = true;
946 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000947 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 return fMCRec->fMatrix->preSkew(sx, sy);
949}
950
951bool SkCanvas::concat(const SkMatrix& matrix) {
952 fDeviceCMDirty = true;
953 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000954 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000955 return fMCRec->fMatrix->preConcat(matrix);
956}
957
958void SkCanvas::setMatrix(const SkMatrix& matrix) {
959 fDeviceCMDirty = true;
960 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000961 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000962 *fMCRec->fMatrix = matrix;
963}
964
965// this is not virtual, so it must call a virtual method so that subclasses
966// will see its action
967void SkCanvas::resetMatrix() {
968 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000969
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970 matrix.reset();
971 this->setMatrix(matrix);
972}
973
974//////////////////////////////////////////////////////////////////////////////
975
reed@google.comc42d35d2011-10-12 11:57:42 +0000976bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000977 AutoValidateClip avc(this);
978
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 fDeviceCMDirty = true;
980 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000981 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000982
983 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000984 // for these simpler matrices, we can stay a rect ever after applying
985 // the matrix. This means we don't have to a) make a path, and b) tell
986 // the region code to scan-convert the path, only to discover that it
987 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989
990 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +0000991 fClipStack.clipDevRect(r, op, doAA);
992 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +0000994 // since we're rotate or some such thing, we convert the rect to a path
995 // and clip against that, since it can handle any matrix. However, to
996 // avoid recursion in the case where we are subclassed (e.g. Pictures)
997 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 SkPath path;
999
1000 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001001 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 }
1003}
1004
reed@google.com00177082011-10-12 14:34:30 +00001005static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001006 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001007 // base is used to limit the size (and therefore memory allocation) of the
1008 // region that results from scan converting devPath.
1009 SkRegion base;
1010
reed@google.com819c9212011-02-23 18:56:55 +00001011 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001012 // since we are intersect, we can do better (tighter) with currRgn's
1013 // bounds, than just using the device. However, if currRgn is complex,
1014 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001015 if (currClip->isRect()) {
1016 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001017 } else {
reed@google.com00177082011-10-12 14:34:30 +00001018 base.setRect(currClip->getBounds());
1019 SkRasterClip clip;
1020 clip.setPath(devPath, base, doAA);
1021 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001022 }
reed@google.com819c9212011-02-23 18:56:55 +00001023 } else {
reed@google.com819c9212011-02-23 18:56:55 +00001024 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
1025 base.setRect(0, 0, bm.width(), bm.height());
1026
1027 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001028 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001029 } else {
reed@google.com00177082011-10-12 14:34:30 +00001030 SkRasterClip clip;
1031 clip.setPath(devPath, base, doAA);
1032 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001033 }
1034 }
1035}
1036
reed@google.comc42d35d2011-10-12 11:57:42 +00001037bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001038 AutoValidateClip avc(this);
1039
reed@android.com8a1c16f2008-12-17 15:59:43 +00001040 fDeviceCMDirty = true;
1041 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001042 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043
1044 SkPath devPath;
1045 path.transform(*fMCRec->fMatrix, &devPath);
1046
reed@google.comfe701122011-11-08 19:41:23 +00001047 // Check if the transfomation, or the original path itself
1048 // made us empty. Note this can also happen if we contained NaN
1049 // values. computing the bounds detects this, and will set our
1050 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1051 if (devPath.getBounds().isEmpty()) {
1052 // resetting the path will remove any NaN or other wanky values
1053 // that might upset our scan converter.
1054 devPath.reset();
1055 }
1056
reed@google.com5c3d1472011-02-22 19:12:23 +00001057 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001058 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001059
reed@google.com00177082011-10-12 14:34:30 +00001060 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061}
1062
1063bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001064 AutoValidateClip avc(this);
1065
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 fDeviceCMDirty = true;
1067 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001068 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069
reed@google.com5c3d1472011-02-22 19:12:23 +00001070 // todo: signal fClipStack that we have a region, and therefore (I guess)
1071 // we have to ignore it, and use the region directly?
1072 fClipStack.clipDevRect(rgn.getBounds());
1073
reed@google.com00177082011-10-12 14:34:30 +00001074 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075}
1076
reed@google.com819c9212011-02-23 18:56:55 +00001077#ifdef SK_DEBUG
1078void SkCanvas::validateClip() const {
1079 // construct clipRgn from the clipstack
1080 const SkDevice* device = this->getDevice();
1081 SkIRect ir;
1082 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001083 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001084
1085 SkClipStack::B2FIter iter(fClipStack);
1086 const SkClipStack::B2FIter::Clip* clip;
1087 while ((clip = iter.next()) != NULL) {
1088 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001089 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001090 } else if (clip->fRect) {
1091 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001092 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001093 } else {
reed@google.com00177082011-10-12 14:34:30 +00001094 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001095 }
1096 }
1097
reed@google.com6f8f2922011-03-04 22:27:10 +00001098#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001099 // now compare against the current rgn
1100 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001101 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001102#endif
reed@google.com819c9212011-02-23 18:56:55 +00001103}
1104#endif
1105
reed@google.com5c3d1472011-02-22 19:12:23 +00001106///////////////////////////////////////////////////////////////////////////////
1107
reed@android.comba09de42010-02-05 20:46:05 +00001108void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001109 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001110 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1111 fLocalBoundsCompareTypeBW;
1112
1113 if (!this->getClipBounds(&r, et)) {
1114 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001116 rCompare.set(SkScalarToCompareType(r.fLeft),
1117 SkScalarToCompareType(r.fTop),
1118 SkScalarToCompareType(r.fRight),
1119 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 }
1121}
1122
reed@android.comd252db02009-04-01 18:31:44 +00001123/* current impl ignores edgetype, and relies on
1124 getLocalClipBoundsCompareType(), which always returns a value assuming
1125 antialiasing (worst case)
1126 */
reed@android.comba09de42010-02-05 20:46:05 +00001127bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001128
reed@google.com16078632011-12-06 18:56:37 +00001129 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001130 return true;
1131
reed@google.com00177082011-10-12 14:34:30 +00001132 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 return true;
1134 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135
tomhudson@google.com8d430182011-06-06 19:11:19 +00001136 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001137 SkRect dst;
1138 fMCRec->fMatrix->mapRect(&dst, rect);
1139 SkIRect idst;
1140 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001141 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001142 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001143 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001144
reed@android.coma380ae42009-07-21 01:17:02 +00001145 // for speed, do the most likely reject compares first
1146 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1147 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1148 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1149 return true;
1150 }
1151 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1152 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1153 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1154 return true;
1155 }
1156 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158}
1159
1160bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001161 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162}
1163
1164bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1165 /* current impl ignores edgetype, and relies on
1166 getLocalClipBoundsCompareType(), which always returns a value assuming
1167 antialiasing (worst case)
1168 */
1169
reed@google.com00177082011-10-12 14:34:30 +00001170 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 return true;
1172 }
reed@google.com4b226022011-01-11 18:32:13 +00001173
reed@android.comaefd2bc2009-03-30 21:02:14 +00001174 SkScalarCompareType userT = SkScalarToCompareType(top);
1175 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001176
reed@android.com8a1c16f2008-12-17 15:59:43 +00001177 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001178 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 if (userT >= userB) {
1180 return true;
1181 }
reed@google.com4b226022011-01-11 18:32:13 +00001182
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183 // check if we are above or below the local clip bounds
1184 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1185 return userT >= clipR.fBottom || userB <= clipR.fTop;
1186}
1187
1188bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001189 SkIRect ibounds;
1190 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191 return false;
1192 }
1193
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001194 SkMatrix inverse;
1195 // if we can't invert the CTM, we can't return local clip bounds
1196 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001197 if (bounds) {
1198 bounds->setEmpty();
1199 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001200 return false;
1201 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001203 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001204 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205 // adjust it outwards if we are antialiasing
1206 int inset = (kAA_EdgeType == et);
1207 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1208 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209 inverse.mapRect(bounds, r);
1210 }
1211 return true;
1212}
1213
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001214bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001215 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001216 if (clip.isEmpty()) {
1217 if (bounds) {
1218 bounds->setEmpty();
1219 }
1220 return false;
1221 }
1222
1223 if (NULL != bounds) {
1224 *bounds = clip.getBounds();
1225 }
1226 return true;
1227}
1228
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229const SkMatrix& SkCanvas::getTotalMatrix() const {
1230 return *fMCRec->fMatrix;
1231}
1232
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001233SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001234 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1235 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001236 return kComplex_ClipType;
1237}
1238
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001240 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241}
1242
reed@google.com7d7ca792011-02-23 22:39:18 +00001243const SkClipStack& SkCanvas::getTotalClipStack() const {
1244 return fClipStack;
1245}
1246
reed@android.comf2b98d62010-12-20 18:26:13 +00001247void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1248 if (NULL == matrix || matrix->isIdentity()) {
1249 if (fUseExternalMatrix) {
1250 fDeviceCMDirty = true;
1251 }
1252 fUseExternalMatrix = false;
1253 } else {
1254 fUseExternalMatrix = true;
1255 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001256
reed@android.comf2b98d62010-12-20 18:26:13 +00001257 fExternalMatrix = *matrix;
1258 matrix->invert(&fExternalInverse);
1259 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001260}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001261
bsalomon@google.come97f0852011-06-17 13:10:25 +00001262SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1263 int width, int height,
1264 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001265 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001266 if (device) {
1267 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1268 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001269 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001270 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001271 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272}
1273
bsalomon@google.come97f0852011-06-17 13:10:25 +00001274SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1275 int width, int height,
1276 bool isOpaque) {
1277 SkDevice* device = this->getDevice();
1278 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001279 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001280 } else {
1281 return NULL;
1282 }
1283}
1284
1285
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286//////////////////////////////////////////////////////////////////////////////
1287// These are the virtual drawing methods
1288//////////////////////////////////////////////////////////////////////////////
1289
reed@google.com2a981812011-04-14 18:59:28 +00001290void SkCanvas::clear(SkColor color) {
1291 SkDrawIter iter(this);
1292
1293 while (iter.next()) {
1294 iter.fDevice->clear(color);
1295 }
1296}
1297
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001299 this->internalDrawPaint(paint);
1300}
1301
1302void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001303 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304
1305 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001306 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 }
1308
reed@google.com4e2b3d32011-04-07 14:18:59 +00001309 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310}
1311
1312void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1313 const SkPaint& paint) {
1314 if ((long)count <= 0) {
1315 return;
1316 }
1317
1318 SkASSERT(pts != NULL);
1319
reed@google.com4e2b3d32011-04-07 14:18:59 +00001320 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001321
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001323 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 }
reed@google.com4b226022011-01-11 18:32:13 +00001325
reed@google.com4e2b3d32011-04-07 14:18:59 +00001326 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327}
1328
1329void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1330 if (paint.canComputeFastBounds()) {
1331 SkRect storage;
1332 if (this->quickReject(paint.computeFastBounds(r, &storage),
1333 paint2EdgeType(&paint))) {
1334 return;
1335 }
1336 }
reed@google.com4b226022011-01-11 18:32:13 +00001337
reed@google.com4e2b3d32011-04-07 14:18:59 +00001338 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339
1340 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001341 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001342 }
1343
reed@google.com4e2b3d32011-04-07 14:18:59 +00001344 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345}
1346
1347void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001348 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001349 SkRect storage;
1350 const SkRect& bounds = path.getBounds();
1351 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001352 paint2EdgeType(&paint))) {
1353 return;
1354 }
1355 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001356 if (path.isEmpty()) {
1357 if (path.isInverseFillType()) {
1358 this->internalDrawPaint(paint);
1359 }
1360 return;
1361 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362
reed@google.com4e2b3d32011-04-07 14:18:59 +00001363 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001364
1365 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001366 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 }
1368
reed@google.com4e2b3d32011-04-07 14:18:59 +00001369 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001370}
1371
1372void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1373 const SkPaint* paint) {
1374 SkDEBUGCODE(bitmap.validate();)
1375
reed@google.com3d608122011-11-21 15:16:16 +00001376 if (NULL == paint || paint->canComputeFastBounds()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 SkRect fastBounds;
1378 fastBounds.set(x, y,
1379 x + SkIntToScalar(bitmap.width()),
1380 y + SkIntToScalar(bitmap.height()));
1381 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1382 return;
1383 }
1384 }
reed@google.com4b226022011-01-11 18:32:13 +00001385
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386 SkMatrix matrix;
1387 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001388 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389}
1390
reed@google.com9987ec32011-09-07 11:57:52 +00001391// this one is non-virtual, so it can be called safely by other canvas apis
1392void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1393 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001394 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1395 return;
1396 }
reed@google.com9987ec32011-09-07 11:57:52 +00001397
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001399 if (NULL == paint || paint->canComputeFastBounds()) {
1400 if (this->quickReject(dst, paint2EdgeType(paint))) {
1401 return;
1402 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403 }
reed@google.com3d608122011-11-21 15:16:16 +00001404
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001406
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001408 SkRect tmpSrc;
1409 if (src) {
1410 tmpSrc.set(*src);
1411 // if the extract process clipped off the top or left of the
1412 // original, we adjust for that here to get the position right.
1413 if (tmpSrc.fLeft > 0) {
1414 tmpSrc.fRight -= tmpSrc.fLeft;
1415 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001416 }
reed@android.com87899992009-10-16 14:48:38 +00001417 if (tmpSrc.fTop > 0) {
1418 tmpSrc.fBottom -= tmpSrc.fTop;
1419 tmpSrc.fTop = 0;
1420 }
1421 } else {
1422 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1423 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 }
reed@android.com87899992009-10-16 14:48:38 +00001425 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001426
reed@android.comf2b98d62010-12-20 18:26:13 +00001427 // ensure that src is "valid" before we pass it to our internal routines
1428 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1429 SkIRect tmpISrc;
1430 if (src) {
1431 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001432 if (!tmpISrc.intersect(*src)) {
1433 return;
1434 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001435 src = &tmpISrc;
1436 }
1437 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438}
1439
reed@google.com9987ec32011-09-07 11:57:52 +00001440void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1441 const SkRect& dst, const SkPaint* paint) {
1442 SkDEBUGCODE(bitmap.validate();)
1443 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1444}
1445
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1447 const SkPaint* paint) {
1448 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001449 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450}
1451
reed@android.comf2b98d62010-12-20 18:26:13 +00001452void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1453 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001455
reed@google.com4e2b3d32011-04-07 14:18:59 +00001456 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001459 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460 }
reed@android.com9b039062009-02-11 15:09:58 +00001461
reed@google.com4e2b3d32011-04-07 14:18:59 +00001462 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463}
1464
reed@google.com9987ec32011-09-07 11:57:52 +00001465void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1466 const SkIRect& center, const SkRect& dst,
1467 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001468 if (NULL == paint || paint->canComputeFastBounds()) {
1469 if (this->quickReject(dst, paint2EdgeType(paint))) {
1470 return;
1471 }
1472 }
1473
reed@google.com9987ec32011-09-07 11:57:52 +00001474 const int32_t w = bitmap.width();
1475 const int32_t h = bitmap.height();
1476
1477 SkIRect c = center;
1478 // pin center to the bounds of the bitmap
1479 c.fLeft = SkMax32(0, center.fLeft);
1480 c.fTop = SkMax32(0, center.fTop);
1481 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1482 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1483
1484 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1485 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1486 SkScalar dstX[4] = {
1487 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1488 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1489 };
1490 SkScalar dstY[4] = {
1491 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1492 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1493 };
1494
1495 if (dstX[1] > dstX[2]) {
1496 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1497 dstX[2] = dstX[1];
1498 }
1499
1500 if (dstY[1] > dstY[2]) {
1501 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1502 dstY[2] = dstY[1];
1503 }
1504
1505 SkIRect s;
1506 SkRect d;
1507 for (int y = 0; y < 3; y++) {
1508 s.fTop = srcY[y];
1509 s.fBottom = srcY[y+1];
1510 d.fTop = dstY[y];
1511 d.fBottom = dstY[y+1];
1512 for (int x = 0; x < 3; x++) {
1513 s.fLeft = srcX[x];
1514 s.fRight = srcX[x+1];
1515 d.fLeft = dstX[x];
1516 d.fRight = dstX[x+1];
1517 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1518 }
1519 }
1520}
1521
1522void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1523 const SkRect& dst, const SkPaint* paint) {
1524 SkDEBUGCODE(bitmap.validate();)
1525
1526 // Need a device entry-point, so gpu can use a mesh
1527 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1528}
1529
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1531 const SkPaint* paint) {
1532 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001533
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534 if (reject_bitmap(bitmap)) {
1535 return;
1536 }
reed@google.com4b226022011-01-11 18:32:13 +00001537
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 SkPaint tmp;
1539 if (NULL == paint) {
1540 paint = &tmp;
1541 }
reed@google.com4b226022011-01-11 18:32:13 +00001542
reed@google.com4e2b3d32011-04-07 14:18:59 +00001543 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001544
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545 while (iter.next()) {
1546 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001547 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001549 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550}
1551
reed@google.comf67e4cf2011-03-15 20:56:58 +00001552class SkDeviceFilteredPaint {
1553public:
1554 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1555 SkDevice::TextFlags flags;
1556 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001557 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001558 newPaint->setFlags(flags.fFlags);
1559 newPaint->setHinting(flags.fHinting);
1560 fPaint = newPaint;
1561 } else {
1562 fPaint = &paint;
1563 }
1564 }
1565
reed@google.comf67e4cf2011-03-15 20:56:58 +00001566 const SkPaint& paint() const { return *fPaint; }
1567
1568private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001569 const SkPaint* fPaint;
1570 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001571};
1572
bungeman@google.com52c748b2011-08-22 21:30:43 +00001573void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1574 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001575 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001576 draw.fDevice->drawRect(draw, r, paint);
1577 } else {
1578 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001579 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001580 draw.fDevice->drawRect(draw, r, p);
1581 }
1582}
1583
1584void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1585 const char text[], size_t byteLength,
1586 SkScalar x, SkScalar y) {
1587 SkASSERT(byteLength == 0 || text != NULL);
1588
1589 // nothing to draw
1590 if (text == NULL || byteLength == 0 ||
1591 draw.fClip->isEmpty() ||
1592 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1593 return;
1594 }
1595
1596 SkScalar width = 0;
1597 SkPoint start;
1598
1599 start.set(0, 0); // to avoid warning
1600 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1601 SkPaint::kStrikeThruText_Flag)) {
1602 width = paint.measureText(text, byteLength);
1603
1604 SkScalar offsetX = 0;
1605 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1606 offsetX = SkScalarHalf(width);
1607 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1608 offsetX = width;
1609 }
1610 start.set(x - offsetX, y);
1611 }
1612
1613 if (0 == width) {
1614 return;
1615 }
1616
1617 uint32_t flags = paint.getFlags();
1618
1619 if (flags & (SkPaint::kUnderlineText_Flag |
1620 SkPaint::kStrikeThruText_Flag)) {
1621 SkScalar textSize = paint.getTextSize();
1622 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1623 SkRect r;
1624
1625 r.fLeft = start.fX;
1626 r.fRight = start.fX + width;
1627
1628 if (flags & SkPaint::kUnderlineText_Flag) {
1629 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1630 start.fY);
1631 r.fTop = offset;
1632 r.fBottom = offset + height;
1633 DrawRect(draw, paint, r, textSize);
1634 }
1635 if (flags & SkPaint::kStrikeThruText_Flag) {
1636 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1637 start.fY);
1638 r.fTop = offset;
1639 r.fBottom = offset + height;
1640 DrawRect(draw, paint, r, textSize);
1641 }
1642 }
1643}
1644
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645void SkCanvas::drawText(const void* text, size_t byteLength,
1646 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001647 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001648
1649 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001650 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001651 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001652 DrawTextDecorations(iter, dfp.paint(),
1653 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654 }
1655
reed@google.com4e2b3d32011-04-07 14:18:59 +00001656 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657}
1658
1659void SkCanvas::drawPosText(const void* text, size_t byteLength,
1660 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001661 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001662
reed@android.com8a1c16f2008-12-17 15:59:43 +00001663 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001664 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001665 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001666 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001667 }
reed@google.com4b226022011-01-11 18:32:13 +00001668
reed@google.com4e2b3d32011-04-07 14:18:59 +00001669 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670}
1671
1672void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1673 const SkScalar xpos[], SkScalar constY,
1674 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001675 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001676
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001678 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001679 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001680 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681 }
reed@google.com4b226022011-01-11 18:32:13 +00001682
reed@google.com4e2b3d32011-04-07 14:18:59 +00001683 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001684}
1685
1686void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1687 const SkPath& path, const SkMatrix* matrix,
1688 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001689 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001690
1691 while (iter.next()) {
1692 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001693 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694 }
1695
reed@google.com4e2b3d32011-04-07 14:18:59 +00001696 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697}
1698
djsollen@google.com56c69772011-11-08 19:00:26 +00001699#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001700void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1701 const SkPoint pos[], const SkPaint& paint,
1702 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001703 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001704
1705 while (iter.next()) {
1706 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001707 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001708 }
1709
reed@google.com4e2b3d32011-04-07 14:18:59 +00001710 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001711}
1712#endif
1713
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1715 const SkPoint verts[], const SkPoint texs[],
1716 const SkColor colors[], SkXfermode* xmode,
1717 const uint16_t indices[], int indexCount,
1718 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001719 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001720
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721 while (iter.next()) {
1722 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001723 colors, xmode, indices, indexCount,
1724 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725 }
reed@google.com4b226022011-01-11 18:32:13 +00001726
reed@google.com4e2b3d32011-04-07 14:18:59 +00001727 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001728}
1729
reed@android.comcb608442009-12-04 21:32:27 +00001730void SkCanvas::drawData(const void* data, size_t length) {
1731 // do nothing. Subclasses may do something with the data
1732}
1733
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734//////////////////////////////////////////////////////////////////////////////
1735// These methods are NOT virtual, and therefore must call back into virtual
1736// methods, rather than actually drawing themselves.
1737//////////////////////////////////////////////////////////////////////////////
1738
1739void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001740 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741 SkPaint paint;
1742
1743 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001744 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001745 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746 }
1747 this->drawPaint(paint);
1748}
1749
reed@android.com845fdac2009-06-23 03:01:32 +00001750void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751 SkPaint paint;
1752
1753 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001754 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001755 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756 }
1757 this->drawPaint(paint);
1758}
1759
1760void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1761 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001762
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 pt.set(x, y);
1764 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1765}
1766
1767void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1768 SkPoint pt;
1769 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001770
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771 pt.set(x, y);
1772 paint.setColor(color);
1773 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1774}
1775
1776void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1777 const SkPaint& paint) {
1778 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001779
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780 pts[0].set(x0, y0);
1781 pts[1].set(x1, y1);
1782 this->drawPoints(kLines_PointMode, 2, pts, paint);
1783}
1784
1785void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1786 SkScalar right, SkScalar bottom,
1787 const SkPaint& paint) {
1788 SkRect r;
1789
1790 r.set(left, top, right, bottom);
1791 this->drawRect(r, paint);
1792}
1793
1794void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1795 const SkPaint& paint) {
1796 if (radius < 0) {
1797 radius = 0;
1798 }
1799
1800 SkRect r;
1801 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001802
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 if (paint.canComputeFastBounds()) {
1804 SkRect storage;
1805 if (this->quickReject(paint.computeFastBounds(r, &storage),
1806 paint2EdgeType(&paint))) {
1807 return;
1808 }
1809 }
reed@google.com4b226022011-01-11 18:32:13 +00001810
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 SkPath path;
1812 path.addOval(r);
1813 this->drawPath(path, paint);
1814}
1815
1816void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1817 const SkPaint& paint) {
1818 if (rx > 0 && ry > 0) {
1819 if (paint.canComputeFastBounds()) {
1820 SkRect storage;
1821 if (this->quickReject(paint.computeFastBounds(r, &storage),
1822 paint2EdgeType(&paint))) {
1823 return;
1824 }
1825 }
1826
1827 SkPath path;
1828 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1829 this->drawPath(path, paint);
1830 } else {
1831 this->drawRect(r, paint);
1832 }
1833}
1834
1835void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1836 if (paint.canComputeFastBounds()) {
1837 SkRect storage;
1838 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1839 paint2EdgeType(&paint))) {
1840 return;
1841 }
1842 }
1843
1844 SkPath path;
1845 path.addOval(oval);
1846 this->drawPath(path, paint);
1847}
1848
1849void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1850 SkScalar sweepAngle, bool useCenter,
1851 const SkPaint& paint) {
1852 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1853 this->drawOval(oval, paint);
1854 } else {
1855 SkPath path;
1856 if (useCenter) {
1857 path.moveTo(oval.centerX(), oval.centerY());
1858 }
1859 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1860 if (useCenter) {
1861 path.close();
1862 }
1863 this->drawPath(path, paint);
1864 }
1865}
1866
1867void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1868 const SkPath& path, SkScalar hOffset,
1869 SkScalar vOffset, const SkPaint& paint) {
1870 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001871
reed@android.com8a1c16f2008-12-17 15:59:43 +00001872 matrix.setTranslate(hOffset, vOffset);
1873 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1874}
1875
reed@android.comf76bacf2009-05-13 14:00:33 +00001876///////////////////////////////////////////////////////////////////////////////
1877
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878void SkCanvas::drawPicture(SkPicture& picture) {
1879 int saveCount = save();
1880 picture.draw(this);
1881 restoreToCount(saveCount);
1882}
1883
reed@android.com8a1c16f2008-12-17 15:59:43 +00001884///////////////////////////////////////////////////////////////////////////////
1885///////////////////////////////////////////////////////////////////////////////
1886
1887SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001888 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001889
1890 SkASSERT(canvas);
1891
1892 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1893 fDone = !fImpl->next();
1894}
1895
1896SkCanvas::LayerIter::~LayerIter() {
1897 fImpl->~SkDrawIter();
1898}
1899
1900void SkCanvas::LayerIter::next() {
1901 fDone = !fImpl->next();
1902}
1903
1904SkDevice* SkCanvas::LayerIter::device() const {
1905 return fImpl->getDevice();
1906}
1907
1908const SkMatrix& SkCanvas::LayerIter::matrix() const {
1909 return fImpl->getMatrix();
1910}
1911
1912const SkPaint& SkCanvas::LayerIter::paint() const {
1913 const SkPaint* paint = fImpl->getPaint();
1914 if (NULL == paint) {
1915 paint = &fDefaultPaint;
1916 }
1917 return *paint;
1918}
1919
1920const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1921int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1922int SkCanvas::LayerIter::y() const { return fImpl->getY(); }