blob: 87da90ad9f372a2776a4d893a058fefed48abb26 [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
reed@google.comb55deeb2012-01-06 14:43:09 +0000735 // Kill the imagefilter if our device doesn't allow it
736 SkLazyPaint lazyP;
737 if (paint && paint->getImageFilter()) {
738 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
739 SkPaint* p = lazyP.set(*paint);
740 p->setImageFilter(NULL);
741 paint = p;
742 }
743 }
744
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 bool isOpaque;
746 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
747
reed@google.com76dd2772012-01-05 21:15:07 +0000748 SkDevice* device;
749 if (paint && paint->getImageFilter()) {
750 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
751 isOpaque);
752 } else {
753 device = this->createLayerDevice(config, ir.width(), ir.height(),
754 isOpaque);
755 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000756 if (NULL == device) {
757 SkDebugf("Unable to create device for layer.");
758 return count;
759 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000760
reed@google.com6f8f2922011-03-04 22:27:10 +0000761 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
763 device->unref();
764
765 layer->fNext = fMCRec->fTopLayer;
766 fMCRec->fLayer = layer;
767 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
768
reed@google.com7c202932011-12-14 18:48:05 +0000769 fLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 return count;
771}
772
773int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
774 SaveFlags flags) {
775 if (0xFF == alpha) {
776 return this->saveLayer(bounds, NULL, flags);
777 } else {
778 SkPaint tmpPaint;
779 tmpPaint.setAlpha(alpha);
780 return this->saveLayer(bounds, &tmpPaint, flags);
781 }
782}
783
784void SkCanvas::restore() {
785 // check for underflow
786 if (fMCStack.count() > 1) {
787 this->internalRestore();
788 }
789}
790
791void SkCanvas::internalRestore() {
792 SkASSERT(fMCStack.count() != 0);
793
794 fDeviceCMDirty = true;
795 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000796 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797
reed@google.com5c3d1472011-02-22 19:12:23 +0000798 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000799 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 DeviceCM* layer = fMCRec->fLayer; // may be null
801 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
802 fMCRec->fLayer = NULL;
803
804 // now do the normal restore()
805 fMCRec->~MCRec(); // balanced in save()
806 fMCStack.pop_back();
807 fMCRec = (MCRec*)fMCStack.back();
808
809 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
810 since if we're being recorded, we don't want to record this (the
811 recorder will have already recorded the restore).
812 */
813 if (NULL != layer) {
814 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000815 const SkIPoint& origin = layer->fDevice->getOrigin();
816 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817 layer->fPaint);
818 // reset this, since drawDevice will have set it to true
819 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000820
821 SkASSERT(fLayerCount > 0);
822 fLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 }
824 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000825 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000826
827 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828}
829
830int SkCanvas::getSaveCount() const {
831 return fMCStack.count();
832}
833
834void SkCanvas::restoreToCount(int count) {
835 // sanity check
836 if (count < 1) {
837 count = 1;
838 }
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000839
840 int n = this->getSaveCount() - count;
841 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 this->restore();
843 }
844}
845
reed@google.com7c202932011-12-14 18:48:05 +0000846bool SkCanvas::isDrawingToLayer() const {
847 return fLayerCount > 0;
848}
849
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850/////////////////////////////////////////////////////////////////////////////
851
852// can't draw it if its empty, or its too big for a fixed-point width or height
853static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000854 return bitmap.width() <= 0 || bitmap.height() <= 0
855#ifndef SK_ALLOW_OVER_32K_BITMAPS
856 || bitmap.width() > 32767 || bitmap.height() > 32767
857#endif
858 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859}
860
reed@android.comf2b98d62010-12-20 18:26:13 +0000861void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 const SkMatrix& matrix, const SkPaint* paint) {
863 if (reject_bitmap(bitmap)) {
864 return;
865 }
866
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000867 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000869 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000871 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000872}
873
reed@google.com76dd2772012-01-05 21:15:07 +0000874#include "SkImageFilter.h"
875
876class DeviceImageFilterProxy : public SkImageFilter::Proxy {
877public:
878 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
879
880 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE;
881 virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
882 const SkMatrix& ctm,
883 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
884
885private:
886 SkDevice* fDevice;
887};
888
889SkDevice* DeviceImageFilterProxy::createDevice(int w, int h) {
890 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
891 w, h, false);
892}
893
894bool DeviceImageFilterProxy::filterImage(SkImageFilter* filter,
895 const SkBitmap& src,
896 const SkMatrix& ctm,
897 SkBitmap* result,
898 SkIPoint* offset) {
899 return fDevice->filterImage(filter, src, ctm, result, offset);
900}
901
reed@google.comb55deeb2012-01-06 14:43:09 +0000902void SkCanvas::drawDevice(SkDevice* srcDev, int x, int y,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 const SkPaint* paint) {
904 SkPaint tmp;
905 if (NULL == paint) {
906 tmp.setDither(true);
907 paint = &tmp;
908 }
reed@google.com4b226022011-01-11 18:32:13 +0000909
reed@google.com4e2b3d32011-04-07 14:18:59 +0000910 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000912 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000913 paint = &looper.paint();
914 SkImageFilter* filter = paint->getImageFilter();
915 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
916 if (filter) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000917 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000918 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000919 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000920 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
921 SkPaint tmp(*paint);
922 tmp.setImageFilter(NULL);
reed@google.comb55deeb2012-01-06 14:43:09 +0000923 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
reed@google.com76dd2772012-01-05 21:15:07 +0000924 }
925 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000926 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000927 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000929 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930}
931
932/////////////////////////////////////////////////////////////////////////////
933
934bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
935 fDeviceCMDirty = true;
936 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000937 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 return fMCRec->fMatrix->preTranslate(dx, dy);
939}
940
941bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
942 fDeviceCMDirty = true;
943 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000944 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945 return fMCRec->fMatrix->preScale(sx, sy);
946}
947
948bool SkCanvas::rotate(SkScalar degrees) {
949 fDeviceCMDirty = true;
950 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000951 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000952 return fMCRec->fMatrix->preRotate(degrees);
953}
954
955bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
956 fDeviceCMDirty = true;
957 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000958 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000959 return fMCRec->fMatrix->preSkew(sx, sy);
960}
961
962bool SkCanvas::concat(const SkMatrix& matrix) {
963 fDeviceCMDirty = true;
964 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000965 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 return fMCRec->fMatrix->preConcat(matrix);
967}
968
969void SkCanvas::setMatrix(const SkMatrix& matrix) {
970 fDeviceCMDirty = true;
971 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000972 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 *fMCRec->fMatrix = matrix;
974}
975
976// this is not virtual, so it must call a virtual method so that subclasses
977// will see its action
978void SkCanvas::resetMatrix() {
979 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000980
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 matrix.reset();
982 this->setMatrix(matrix);
983}
984
985//////////////////////////////////////////////////////////////////////////////
986
reed@google.comc42d35d2011-10-12 11:57:42 +0000987bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000988 AutoValidateClip avc(this);
989
reed@android.com8a1c16f2008-12-17 15:59:43 +0000990 fDeviceCMDirty = true;
991 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000992 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993
994 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +0000995 // for these simpler matrices, we can stay a rect ever after applying
996 // the matrix. This means we don't have to a) make a path, and b) tell
997 // the region code to scan-convert the path, only to discover that it
998 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000
1001 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001002 fClipStack.clipDevRect(r, op, doAA);
1003 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001005 // since we're rotate or some such thing, we convert the rect to a path
1006 // and clip against that, since it can handle any matrix. However, to
1007 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1008 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 SkPath path;
1010
1011 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001012 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001013 }
1014}
1015
reed@google.com00177082011-10-12 14:34:30 +00001016static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001017 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001018 // base is used to limit the size (and therefore memory allocation) of the
1019 // region that results from scan converting devPath.
1020 SkRegion base;
1021
reed@google.com819c9212011-02-23 18:56:55 +00001022 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001023 // since we are intersect, we can do better (tighter) with currRgn's
1024 // bounds, than just using the device. However, if currRgn is complex,
1025 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001026 if (currClip->isRect()) {
1027 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001028 } else {
reed@google.com00177082011-10-12 14:34:30 +00001029 base.setRect(currClip->getBounds());
1030 SkRasterClip clip;
1031 clip.setPath(devPath, base, doAA);
1032 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001033 }
reed@google.com819c9212011-02-23 18:56:55 +00001034 } else {
reed@google.com819c9212011-02-23 18:56:55 +00001035 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
1036 base.setRect(0, 0, bm.width(), bm.height());
1037
1038 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001039 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001040 } else {
reed@google.com00177082011-10-12 14:34:30 +00001041 SkRasterClip clip;
1042 clip.setPath(devPath, base, doAA);
1043 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001044 }
1045 }
1046}
1047
reed@google.comc42d35d2011-10-12 11:57:42 +00001048bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001049 AutoValidateClip avc(this);
1050
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 fDeviceCMDirty = true;
1052 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001053 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054
1055 SkPath devPath;
1056 path.transform(*fMCRec->fMatrix, &devPath);
1057
reed@google.comfe701122011-11-08 19:41:23 +00001058 // Check if the transfomation, or the original path itself
1059 // made us empty. Note this can also happen if we contained NaN
1060 // values. computing the bounds detects this, and will set our
1061 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1062 if (devPath.getBounds().isEmpty()) {
1063 // resetting the path will remove any NaN or other wanky values
1064 // that might upset our scan converter.
1065 devPath.reset();
1066 }
1067
reed@google.com5c3d1472011-02-22 19:12:23 +00001068 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001069 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001070
reed@google.com00177082011-10-12 14:34:30 +00001071 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072}
1073
1074bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001075 AutoValidateClip avc(this);
1076
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 fDeviceCMDirty = true;
1078 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001079 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080
reed@google.com5c3d1472011-02-22 19:12:23 +00001081 // todo: signal fClipStack that we have a region, and therefore (I guess)
1082 // we have to ignore it, and use the region directly?
1083 fClipStack.clipDevRect(rgn.getBounds());
1084
reed@google.com00177082011-10-12 14:34:30 +00001085 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086}
1087
reed@google.com819c9212011-02-23 18:56:55 +00001088#ifdef SK_DEBUG
1089void SkCanvas::validateClip() const {
1090 // construct clipRgn from the clipstack
1091 const SkDevice* device = this->getDevice();
1092 SkIRect ir;
1093 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001094 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001095
1096 SkClipStack::B2FIter iter(fClipStack);
1097 const SkClipStack::B2FIter::Clip* clip;
1098 while ((clip = iter.next()) != NULL) {
1099 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001100 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001101 } else if (clip->fRect) {
1102 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001103 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001104 } else {
reed@google.com00177082011-10-12 14:34:30 +00001105 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001106 }
1107 }
1108
reed@google.com6f8f2922011-03-04 22:27:10 +00001109#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001110 // now compare against the current rgn
1111 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001112 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001113#endif
reed@google.com819c9212011-02-23 18:56:55 +00001114}
1115#endif
1116
reed@google.com5c3d1472011-02-22 19:12:23 +00001117///////////////////////////////////////////////////////////////////////////////
1118
reed@android.comba09de42010-02-05 20:46:05 +00001119void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001121 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1122 fLocalBoundsCompareTypeBW;
1123
1124 if (!this->getClipBounds(&r, et)) {
1125 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001127 rCompare.set(SkScalarToCompareType(r.fLeft),
1128 SkScalarToCompareType(r.fTop),
1129 SkScalarToCompareType(r.fRight),
1130 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 }
1132}
1133
reed@android.comd252db02009-04-01 18:31:44 +00001134/* current impl ignores edgetype, and relies on
1135 getLocalClipBoundsCompareType(), which always returns a value assuming
1136 antialiasing (worst case)
1137 */
reed@android.comba09de42010-02-05 20:46:05 +00001138bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001139
reed@google.com16078632011-12-06 18:56:37 +00001140 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001141 return true;
1142
reed@google.com00177082011-10-12 14:34:30 +00001143 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144 return true;
1145 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001146
tomhudson@google.com8d430182011-06-06 19:11:19 +00001147 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001148 SkRect dst;
1149 fMCRec->fMatrix->mapRect(&dst, rect);
1150 SkIRect idst;
1151 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001152 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001153 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001154 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001155
reed@android.coma380ae42009-07-21 01:17:02 +00001156 // for speed, do the most likely reject compares first
1157 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1158 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1159 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1160 return true;
1161 }
1162 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1163 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1164 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1165 return true;
1166 }
1167 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001169}
1170
1171bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001172 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173}
1174
1175bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1176 /* current impl ignores edgetype, and relies on
1177 getLocalClipBoundsCompareType(), which always returns a value assuming
1178 antialiasing (worst case)
1179 */
1180
reed@google.com00177082011-10-12 14:34:30 +00001181 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 return true;
1183 }
reed@google.com4b226022011-01-11 18:32:13 +00001184
reed@android.comaefd2bc2009-03-30 21:02:14 +00001185 SkScalarCompareType userT = SkScalarToCompareType(top);
1186 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001187
reed@android.com8a1c16f2008-12-17 15:59:43 +00001188 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001189 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 if (userT >= userB) {
1191 return true;
1192 }
reed@google.com4b226022011-01-11 18:32:13 +00001193
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 // check if we are above or below the local clip bounds
1195 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1196 return userT >= clipR.fBottom || userB <= clipR.fTop;
1197}
1198
1199bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001200 SkIRect ibounds;
1201 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 return false;
1203 }
1204
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001205 SkMatrix inverse;
1206 // if we can't invert the CTM, we can't return local clip bounds
1207 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001208 if (bounds) {
1209 bounds->setEmpty();
1210 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001211 return false;
1212 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001213
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001214 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001215 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001216 // adjust it outwards if we are antialiasing
1217 int inset = (kAA_EdgeType == et);
1218 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1219 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001220 inverse.mapRect(bounds, r);
1221 }
1222 return true;
1223}
1224
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001225bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001226 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001227 if (clip.isEmpty()) {
1228 if (bounds) {
1229 bounds->setEmpty();
1230 }
1231 return false;
1232 }
1233
1234 if (NULL != bounds) {
1235 *bounds = clip.getBounds();
1236 }
1237 return true;
1238}
1239
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240const SkMatrix& SkCanvas::getTotalMatrix() const {
1241 return *fMCRec->fMatrix;
1242}
1243
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001244SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001245 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1246 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001247 return kComplex_ClipType;
1248}
1249
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001251 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252}
1253
reed@google.com7d7ca792011-02-23 22:39:18 +00001254const SkClipStack& SkCanvas::getTotalClipStack() const {
1255 return fClipStack;
1256}
1257
reed@android.comf2b98d62010-12-20 18:26:13 +00001258void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1259 if (NULL == matrix || matrix->isIdentity()) {
1260 if (fUseExternalMatrix) {
1261 fDeviceCMDirty = true;
1262 }
1263 fUseExternalMatrix = false;
1264 } else {
1265 fUseExternalMatrix = true;
1266 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001267
reed@android.comf2b98d62010-12-20 18:26:13 +00001268 fExternalMatrix = *matrix;
1269 matrix->invert(&fExternalInverse);
1270 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001271}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272
bsalomon@google.come97f0852011-06-17 13:10:25 +00001273SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1274 int width, int height,
1275 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001276 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001277 if (device) {
1278 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1279 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001280 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001281 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283}
1284
bsalomon@google.come97f0852011-06-17 13:10:25 +00001285SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
1286 int width, int height,
1287 bool isOpaque) {
1288 SkDevice* device = this->getDevice();
1289 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001290 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001291 } else {
1292 return NULL;
1293 }
1294}
1295
1296
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297//////////////////////////////////////////////////////////////////////////////
1298// These are the virtual drawing methods
1299//////////////////////////////////////////////////////////////////////////////
1300
reed@google.com2a981812011-04-14 18:59:28 +00001301void SkCanvas::clear(SkColor color) {
1302 SkDrawIter iter(this);
1303
1304 while (iter.next()) {
1305 iter.fDevice->clear(color);
1306 }
1307}
1308
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001310 this->internalDrawPaint(paint);
1311}
1312
1313void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001314 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315
1316 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001317 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001318 }
1319
reed@google.com4e2b3d32011-04-07 14:18:59 +00001320 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321}
1322
1323void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1324 const SkPaint& paint) {
1325 if ((long)count <= 0) {
1326 return;
1327 }
1328
1329 SkASSERT(pts != NULL);
1330
reed@google.com4e2b3d32011-04-07 14:18:59 +00001331 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001332
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001334 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 }
reed@google.com4b226022011-01-11 18:32:13 +00001336
reed@google.com4e2b3d32011-04-07 14:18:59 +00001337 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338}
1339
1340void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1341 if (paint.canComputeFastBounds()) {
1342 SkRect storage;
1343 if (this->quickReject(paint.computeFastBounds(r, &storage),
1344 paint2EdgeType(&paint))) {
1345 return;
1346 }
1347 }
reed@google.com4b226022011-01-11 18:32:13 +00001348
reed@google.com4e2b3d32011-04-07 14:18:59 +00001349 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350
1351 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001352 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 }
1354
reed@google.com4e2b3d32011-04-07 14:18:59 +00001355 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356}
1357
1358void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001359 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001360 SkRect storage;
1361 const SkRect& bounds = path.getBounds();
1362 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001363 paint2EdgeType(&paint))) {
1364 return;
1365 }
1366 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001367 if (path.isEmpty()) {
1368 if (path.isInverseFillType()) {
1369 this->internalDrawPaint(paint);
1370 }
1371 return;
1372 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373
reed@google.com4e2b3d32011-04-07 14:18:59 +00001374 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375
1376 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001377 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378 }
1379
reed@google.com4e2b3d32011-04-07 14:18:59 +00001380 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381}
1382
1383void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1384 const SkPaint* paint) {
1385 SkDEBUGCODE(bitmap.validate();)
1386
reed@google.com3d608122011-11-21 15:16:16 +00001387 if (NULL == paint || paint->canComputeFastBounds()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001388 SkRect fastBounds;
1389 fastBounds.set(x, y,
1390 x + SkIntToScalar(bitmap.width()),
1391 y + SkIntToScalar(bitmap.height()));
1392 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1393 return;
1394 }
1395 }
reed@google.com4b226022011-01-11 18:32:13 +00001396
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 SkMatrix matrix;
1398 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001399 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400}
1401
reed@google.com9987ec32011-09-07 11:57:52 +00001402// this one is non-virtual, so it can be called safely by other canvas apis
1403void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1404 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1406 return;
1407 }
reed@google.com9987ec32011-09-07 11:57:52 +00001408
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001410 if (NULL == paint || paint->canComputeFastBounds()) {
1411 if (this->quickReject(dst, paint2EdgeType(paint))) {
1412 return;
1413 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 }
reed@google.com3d608122011-11-21 15:16:16 +00001415
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416 const SkBitmap* bitmapPtr = &bitmap;
reed@google.com9987ec32011-09-07 11:57:52 +00001417
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001419 SkRect tmpSrc;
1420 if (src) {
1421 tmpSrc.set(*src);
1422 // if the extract process clipped off the top or left of the
1423 // original, we adjust for that here to get the position right.
1424 if (tmpSrc.fLeft > 0) {
1425 tmpSrc.fRight -= tmpSrc.fLeft;
1426 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001427 }
reed@android.com87899992009-10-16 14:48:38 +00001428 if (tmpSrc.fTop > 0) {
1429 tmpSrc.fBottom -= tmpSrc.fTop;
1430 tmpSrc.fTop = 0;
1431 }
1432 } else {
1433 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1434 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 }
reed@android.com87899992009-10-16 14:48:38 +00001436 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
reed@google.com9987ec32011-09-07 11:57:52 +00001437
reed@android.comf2b98d62010-12-20 18:26:13 +00001438 // ensure that src is "valid" before we pass it to our internal routines
1439 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1440 SkIRect tmpISrc;
1441 if (src) {
1442 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001443 if (!tmpISrc.intersect(*src)) {
1444 return;
1445 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001446 src = &tmpISrc;
1447 }
1448 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449}
1450
reed@google.com9987ec32011-09-07 11:57:52 +00001451void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1452 const SkRect& dst, const SkPaint* paint) {
1453 SkDEBUGCODE(bitmap.validate();)
1454 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1455}
1456
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1458 const SkPaint* paint) {
1459 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001460 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461}
1462
reed@android.comf2b98d62010-12-20 18:26:13 +00001463void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1464 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001466
reed@google.com4e2b3d32011-04-07 14:18:59 +00001467 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001468
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001470 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471 }
reed@android.com9b039062009-02-11 15:09:58 +00001472
reed@google.com4e2b3d32011-04-07 14:18:59 +00001473 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474}
1475
reed@google.com9987ec32011-09-07 11:57:52 +00001476void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1477 const SkIRect& center, const SkRect& dst,
1478 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001479 if (NULL == paint || paint->canComputeFastBounds()) {
1480 if (this->quickReject(dst, paint2EdgeType(paint))) {
1481 return;
1482 }
1483 }
1484
reed@google.com9987ec32011-09-07 11:57:52 +00001485 const int32_t w = bitmap.width();
1486 const int32_t h = bitmap.height();
1487
1488 SkIRect c = center;
1489 // pin center to the bounds of the bitmap
1490 c.fLeft = SkMax32(0, center.fLeft);
1491 c.fTop = SkMax32(0, center.fTop);
1492 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1493 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1494
1495 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1496 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1497 SkScalar dstX[4] = {
1498 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1499 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1500 };
1501 SkScalar dstY[4] = {
1502 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1503 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1504 };
1505
1506 if (dstX[1] > dstX[2]) {
1507 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1508 dstX[2] = dstX[1];
1509 }
1510
1511 if (dstY[1] > dstY[2]) {
1512 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1513 dstY[2] = dstY[1];
1514 }
1515
1516 SkIRect s;
1517 SkRect d;
1518 for (int y = 0; y < 3; y++) {
1519 s.fTop = srcY[y];
1520 s.fBottom = srcY[y+1];
1521 d.fTop = dstY[y];
1522 d.fBottom = dstY[y+1];
1523 for (int x = 0; x < 3; x++) {
1524 s.fLeft = srcX[x];
1525 s.fRight = srcX[x+1];
1526 d.fLeft = dstX[x];
1527 d.fRight = dstX[x+1];
1528 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1529 }
1530 }
1531}
1532
1533void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1534 const SkRect& dst, const SkPaint* paint) {
1535 SkDEBUGCODE(bitmap.validate();)
1536
1537 // Need a device entry-point, so gpu can use a mesh
1538 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1539}
1540
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1542 const SkPaint* paint) {
1543 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001544
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545 if (reject_bitmap(bitmap)) {
1546 return;
1547 }
reed@google.com4b226022011-01-11 18:32:13 +00001548
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549 SkPaint tmp;
1550 if (NULL == paint) {
1551 paint = &tmp;
1552 }
reed@google.com4b226022011-01-11 18:32:13 +00001553
reed@google.com4e2b3d32011-04-07 14:18:59 +00001554 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001555
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556 while (iter.next()) {
1557 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001558 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001560 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561}
1562
reed@google.comf67e4cf2011-03-15 20:56:58 +00001563class SkDeviceFilteredPaint {
1564public:
1565 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1566 SkDevice::TextFlags flags;
1567 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001568 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001569 newPaint->setFlags(flags.fFlags);
1570 newPaint->setHinting(flags.fHinting);
1571 fPaint = newPaint;
1572 } else {
1573 fPaint = &paint;
1574 }
1575 }
1576
reed@google.comf67e4cf2011-03-15 20:56:58 +00001577 const SkPaint& paint() const { return *fPaint; }
1578
1579private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001580 const SkPaint* fPaint;
1581 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001582};
1583
bungeman@google.com52c748b2011-08-22 21:30:43 +00001584void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1585 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001586 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001587 draw.fDevice->drawRect(draw, r, paint);
1588 } else {
1589 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001590 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001591 draw.fDevice->drawRect(draw, r, p);
1592 }
1593}
1594
1595void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1596 const char text[], size_t byteLength,
1597 SkScalar x, SkScalar y) {
1598 SkASSERT(byteLength == 0 || text != NULL);
1599
1600 // nothing to draw
1601 if (text == NULL || byteLength == 0 ||
1602 draw.fClip->isEmpty() ||
1603 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1604 return;
1605 }
1606
1607 SkScalar width = 0;
1608 SkPoint start;
1609
1610 start.set(0, 0); // to avoid warning
1611 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1612 SkPaint::kStrikeThruText_Flag)) {
1613 width = paint.measureText(text, byteLength);
1614
1615 SkScalar offsetX = 0;
1616 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1617 offsetX = SkScalarHalf(width);
1618 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1619 offsetX = width;
1620 }
1621 start.set(x - offsetX, y);
1622 }
1623
1624 if (0 == width) {
1625 return;
1626 }
1627
1628 uint32_t flags = paint.getFlags();
1629
1630 if (flags & (SkPaint::kUnderlineText_Flag |
1631 SkPaint::kStrikeThruText_Flag)) {
1632 SkScalar textSize = paint.getTextSize();
1633 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1634 SkRect r;
1635
1636 r.fLeft = start.fX;
1637 r.fRight = start.fX + width;
1638
1639 if (flags & SkPaint::kUnderlineText_Flag) {
1640 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1641 start.fY);
1642 r.fTop = offset;
1643 r.fBottom = offset + height;
1644 DrawRect(draw, paint, r, textSize);
1645 }
1646 if (flags & SkPaint::kStrikeThruText_Flag) {
1647 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1648 start.fY);
1649 r.fTop = offset;
1650 r.fBottom = offset + height;
1651 DrawRect(draw, paint, r, textSize);
1652 }
1653 }
1654}
1655
reed@android.com8a1c16f2008-12-17 15:59:43 +00001656void SkCanvas::drawText(const void* text, size_t byteLength,
1657 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001658 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001659
1660 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001661 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001662 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001663 DrawTextDecorations(iter, dfp.paint(),
1664 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001665 }
1666
reed@google.com4e2b3d32011-04-07 14:18:59 +00001667 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668}
1669
1670void SkCanvas::drawPosText(const void* text, size_t byteLength,
1671 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001672 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001673
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001675 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001677 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001678 }
reed@google.com4b226022011-01-11 18:32:13 +00001679
reed@google.com4e2b3d32011-04-07 14:18:59 +00001680 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681}
1682
1683void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1684 const SkScalar xpos[], SkScalar constY,
1685 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001686 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001687
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001689 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001690 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001691 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001692 }
reed@google.com4b226022011-01-11 18:32:13 +00001693
reed@google.com4e2b3d32011-04-07 14:18:59 +00001694 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695}
1696
1697void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1698 const SkPath& path, const SkMatrix* matrix,
1699 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001700 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701
1702 while (iter.next()) {
1703 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001704 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705 }
1706
reed@google.com4e2b3d32011-04-07 14:18:59 +00001707 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001708}
1709
djsollen@google.com56c69772011-11-08 19:00:26 +00001710#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001711void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1712 const SkPoint pos[], const SkPaint& paint,
1713 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001714 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001715
1716 while (iter.next()) {
1717 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001718 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001719 }
1720
reed@google.com4e2b3d32011-04-07 14:18:59 +00001721 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001722}
1723#endif
1724
reed@android.com8a1c16f2008-12-17 15:59:43 +00001725void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1726 const SkPoint verts[], const SkPoint texs[],
1727 const SkColor colors[], SkXfermode* xmode,
1728 const uint16_t indices[], int indexCount,
1729 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001730 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001731
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 while (iter.next()) {
1733 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001734 colors, xmode, indices, indexCount,
1735 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736 }
reed@google.com4b226022011-01-11 18:32:13 +00001737
reed@google.com4e2b3d32011-04-07 14:18:59 +00001738 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001739}
1740
reed@android.comcb608442009-12-04 21:32:27 +00001741void SkCanvas::drawData(const void* data, size_t length) {
1742 // do nothing. Subclasses may do something with the data
1743}
1744
reed@android.com8a1c16f2008-12-17 15:59:43 +00001745//////////////////////////////////////////////////////////////////////////////
1746// These methods are NOT virtual, and therefore must call back into virtual
1747// methods, rather than actually drawing themselves.
1748//////////////////////////////////////////////////////////////////////////////
1749
1750void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001751 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752 SkPaint paint;
1753
1754 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001755 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001756 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757 }
1758 this->drawPaint(paint);
1759}
1760
reed@android.com845fdac2009-06-23 03:01:32 +00001761void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 SkPaint paint;
1763
1764 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001765 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001766 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767 }
1768 this->drawPaint(paint);
1769}
1770
1771void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1772 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001773
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 pt.set(x, y);
1775 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1776}
1777
1778void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1779 SkPoint pt;
1780 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001781
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782 pt.set(x, y);
1783 paint.setColor(color);
1784 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1785}
1786
1787void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1788 const SkPaint& paint) {
1789 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001790
reed@android.com8a1c16f2008-12-17 15:59:43 +00001791 pts[0].set(x0, y0);
1792 pts[1].set(x1, y1);
1793 this->drawPoints(kLines_PointMode, 2, pts, paint);
1794}
1795
1796void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1797 SkScalar right, SkScalar bottom,
1798 const SkPaint& paint) {
1799 SkRect r;
1800
1801 r.set(left, top, right, bottom);
1802 this->drawRect(r, paint);
1803}
1804
1805void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1806 const SkPaint& paint) {
1807 if (radius < 0) {
1808 radius = 0;
1809 }
1810
1811 SkRect r;
1812 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001813
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 if (paint.canComputeFastBounds()) {
1815 SkRect storage;
1816 if (this->quickReject(paint.computeFastBounds(r, &storage),
1817 paint2EdgeType(&paint))) {
1818 return;
1819 }
1820 }
reed@google.com4b226022011-01-11 18:32:13 +00001821
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822 SkPath path;
1823 path.addOval(r);
1824 this->drawPath(path, paint);
1825}
1826
1827void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1828 const SkPaint& paint) {
1829 if (rx > 0 && ry > 0) {
1830 if (paint.canComputeFastBounds()) {
1831 SkRect storage;
1832 if (this->quickReject(paint.computeFastBounds(r, &storage),
1833 paint2EdgeType(&paint))) {
1834 return;
1835 }
1836 }
1837
1838 SkPath path;
1839 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1840 this->drawPath(path, paint);
1841 } else {
1842 this->drawRect(r, paint);
1843 }
1844}
1845
1846void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1847 if (paint.canComputeFastBounds()) {
1848 SkRect storage;
1849 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1850 paint2EdgeType(&paint))) {
1851 return;
1852 }
1853 }
1854
1855 SkPath path;
1856 path.addOval(oval);
1857 this->drawPath(path, paint);
1858}
1859
1860void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1861 SkScalar sweepAngle, bool useCenter,
1862 const SkPaint& paint) {
1863 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1864 this->drawOval(oval, paint);
1865 } else {
1866 SkPath path;
1867 if (useCenter) {
1868 path.moveTo(oval.centerX(), oval.centerY());
1869 }
1870 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1871 if (useCenter) {
1872 path.close();
1873 }
1874 this->drawPath(path, paint);
1875 }
1876}
1877
1878void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1879 const SkPath& path, SkScalar hOffset,
1880 SkScalar vOffset, const SkPaint& paint) {
1881 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001882
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883 matrix.setTranslate(hOffset, vOffset);
1884 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1885}
1886
reed@android.comf76bacf2009-05-13 14:00:33 +00001887///////////////////////////////////////////////////////////////////////////////
1888
reed@android.com8a1c16f2008-12-17 15:59:43 +00001889void SkCanvas::drawPicture(SkPicture& picture) {
1890 int saveCount = save();
1891 picture.draw(this);
1892 restoreToCount(saveCount);
1893}
1894
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895///////////////////////////////////////////////////////////////////////////////
1896///////////////////////////////////////////////////////////////////////////////
1897
1898SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001899 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900
1901 SkASSERT(canvas);
1902
1903 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1904 fDone = !fImpl->next();
1905}
1906
1907SkCanvas::LayerIter::~LayerIter() {
1908 fImpl->~SkDrawIter();
1909}
1910
1911void SkCanvas::LayerIter::next() {
1912 fDone = !fImpl->next();
1913}
1914
1915SkDevice* SkCanvas::LayerIter::device() const {
1916 return fImpl->getDevice();
1917}
1918
1919const SkMatrix& SkCanvas::LayerIter::matrix() const {
1920 return fImpl->getMatrix();
1921}
1922
1923const SkPaint& SkCanvas::LayerIter::paint() const {
1924 const SkPaint* paint = fImpl->getPaint();
1925 if (NULL == paint) {
1926 paint = &fDefaultPaint;
1927 }
1928 return *paint;
1929}
1930
1931const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1932int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1933int SkCanvas::LayerIter::y() const { return fImpl->getY(); }