blob: ed65eca72587d218f9180534a5891cac45720bfd [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) {
junov@google.com4370aed2012-01-18 16:21:08 +0000217 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000218 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 canvas->updateDeviceCMCache();
220
reed@google.com7d7ca792011-02-23 22:39:18 +0000221 fClipStack = &canvas->getTotalClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 fBounder = canvas->getBounder();
223 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000224 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 }
reed@google.com4b226022011-01-11 18:32:13 +0000226
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 bool next() {
228 // skip over recs with empty clips
229 if (fSkipEmptyClips) {
230 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
231 fCurrLayer = fCurrLayer->fNext;
232 }
233 }
234
235 if (NULL != fCurrLayer) {
236 const DeviceCM* rec = fCurrLayer;
237
238 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000239 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
240 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 fDevice = rec->fDevice;
242 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000244 fMVMatrix = rec->fMVMatrix;
245 fExtMatrix = rec->fExtMatrix;
246 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247
248 fCurrLayer = rec->fNext;
249 if (fBounder) {
250 fBounder->setClip(fClip);
251 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000253
bsalomon@google.comd302f142011-03-03 13:54:13 +0000254 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 return true;
256 }
257 return false;
258 }
reed@google.com4b226022011-01-11 18:32:13 +0000259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000261 int getX() const { return fDevice->getOrigin().x(); }
262 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 const SkMatrix& getMatrix() const { return *fMatrix; }
264 const SkRegion& getClip() const { return *fClip; }
265 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267private:
268 SkCanvas* fCanvas;
269 const DeviceCM* fCurrLayer;
270 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 SkBool8 fSkipEmptyClips;
272
273 typedef SkDraw INHERITED;
274};
275
276/////////////////////////////////////////////////////////////////////////////
277
278class AutoDrawLooper {
279public:
reed@google.com4e2b3d32011-04-07 14:18:59 +0000280 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
281 fCanvas = canvas;
282 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000284 fPaint = NULL;
285 fSaveCount = canvas->getSaveCount();
286 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 if (fLooper) {
289 fLooper->init(canvas);
290 }
291 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000292
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 ~AutoDrawLooper() {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000294 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000296
reed@google.com4e2b3d32011-04-07 14:18:59 +0000297 const SkPaint& paint() const {
298 SkASSERT(fPaint);
299 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000300 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000301
reed@google.com4e2b3d32011-04-07 14:18:59 +0000302 bool next(SkDrawFilter::Type drawType);
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000305 SkLazyPaint fLazyPaint;
306 SkCanvas* fCanvas;
307 const SkPaint& fOrigPaint;
308 SkDrawLooper* fLooper;
309 SkDrawFilter* fFilter;
310 const SkPaint* fPaint;
311 int fSaveCount;
312 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313};
314
reed@google.com4e2b3d32011-04-07 14:18:59 +0000315bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000316 fPaint = NULL;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000317 if (fDone) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000318 return false;
319 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000320
reed@google.com632e1a22011-10-06 12:37:00 +0000321 if (fLooper || fFilter) {
322 SkPaint* paint = fLazyPaint.set(fOrigPaint);
323 if (fLooper && !fLooper->next(fCanvas, paint)) {
324 fDone = true;
325 return false;
326 }
327 if (fFilter) {
328 fFilter->filter(paint, drawType);
329 if (NULL == fLooper) {
330 // no looper means we only draw once
331 fDone = true;
332 }
333 }
334 fPaint = paint;
335 } else {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000336 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000337 fPaint = &fOrigPaint;
338 }
339
340 // call this after any possible paint modifiers
341 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000342 fPaint = NULL;
343 return false;
344 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000345 return true;
346}
347
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348/* Stack helper for managing a SkBounder. In the destructor, if we were
349 given a bounder, we call its commit() method, signifying that we are
350 done accumulating bounds for that draw.
351*/
352class SkAutoBounderCommit {
353public:
354 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
355 ~SkAutoBounderCommit() {
356 if (NULL != fBounder) {
357 fBounder->commit();
358 }
359 }
360private:
361 SkBounder* fBounder;
362};
363
364#include "SkColorPriv.h"
365
366class AutoValidator {
367public:
368 AutoValidator(SkDevice* device) : fDevice(device) {}
369 ~AutoValidator() {
370#ifdef SK_DEBUG
371 const SkBitmap& bm = fDevice->accessBitmap(false);
372 if (bm.config() == SkBitmap::kARGB_4444_Config) {
373 for (int y = 0; y < bm.height(); y++) {
374 const SkPMColor16* p = bm.getAddr16(0, y);
375 for (int x = 0; x < bm.width(); x++) {
376 SkPMColor16 c = p[x];
377 SkPMColor16Assert(c);
378 }
379 }
380 }
381#endif
382 }
383private:
384 SkDevice* fDevice;
385};
386
387////////// macros to place around the internal draw calls //////////////////
388
reed@google.com4e2b3d32011-04-07 14:18:59 +0000389#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000391 AutoDrawLooper looper(this, paint); \
392 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393 SkAutoBounderCommit ac(fBounder); \
394 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000395
reed@google.com4e2b3d32011-04-07 14:18:59 +0000396#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397
398////////////////////////////////////////////////////////////////////////////
399
400SkDevice* SkCanvas::init(SkDevice* device) {
401 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000402 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000404 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000405 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000406 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000407 fDeviceCMDirty = false;
reed@google.com7c202932011-12-14 18:48:05 +0000408 fLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409
410 fMCRec = (MCRec*)fMCStack.push_back();
411 new (fMCRec) MCRec(NULL, 0);
412
413 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
414 fMCRec->fTopLayer = fMCRec->fLayer;
415 fMCRec->fNext = NULL;
416
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000417 fExternalMatrix.reset();
418 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000419 fUseExternalMatrix = false;
420
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421 return this->setDevice(device);
422}
423
reed@google.comcde92112011-07-06 20:00:52 +0000424SkCanvas::SkCanvas()
425: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000426 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000427
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000428 this->init(NULL);
429}
430
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000432 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 inc_canvas();
434
435 this->init(device);
436}
437
438SkCanvas::SkCanvas(const SkBitmap& bitmap)
439 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
440 inc_canvas();
441
reed@google.comcde92112011-07-06 20:00:52 +0000442 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443}
444
445SkCanvas::~SkCanvas() {
446 // free up the contents of our deque
447 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000448 SkASSERT(0 == fLayerCount);
449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 this->internalRestore(); // restore the last, since we're going away
451
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000452 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000453
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 dec_canvas();
455}
456
457SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
458 SkRefCnt_SafeAssign(fBounder, bounder);
459 return bounder;
460}
461
462SkDrawFilter* SkCanvas::getDrawFilter() const {
463 return fMCRec->fFilter;
464}
465
466SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
467 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
468 return filter;
469}
470
471///////////////////////////////////////////////////////////////////////////////
472
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000473void SkCanvas::flush() {
474 SkDevice* device = this->getDevice();
475 if (device) {
476 device->flush();
477 }
478}
479
reed@google.com210ce002011-11-01 14:24:23 +0000480SkISize SkCanvas::getDeviceSize() const {
481 SkDevice* d = this->getDevice();
482 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
483}
484
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485SkDevice* SkCanvas::getDevice() const {
486 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000487 SkDeque::F2BIter iter(fMCStack);
488 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 SkASSERT(rec && rec->fLayer);
490 return rec->fLayer->fDevice;
491}
492
reed@google.com9266fed2011-03-30 00:18:03 +0000493SkDevice* SkCanvas::getTopDevice() const {
494 return fMCRec->fTopLayer->fDevice;
495}
496
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497SkDevice* SkCanvas::setDevice(SkDevice* device) {
498 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000499 SkDeque::F2BIter iter(fMCStack);
500 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 SkASSERT(rec && rec->fLayer);
502 SkDevice* rootDevice = rec->fLayer->fDevice;
503
504 if (rootDevice == device) {
505 return device;
506 }
reed@google.com4b226022011-01-11 18:32:13 +0000507
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 /* Notify the devices that they are going in/out of scope, so they can do
509 things like lock/unlock their pixels, etc.
510 */
511 if (device) {
512 device->lockPixels();
513 }
514 if (rootDevice) {
515 rootDevice->unlockPixels();
516 }
517
518 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
519 rootDevice = device;
520
521 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000522
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 /* Now we update our initial region to have the bounds of the new device,
524 and then intersect all of the clips in our stack with these bounds,
525 to ensure that we can't draw outside of the device's bounds (and trash
526 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000527
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 NOTE: this is only a partial-fix, since if the new device is larger than
529 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000530 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
532 reconstruct the correct clips, so this approximation will have to do.
533 The caller really needs to restore() back to the base if they want to
534 accurately take advantage of the new device bounds.
535 */
536
537 if (NULL == device) {
reed@google.com00177082011-10-12 14:34:30 +0000538 rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000540 (void)rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000542 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 } else {
544 // compute our total bounds for all devices
545 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000546
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 bounds.set(0, 0, device->width(), device->height());
548
549 // now jam our 1st clip to be bounds, and intersect the rest with that
reed@google.com00177082011-10-12 14:34:30 +0000550 rec->fRasterClip->setRect(bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000552 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 }
554 }
555 return device;
556}
557
reed@google.comaf951c92011-06-16 19:10:39 +0000558SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
559 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 device->unref();
561 return device;
562}
563
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000564bool SkCanvas::readPixels(SkBitmap* bitmap,
565 int x, int y,
566 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000567 SkDevice* device = this->getDevice();
568 if (!device) {
569 return false;
570 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000571 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000572}
573
bsalomon@google.comc6980972011-11-02 19:57:21 +0000574bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000575 SkDevice* device = this->getDevice();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000576
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000577 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000578 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000579 if (!bounds.intersect(srcRect)) {
580 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000581 }
582
583 SkBitmap tmp;
584 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
585 bounds.height());
586 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
587 bitmap->swap(tmp);
588 return true;
589 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000590 return false;
591 }
reed@google.com51df9e32010-12-23 19:29:18 +0000592}
593
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000594void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
595 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000596 SkDevice* device = this->getDevice();
597 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000598 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000599 }
600}
601
junov@google.com4370aed2012-01-18 16:21:08 +0000602SkCanvas* SkCanvas::canvasForDrawIter() {
603 return this;
604}
605
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606//////////////////////////////////////////////////////////////////////////////
607
reed@android.com8a1c16f2008-12-17 15:59:43 +0000608void SkCanvas::updateDeviceCMCache() {
609 if (fDeviceCMDirty) {
610 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000611 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000613
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000615 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000616 if (fUseExternalMatrix) {
617 layer->updateExternalMatrix(fExternalMatrix,
618 fExternalInverse);
619 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000621 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000623 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000624 if (fUseExternalMatrix) {
625 layer->updateExternalMatrix(fExternalMatrix,
626 fExternalInverse);
627 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 } while ((layer = layer->fNext) != NULL);
629 }
630 fDeviceCMDirty = false;
631 }
632}
633
reed@android.comf2b98d62010-12-20 18:26:13 +0000634void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000635 const SkRegion& clip,
636 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000638 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000639 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000640 fLastDeviceToGainFocus = device;
641 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642}
643
644///////////////////////////////////////////////////////////////////////////////
645
646int SkCanvas::internalSave(SaveFlags flags) {
647 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000648
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 MCRec* newTop = (MCRec*)fMCStack.push_back();
650 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000651
reed@android.com8a1c16f2008-12-17 15:59:43 +0000652 newTop->fNext = fMCRec;
653 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000654
reed@google.com5c3d1472011-02-22 19:12:23 +0000655 fClipStack.save();
656 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
657
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658 return saveCount;
659}
660
661int SkCanvas::save(SaveFlags flags) {
662 // call shared impl
663 return this->internalSave(flags);
664}
665
666#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
667#define C16MASK (1 << SkBitmap::kRGB_565_Config)
668#define C8MASK (1 << SkBitmap::kA8_Config)
669
670static SkBitmap::Config resolve_config(SkCanvas* canvas,
671 const SkIRect& bounds,
672 SkCanvas::SaveFlags flags,
673 bool* isOpaque) {
674 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
675
676#if 0
677 // loop through and union all the configs we may draw into
678 uint32_t configMask = 0;
679 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
680 {
681 SkDevice* device = canvas->getLayerDevice(i);
682 if (device->intersects(bounds))
683 configMask |= 1 << device->config();
684 }
685
686 // if the caller wants alpha or fullcolor, we can't return 565
687 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
688 SkCanvas::kHasAlphaLayer_SaveFlag))
689 configMask &= ~C16MASK;
690
691 switch (configMask) {
692 case C8MASK: // if we only have A8, return that
693 return SkBitmap::kA8_Config;
694
695 case C16MASK: // if we only have 565, return that
696 return SkBitmap::kRGB_565_Config;
697
698 default:
699 return SkBitmap::kARGB_8888_Config; // default answer
700 }
701#else
702 return SkBitmap::kARGB_8888_Config; // default answer
703#endif
704}
705
706static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
707 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
708}
709
710int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
711 SaveFlags flags) {
712 // do this before we create the layer. We don't call the public save() since
713 // that would invoke a possibly overridden virtual
714 int count = this->internalSave(flags);
715
716 fDeviceCMDirty = true;
717
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000718 SkIRect clipBounds;
719 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000720 return count;
721 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000723 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 if (NULL != bounds) {
725 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000726
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 this->getTotalMatrix().mapRect(&r, *bounds);
728 r.roundOut(&ir);
729 // early exit if the layer's bounds are clipped out
730 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000731 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000732 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000733 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 return count;
735 }
736 } else { // no user bounds, so just use the clip
737 ir = clipBounds;
738 }
739
reed@google.com5c3d1472011-02-22 19:12:23 +0000740 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 // early exit if the clip is now empty
742 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000743 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 return count;
745 }
746
reed@google.comb55deeb2012-01-06 14:43:09 +0000747 // Kill the imagefilter if our device doesn't allow it
748 SkLazyPaint lazyP;
749 if (paint && paint->getImageFilter()) {
750 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
751 SkPaint* p = lazyP.set(*paint);
752 p->setImageFilter(NULL);
753 paint = p;
754 }
755 }
756
reed@android.com8a1c16f2008-12-17 15:59:43 +0000757 bool isOpaque;
758 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
759
reed@google.com76dd2772012-01-05 21:15:07 +0000760 SkDevice* device;
761 if (paint && paint->getImageFilter()) {
762 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
763 isOpaque);
764 } else {
765 device = this->createLayerDevice(config, ir.width(), ir.height(),
766 isOpaque);
767 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000768 if (NULL == device) {
769 SkDebugf("Unable to create device for layer.");
770 return count;
771 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000772
reed@google.com6f8f2922011-03-04 22:27:10 +0000773 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000774 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
775 device->unref();
776
777 layer->fNext = fMCRec->fTopLayer;
778 fMCRec->fLayer = layer;
779 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
780
reed@google.com7c202932011-12-14 18:48:05 +0000781 fLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 return count;
783}
784
785int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
786 SaveFlags flags) {
787 if (0xFF == alpha) {
788 return this->saveLayer(bounds, NULL, flags);
789 } else {
790 SkPaint tmpPaint;
791 tmpPaint.setAlpha(alpha);
792 return this->saveLayer(bounds, &tmpPaint, flags);
793 }
794}
795
796void SkCanvas::restore() {
797 // check for underflow
798 if (fMCStack.count() > 1) {
799 this->internalRestore();
800 }
801}
802
803void SkCanvas::internalRestore() {
804 SkASSERT(fMCStack.count() != 0);
805
806 fDeviceCMDirty = true;
807 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000808 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809
reed@google.com5c3d1472011-02-22 19:12:23 +0000810 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000811 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812 DeviceCM* layer = fMCRec->fLayer; // may be null
813 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
814 fMCRec->fLayer = NULL;
815
816 // now do the normal restore()
817 fMCRec->~MCRec(); // balanced in save()
818 fMCStack.pop_back();
819 fMCRec = (MCRec*)fMCStack.back();
820
821 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
822 since if we're being recorded, we don't want to record this (the
823 recorder will have already recorded the restore).
824 */
825 if (NULL != layer) {
826 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000827 const SkIPoint& origin = layer->fDevice->getOrigin();
828 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 layer->fPaint);
830 // reset this, since drawDevice will have set it to true
831 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000832
833 SkASSERT(fLayerCount > 0);
834 fLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000835 }
836 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000837 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000838
839 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840}
841
842int SkCanvas::getSaveCount() const {
843 return fMCStack.count();
844}
845
846void SkCanvas::restoreToCount(int count) {
847 // sanity check
848 if (count < 1) {
849 count = 1;
850 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000851
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000852 int n = this->getSaveCount() - count;
853 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 this->restore();
855 }
856}
857
reed@google.com7c202932011-12-14 18:48:05 +0000858bool SkCanvas::isDrawingToLayer() const {
859 return fLayerCount > 0;
860}
861
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862/////////////////////////////////////////////////////////////////////////////
863
864// can't draw it if its empty, or its too big for a fixed-point width or height
865static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000866 return bitmap.width() <= 0 || bitmap.height() <= 0
867#ifndef SK_ALLOW_OVER_32K_BITMAPS
868 || bitmap.width() > 32767 || bitmap.height() > 32767
869#endif
870 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000871}
872
reed@android.comf2b98d62010-12-20 18:26:13 +0000873void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 const SkMatrix& matrix, const SkPaint* paint) {
875 if (reject_bitmap(bitmap)) {
876 return;
877 }
878
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000879 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000881 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000883 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884}
885
reed@google.com76dd2772012-01-05 21:15:07 +0000886#include "SkImageFilter.h"
887
888class DeviceImageFilterProxy : public SkImageFilter::Proxy {
889public:
890 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000891
reed@google.com76dd2772012-01-05 21:15:07 +0000892 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE;
893 virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
894 const SkMatrix& ctm,
895 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
896
897private:
898 SkDevice* fDevice;
899};
900
901SkDevice* DeviceImageFilterProxy::createDevice(int w, int h) {
902 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
903 w, h, false);
904}
905
906bool DeviceImageFilterProxy::filterImage(SkImageFilter* filter,
907 const SkBitmap& src,
908 const SkMatrix& ctm,
909 SkBitmap* result,
910 SkIPoint* offset) {
911 return fDevice->filterImage(filter, src, ctm, result, offset);
912}
913
reed@google.comb55deeb2012-01-06 14:43:09 +0000914void SkCanvas::drawDevice(SkDevice* srcDev, int x, int y,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000915 const SkPaint* paint) {
916 SkPaint tmp;
917 if (NULL == paint) {
918 tmp.setDither(true);
919 paint = &tmp;
920 }
reed@google.com4b226022011-01-11 18:32:13 +0000921
reed@google.com4e2b3d32011-04-07 14:18:59 +0000922 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000924 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000925 paint = &looper.paint();
926 SkImageFilter* filter = paint->getImageFilter();
927 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
928 if (filter) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000929 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000930 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000931 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000932 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
933 SkPaint tmp(*paint);
934 tmp.setImageFilter(NULL);
reed@google.comb55deeb2012-01-06 14:43:09 +0000935 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
reed@google.com76dd2772012-01-05 21:15:07 +0000936 }
937 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000938 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000939 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000941 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942}
943
944/////////////////////////////////////////////////////////////////////////////
945
946bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
947 fDeviceCMDirty = true;
948 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000949 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 return fMCRec->fMatrix->preTranslate(dx, dy);
951}
952
953bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
954 fDeviceCMDirty = true;
955 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000956 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 return fMCRec->fMatrix->preScale(sx, sy);
958}
959
960bool SkCanvas::rotate(SkScalar degrees) {
961 fDeviceCMDirty = true;
962 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000963 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964 return fMCRec->fMatrix->preRotate(degrees);
965}
966
967bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
968 fDeviceCMDirty = true;
969 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000970 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 return fMCRec->fMatrix->preSkew(sx, sy);
972}
973
974bool SkCanvas::concat(const SkMatrix& matrix) {
975 fDeviceCMDirty = true;
976 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000977 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 return fMCRec->fMatrix->preConcat(matrix);
979}
980
981void SkCanvas::setMatrix(const SkMatrix& matrix) {
982 fDeviceCMDirty = true;
983 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000984 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 *fMCRec->fMatrix = matrix;
986}
987
988// this is not virtual, so it must call a virtual method so that subclasses
989// will see its action
990void SkCanvas::resetMatrix() {
991 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 matrix.reset();
994 this->setMatrix(matrix);
995}
996
997//////////////////////////////////////////////////////////////////////////////
998
reed@google.comc42d35d2011-10-12 11:57:42 +0000999bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001000 AutoValidateClip avc(this);
1001
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 fDeviceCMDirty = true;
1003 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001004 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005
1006 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001007 // for these simpler matrices, we can stay a rect ever after applying
1008 // the matrix. This means we don't have to a) make a path, and b) tell
1009 // the region code to scan-convert the path, only to discover that it
1010 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012
1013 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001014 fClipStack.clipDevRect(r, op, doAA);
1015 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001017 // since we're rotate or some such thing, we convert the rect to a path
1018 // and clip against that, since it can handle any matrix. However, to
1019 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1020 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001021 SkPath path;
1022
1023 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001024 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001025 }
1026}
1027
reed@google.com00177082011-10-12 14:34:30 +00001028static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001029 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001030 // base is used to limit the size (and therefore memory allocation) of the
1031 // region that results from scan converting devPath.
1032 SkRegion base;
1033
reed@google.com819c9212011-02-23 18:56:55 +00001034 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001035 // since we are intersect, we can do better (tighter) with currRgn's
1036 // bounds, than just using the device. However, if currRgn is complex,
1037 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001038 if (currClip->isRect()) {
1039 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001040 } else {
reed@google.com00177082011-10-12 14:34:30 +00001041 base.setRect(currClip->getBounds());
1042 SkRasterClip clip;
1043 clip.setPath(devPath, base, doAA);
1044 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001045 }
reed@google.com819c9212011-02-23 18:56:55 +00001046 } else {
reed@google.com819c9212011-02-23 18:56:55 +00001047 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
1048 base.setRect(0, 0, bm.width(), bm.height());
1049
1050 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001051 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001052 } else {
reed@google.com00177082011-10-12 14:34:30 +00001053 SkRasterClip clip;
1054 clip.setPath(devPath, base, doAA);
1055 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001056 }
1057 }
1058}
1059
reed@google.comc42d35d2011-10-12 11:57:42 +00001060bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001061 AutoValidateClip avc(this);
1062
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 fDeviceCMDirty = true;
1064 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001065 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066
1067 SkPath devPath;
1068 path.transform(*fMCRec->fMatrix, &devPath);
1069
reed@google.comfe701122011-11-08 19:41:23 +00001070 // Check if the transfomation, or the original path itself
1071 // made us empty. Note this can also happen if we contained NaN
1072 // values. computing the bounds detects this, and will set our
1073 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1074 if (devPath.getBounds().isEmpty()) {
1075 // resetting the path will remove any NaN or other wanky values
1076 // that might upset our scan converter.
1077 devPath.reset();
1078 }
1079
reed@google.com5c3d1472011-02-22 19:12:23 +00001080 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001081 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001082
reed@google.com00177082011-10-12 14:34:30 +00001083 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084}
1085
1086bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001087 AutoValidateClip avc(this);
1088
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 fDeviceCMDirty = true;
1090 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001091 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092
reed@google.com5c3d1472011-02-22 19:12:23 +00001093 // todo: signal fClipStack that we have a region, and therefore (I guess)
1094 // we have to ignore it, and use the region directly?
1095 fClipStack.clipDevRect(rgn.getBounds());
1096
reed@google.com00177082011-10-12 14:34:30 +00001097 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098}
1099
reed@google.com819c9212011-02-23 18:56:55 +00001100#ifdef SK_DEBUG
1101void SkCanvas::validateClip() const {
1102 // construct clipRgn from the clipstack
1103 const SkDevice* device = this->getDevice();
1104 SkIRect ir;
1105 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001106 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001107
1108 SkClipStack::B2FIter iter(fClipStack);
1109 const SkClipStack::B2FIter::Clip* clip;
1110 while ((clip = iter.next()) != NULL) {
1111 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001112 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001113 } else if (clip->fRect) {
1114 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001115 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001116 } else {
reed@google.com00177082011-10-12 14:34:30 +00001117 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001118 }
1119 }
1120
reed@google.com6f8f2922011-03-04 22:27:10 +00001121#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001122 // now compare against the current rgn
1123 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001124 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001125#endif
reed@google.com819c9212011-02-23 18:56:55 +00001126}
1127#endif
1128
reed@google.com5c3d1472011-02-22 19:12:23 +00001129///////////////////////////////////////////////////////////////////////////////
1130
reed@android.comba09de42010-02-05 20:46:05 +00001131void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001133 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1134 fLocalBoundsCompareTypeBW;
1135
1136 if (!this->getClipBounds(&r, et)) {
1137 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001139 rCompare.set(SkScalarToCompareType(r.fLeft),
1140 SkScalarToCompareType(r.fTop),
1141 SkScalarToCompareType(r.fRight),
1142 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 }
1144}
1145
reed@android.comd252db02009-04-01 18:31:44 +00001146/* current impl ignores edgetype, and relies on
1147 getLocalClipBoundsCompareType(), which always returns a value assuming
1148 antialiasing (worst case)
1149 */
reed@android.comba09de42010-02-05 20:46:05 +00001150bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001151
reed@google.com16078632011-12-06 18:56:37 +00001152 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001153 return true;
1154
reed@google.com00177082011-10-12 14:34:30 +00001155 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 return true;
1157 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158
tomhudson@google.com8d430182011-06-06 19:11:19 +00001159 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001160 SkRect dst;
1161 fMCRec->fMatrix->mapRect(&dst, rect);
1162 SkIRect idst;
1163 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001164 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001165 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001166 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001167
reed@android.coma380ae42009-07-21 01:17:02 +00001168 // for speed, do the most likely reject compares first
1169 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1170 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1171 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1172 return true;
1173 }
1174 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1175 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1176 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1177 return true;
1178 }
1179 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181}
1182
1183bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001184 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185}
1186
1187bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1188 /* current impl ignores edgetype, and relies on
1189 getLocalClipBoundsCompareType(), which always returns a value assuming
1190 antialiasing (worst case)
1191 */
1192
reed@google.com00177082011-10-12 14:34:30 +00001193 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194 return true;
1195 }
reed@google.com4b226022011-01-11 18:32:13 +00001196
reed@android.comaefd2bc2009-03-30 21:02:14 +00001197 SkScalarCompareType userT = SkScalarToCompareType(top);
1198 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001199
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001201 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202 if (userT >= userB) {
1203 return true;
1204 }
reed@google.com4b226022011-01-11 18:32:13 +00001205
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206 // check if we are above or below the local clip bounds
1207 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1208 return userT >= clipR.fBottom || userB <= clipR.fTop;
1209}
1210
1211bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001212 SkIRect ibounds;
1213 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214 return false;
1215 }
1216
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001217 SkMatrix inverse;
1218 // if we can't invert the CTM, we can't return local clip bounds
1219 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001220 if (bounds) {
1221 bounds->setEmpty();
1222 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001223 return false;
1224 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001226 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001227 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001228 // adjust it outwards if we are antialiasing
1229 int inset = (kAA_EdgeType == et);
1230 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1231 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232 inverse.mapRect(bounds, r);
1233 }
1234 return true;
1235}
1236
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001237bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001238 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001239 if (clip.isEmpty()) {
1240 if (bounds) {
1241 bounds->setEmpty();
1242 }
1243 return false;
1244 }
1245
1246 if (NULL != bounds) {
1247 *bounds = clip.getBounds();
1248 }
1249 return true;
1250}
1251
reed@android.com8a1c16f2008-12-17 15:59:43 +00001252const SkMatrix& SkCanvas::getTotalMatrix() const {
1253 return *fMCRec->fMatrix;
1254}
1255
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001256SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001257 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1258 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001259 return kComplex_ClipType;
1260}
1261
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001263 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001264}
1265
reed@google.com7d7ca792011-02-23 22:39:18 +00001266const SkClipStack& SkCanvas::getTotalClipStack() const {
1267 return fClipStack;
1268}
1269
reed@android.comf2b98d62010-12-20 18:26:13 +00001270void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1271 if (NULL == matrix || matrix->isIdentity()) {
1272 if (fUseExternalMatrix) {
1273 fDeviceCMDirty = true;
1274 }
1275 fUseExternalMatrix = false;
1276 } else {
1277 fUseExternalMatrix = true;
1278 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001279
reed@android.comf2b98d62010-12-20 18:26:13 +00001280 fExternalMatrix = *matrix;
1281 matrix->invert(&fExternalInverse);
1282 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001283}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284
bsalomon@google.come97f0852011-06-17 13:10:25 +00001285SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1286 int width, int height,
1287 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001288 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001289 if (device) {
1290 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1291 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001292 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001293 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001294 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295}
1296
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001297SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001298 int width, int height,
1299 bool isOpaque) {
1300 SkDevice* device = this->getDevice();
1301 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001302 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001303 } else {
1304 return NULL;
1305 }
1306}
1307
1308
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309//////////////////////////////////////////////////////////////////////////////
1310// These are the virtual drawing methods
1311//////////////////////////////////////////////////////////////////////////////
1312
reed@google.com2a981812011-04-14 18:59:28 +00001313void SkCanvas::clear(SkColor color) {
1314 SkDrawIter iter(this);
1315
1316 while (iter.next()) {
1317 iter.fDevice->clear(color);
1318 }
1319}
1320
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001322 this->internalDrawPaint(paint);
1323}
1324
1325void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001326 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327
1328 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001329 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 }
1331
reed@google.com4e2b3d32011-04-07 14:18:59 +00001332 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333}
1334
1335void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1336 const SkPaint& paint) {
1337 if ((long)count <= 0) {
1338 return;
1339 }
1340
1341 SkASSERT(pts != NULL);
1342
reed@google.com4e2b3d32011-04-07 14:18:59 +00001343 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001344
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001346 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001347 }
reed@google.com4b226022011-01-11 18:32:13 +00001348
reed@google.com4e2b3d32011-04-07 14:18:59 +00001349 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350}
1351
1352void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1353 if (paint.canComputeFastBounds()) {
1354 SkRect storage;
1355 if (this->quickReject(paint.computeFastBounds(r, &storage),
1356 paint2EdgeType(&paint))) {
1357 return;
1358 }
1359 }
reed@google.com4b226022011-01-11 18:32:13 +00001360
reed@google.com4e2b3d32011-04-07 14:18:59 +00001361 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362
1363 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001364 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001365 }
1366
reed@google.com4e2b3d32011-04-07 14:18:59 +00001367 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368}
1369
1370void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001371 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001372 SkRect storage;
1373 const SkRect& bounds = path.getBounds();
1374 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001375 paint2EdgeType(&paint))) {
1376 return;
1377 }
1378 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001379 if (path.isEmpty()) {
1380 if (path.isInverseFillType()) {
1381 this->internalDrawPaint(paint);
1382 }
1383 return;
1384 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001385
reed@google.com4e2b3d32011-04-07 14:18:59 +00001386 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387
1388 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001389 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001390 }
1391
reed@google.com4e2b3d32011-04-07 14:18:59 +00001392 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393}
1394
1395void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1396 const SkPaint* paint) {
1397 SkDEBUGCODE(bitmap.validate();)
1398
reed@google.com3d608122011-11-21 15:16:16 +00001399 if (NULL == paint || paint->canComputeFastBounds()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001400 SkRect fastBounds;
1401 fastBounds.set(x, y,
1402 x + SkIntToScalar(bitmap.width()),
1403 y + SkIntToScalar(bitmap.height()));
1404 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1405 return;
1406 }
1407 }
reed@google.com4b226022011-01-11 18:32:13 +00001408
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 SkMatrix matrix;
1410 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001411 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412}
1413
reed@google.com9987ec32011-09-07 11:57:52 +00001414// this one is non-virtual, so it can be called safely by other canvas apis
1415void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1416 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1418 return;
1419 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001420
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001422 if (NULL == paint || paint->canComputeFastBounds()) {
1423 if (this->quickReject(dst, paint2EdgeType(paint))) {
1424 return;
1425 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 }
reed@google.com3d608122011-11-21 15:16:16 +00001427
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001429
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001431 SkRect tmpSrc;
1432 if (src) {
1433 tmpSrc.set(*src);
1434 // if the extract process clipped off the top or left of the
1435 // original, we adjust for that here to get the position right.
1436 if (tmpSrc.fLeft > 0) {
1437 tmpSrc.fRight -= tmpSrc.fLeft;
1438 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001439 }
reed@android.com87899992009-10-16 14:48:38 +00001440 if (tmpSrc.fTop > 0) {
1441 tmpSrc.fBottom -= tmpSrc.fTop;
1442 tmpSrc.fTop = 0;
1443 }
1444 } else {
1445 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1446 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447 }
reed@android.com87899992009-10-16 14:48:38 +00001448 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001449
reed@android.comf2b98d62010-12-20 18:26:13 +00001450 // ensure that src is "valid" before we pass it to our internal routines
1451 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1452 SkIRect tmpISrc;
1453 if (src) {
1454 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001455 if (!tmpISrc.intersect(*src)) {
1456 return;
1457 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001458 src = &tmpISrc;
1459 }
1460 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461}
1462
reed@google.com9987ec32011-09-07 11:57:52 +00001463void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1464 const SkRect& dst, const SkPaint* paint) {
1465 SkDEBUGCODE(bitmap.validate();)
1466 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1467}
1468
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1470 const SkPaint* paint) {
1471 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001472 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473}
1474
reed@android.comf2b98d62010-12-20 18:26:13 +00001475void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1476 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001478
reed@google.com4e2b3d32011-04-07 14:18:59 +00001479 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001480
reed@android.com8a1c16f2008-12-17 15:59:43 +00001481 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001482 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 }
reed@android.com9b039062009-02-11 15:09:58 +00001484
reed@google.com4e2b3d32011-04-07 14:18:59 +00001485 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486}
1487
reed@google.com9987ec32011-09-07 11:57:52 +00001488void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1489 const SkIRect& center, const SkRect& dst,
1490 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001491 if (NULL == paint || paint->canComputeFastBounds()) {
1492 if (this->quickReject(dst, paint2EdgeType(paint))) {
1493 return;
1494 }
1495 }
1496
reed@google.com9987ec32011-09-07 11:57:52 +00001497 const int32_t w = bitmap.width();
1498 const int32_t h = bitmap.height();
1499
1500 SkIRect c = center;
1501 // pin center to the bounds of the bitmap
1502 c.fLeft = SkMax32(0, center.fLeft);
1503 c.fTop = SkMax32(0, center.fTop);
1504 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1505 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1506
1507 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1508 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1509 SkScalar dstX[4] = {
1510 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1511 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1512 };
1513 SkScalar dstY[4] = {
1514 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1515 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1516 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001517
reed@google.com9987ec32011-09-07 11:57:52 +00001518 if (dstX[1] > dstX[2]) {
1519 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1520 dstX[2] = dstX[1];
1521 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001522
reed@google.com9987ec32011-09-07 11:57:52 +00001523 if (dstY[1] > dstY[2]) {
1524 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1525 dstY[2] = dstY[1];
1526 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001527
reed@google.com9987ec32011-09-07 11:57:52 +00001528 SkIRect s;
1529 SkRect d;
1530 for (int y = 0; y < 3; y++) {
1531 s.fTop = srcY[y];
1532 s.fBottom = srcY[y+1];
1533 d.fTop = dstY[y];
1534 d.fBottom = dstY[y+1];
1535 for (int x = 0; x < 3; x++) {
1536 s.fLeft = srcX[x];
1537 s.fRight = srcX[x+1];
1538 d.fLeft = dstX[x];
1539 d.fRight = dstX[x+1];
1540 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1541 }
1542 }
1543}
1544
1545void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1546 const SkRect& dst, const SkPaint* paint) {
1547 SkDEBUGCODE(bitmap.validate();)
1548
1549 // Need a device entry-point, so gpu can use a mesh
1550 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1551}
1552
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1554 const SkPaint* paint) {
1555 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001556
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557 if (reject_bitmap(bitmap)) {
1558 return;
1559 }
reed@google.com4b226022011-01-11 18:32:13 +00001560
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561 SkPaint tmp;
1562 if (NULL == paint) {
1563 paint = &tmp;
1564 }
reed@google.com4b226022011-01-11 18:32:13 +00001565
reed@google.com4e2b3d32011-04-07 14:18:59 +00001566 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001567
reed@android.com8a1c16f2008-12-17 15:59:43 +00001568 while (iter.next()) {
1569 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001570 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001571 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001572 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573}
1574
reed@google.comf67e4cf2011-03-15 20:56:58 +00001575class SkDeviceFilteredPaint {
1576public:
1577 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1578 SkDevice::TextFlags flags;
1579 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001580 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001581 newPaint->setFlags(flags.fFlags);
1582 newPaint->setHinting(flags.fHinting);
1583 fPaint = newPaint;
1584 } else {
1585 fPaint = &paint;
1586 }
1587 }
1588
reed@google.comf67e4cf2011-03-15 20:56:58 +00001589 const SkPaint& paint() const { return *fPaint; }
1590
1591private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001592 const SkPaint* fPaint;
1593 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001594};
1595
bungeman@google.com52c748b2011-08-22 21:30:43 +00001596void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1597 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001598 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001599 draw.fDevice->drawRect(draw, r, paint);
1600 } else {
1601 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001602 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001603 draw.fDevice->drawRect(draw, r, p);
1604 }
1605}
1606
1607void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1608 const char text[], size_t byteLength,
1609 SkScalar x, SkScalar y) {
1610 SkASSERT(byteLength == 0 || text != NULL);
1611
1612 // nothing to draw
1613 if (text == NULL || byteLength == 0 ||
1614 draw.fClip->isEmpty() ||
1615 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1616 return;
1617 }
1618
1619 SkScalar width = 0;
1620 SkPoint start;
1621
1622 start.set(0, 0); // to avoid warning
1623 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1624 SkPaint::kStrikeThruText_Flag)) {
1625 width = paint.measureText(text, byteLength);
1626
1627 SkScalar offsetX = 0;
1628 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1629 offsetX = SkScalarHalf(width);
1630 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1631 offsetX = width;
1632 }
1633 start.set(x - offsetX, y);
1634 }
1635
1636 if (0 == width) {
1637 return;
1638 }
1639
1640 uint32_t flags = paint.getFlags();
1641
1642 if (flags & (SkPaint::kUnderlineText_Flag |
1643 SkPaint::kStrikeThruText_Flag)) {
1644 SkScalar textSize = paint.getTextSize();
1645 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1646 SkRect r;
1647
1648 r.fLeft = start.fX;
1649 r.fRight = start.fX + width;
1650
1651 if (flags & SkPaint::kUnderlineText_Flag) {
1652 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1653 start.fY);
1654 r.fTop = offset;
1655 r.fBottom = offset + height;
1656 DrawRect(draw, paint, r, textSize);
1657 }
1658 if (flags & SkPaint::kStrikeThruText_Flag) {
1659 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1660 start.fY);
1661 r.fTop = offset;
1662 r.fBottom = offset + height;
1663 DrawRect(draw, paint, r, textSize);
1664 }
1665 }
1666}
1667
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668void SkCanvas::drawText(const void* text, size_t byteLength,
1669 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001670 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001671
1672 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001673 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001674 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001675 DrawTextDecorations(iter, dfp.paint(),
1676 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677 }
1678
reed@google.com4e2b3d32011-04-07 14:18:59 +00001679 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680}
1681
1682void SkCanvas::drawPosText(const void* text, size_t byteLength,
1683 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001684 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001685
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001687 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001688 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001689 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001690 }
reed@google.com4b226022011-01-11 18:32:13 +00001691
reed@google.com4e2b3d32011-04-07 14:18:59 +00001692 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001693}
1694
1695void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1696 const SkScalar xpos[], SkScalar constY,
1697 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001698 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001699
reed@android.com8a1c16f2008-12-17 15:59:43 +00001700 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001701 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001703 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704 }
reed@google.com4b226022011-01-11 18:32:13 +00001705
reed@google.com4e2b3d32011-04-07 14:18:59 +00001706 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707}
1708
1709void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1710 const SkPath& path, const SkMatrix* matrix,
1711 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001712 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713
1714 while (iter.next()) {
1715 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001716 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001717 }
1718
reed@google.com4e2b3d32011-04-07 14:18:59 +00001719 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720}
1721
djsollen@google.com56c69772011-11-08 19:00:26 +00001722#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001723void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1724 const SkPoint pos[], const SkPaint& paint,
1725 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001726 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001727
1728 while (iter.next()) {
1729 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001730 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001731 }
1732
reed@google.com4e2b3d32011-04-07 14:18:59 +00001733 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001734}
1735#endif
1736
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1738 const SkPoint verts[], const SkPoint texs[],
1739 const SkColor colors[], SkXfermode* xmode,
1740 const uint16_t indices[], int indexCount,
1741 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001742 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001743
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744 while (iter.next()) {
1745 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001746 colors, xmode, indices, indexCount,
1747 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748 }
reed@google.com4b226022011-01-11 18:32:13 +00001749
reed@google.com4e2b3d32011-04-07 14:18:59 +00001750 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751}
1752
reed@android.comcb608442009-12-04 21:32:27 +00001753void SkCanvas::drawData(const void* data, size_t length) {
1754 // do nothing. Subclasses may do something with the data
1755}
1756
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757//////////////////////////////////////////////////////////////////////////////
1758// These methods are NOT virtual, and therefore must call back into virtual
1759// methods, rather than actually drawing themselves.
1760//////////////////////////////////////////////////////////////////////////////
1761
1762void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001763 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 SkPaint paint;
1765
1766 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001767 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001768 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769 }
1770 this->drawPaint(paint);
1771}
1772
reed@android.com845fdac2009-06-23 03:01:32 +00001773void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 SkPaint paint;
1775
1776 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001777 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001778 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 }
1780 this->drawPaint(paint);
1781}
1782
1783void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1784 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001785
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786 pt.set(x, y);
1787 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1788}
1789
1790void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1791 SkPoint pt;
1792 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001793
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794 pt.set(x, y);
1795 paint.setColor(color);
1796 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1797}
1798
1799void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1800 const SkPaint& paint) {
1801 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001802
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 pts[0].set(x0, y0);
1804 pts[1].set(x1, y1);
1805 this->drawPoints(kLines_PointMode, 2, pts, paint);
1806}
1807
1808void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1809 SkScalar right, SkScalar bottom,
1810 const SkPaint& paint) {
1811 SkRect r;
1812
1813 r.set(left, top, right, bottom);
1814 this->drawRect(r, paint);
1815}
1816
1817void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1818 const SkPaint& paint) {
1819 if (radius < 0) {
1820 radius = 0;
1821 }
1822
1823 SkRect r;
1824 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001825
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826 if (paint.canComputeFastBounds()) {
1827 SkRect storage;
1828 if (this->quickReject(paint.computeFastBounds(r, &storage),
1829 paint2EdgeType(&paint))) {
1830 return;
1831 }
1832 }
reed@google.com4b226022011-01-11 18:32:13 +00001833
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 SkPath path;
1835 path.addOval(r);
1836 this->drawPath(path, paint);
1837}
1838
1839void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1840 const SkPaint& paint) {
1841 if (rx > 0 && ry > 0) {
1842 if (paint.canComputeFastBounds()) {
1843 SkRect storage;
1844 if (this->quickReject(paint.computeFastBounds(r, &storage),
1845 paint2EdgeType(&paint))) {
1846 return;
1847 }
1848 }
1849
1850 SkPath path;
1851 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1852 this->drawPath(path, paint);
1853 } else {
1854 this->drawRect(r, paint);
1855 }
1856}
1857
1858void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1859 if (paint.canComputeFastBounds()) {
1860 SkRect storage;
1861 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1862 paint2EdgeType(&paint))) {
1863 return;
1864 }
1865 }
1866
1867 SkPath path;
1868 path.addOval(oval);
1869 this->drawPath(path, paint);
1870}
1871
1872void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1873 SkScalar sweepAngle, bool useCenter,
1874 const SkPaint& paint) {
1875 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1876 this->drawOval(oval, paint);
1877 } else {
1878 SkPath path;
1879 if (useCenter) {
1880 path.moveTo(oval.centerX(), oval.centerY());
1881 }
1882 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1883 if (useCenter) {
1884 path.close();
1885 }
1886 this->drawPath(path, paint);
1887 }
1888}
1889
1890void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1891 const SkPath& path, SkScalar hOffset,
1892 SkScalar vOffset, const SkPaint& paint) {
1893 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001894
reed@android.com8a1c16f2008-12-17 15:59:43 +00001895 matrix.setTranslate(hOffset, vOffset);
1896 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1897}
1898
reed@android.comf76bacf2009-05-13 14:00:33 +00001899///////////////////////////////////////////////////////////////////////////////
1900
reed@android.com8a1c16f2008-12-17 15:59:43 +00001901void SkCanvas::drawPicture(SkPicture& picture) {
1902 int saveCount = save();
1903 picture.draw(this);
1904 restoreToCount(saveCount);
1905}
1906
reed@android.com8a1c16f2008-12-17 15:59:43 +00001907///////////////////////////////////////////////////////////////////////////////
1908///////////////////////////////////////////////////////////////////////////////
1909
1910SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001911 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912
1913 SkASSERT(canvas);
1914
1915 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1916 fDone = !fImpl->next();
1917}
1918
1919SkCanvas::LayerIter::~LayerIter() {
1920 fImpl->~SkDrawIter();
1921}
1922
1923void SkCanvas::LayerIter::next() {
1924 fDone = !fImpl->next();
1925}
1926
1927SkDevice* SkCanvas::LayerIter::device() const {
1928 return fImpl->getDevice();
1929}
1930
1931const SkMatrix& SkCanvas::LayerIter::matrix() const {
1932 return fImpl->getMatrix();
1933}
1934
1935const SkPaint& SkCanvas::LayerIter::paint() const {
1936 const SkPaint* paint = fImpl->getPaint();
1937 if (NULL == paint) {
1938 paint = &fDefaultPaint;
1939 }
1940 return *paint;
1941}
1942
1943const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1944int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1945int SkCanvas::LayerIter::y() const { return fImpl->getY(); }