blob: 2a5a748c85b561f0c35ea42f3248e588391d5945 [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
reed@google.com210ce002011-11-01 14:24:23 +0000473SkISize SkCanvas::getDeviceSize() const {
474 SkDevice* d = this->getDevice();
475 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
476}
477
reed@android.com8a1c16f2008-12-17 15:59:43 +0000478SkDevice* SkCanvas::getDevice() const {
479 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000480 SkDeque::F2BIter iter(fMCStack);
481 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 SkASSERT(rec && rec->fLayer);
483 return rec->fLayer->fDevice;
484}
485
reed@google.com9266fed2011-03-30 00:18:03 +0000486SkDevice* SkCanvas::getTopDevice() const {
487 return fMCRec->fTopLayer->fDevice;
488}
489
reed@android.com8a1c16f2008-12-17 15:59:43 +0000490SkDevice* SkCanvas::setDevice(SkDevice* device) {
491 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000492 SkDeque::F2BIter iter(fMCStack);
493 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 SkASSERT(rec && rec->fLayer);
495 SkDevice* rootDevice = rec->fLayer->fDevice;
496
497 if (rootDevice == device) {
498 return device;
499 }
reed@google.com4b226022011-01-11 18:32:13 +0000500
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 /* Notify the devices that they are going in/out of scope, so they can do
502 things like lock/unlock their pixels, etc.
503 */
504 if (device) {
505 device->lockPixels();
506 }
507 if (rootDevice) {
508 rootDevice->unlockPixels();
509 }
510
511 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
512 rootDevice = device;
513
514 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000515
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 /* Now we update our initial region to have the bounds of the new device,
517 and then intersect all of the clips in our stack with these bounds,
518 to ensure that we can't draw outside of the device's bounds (and trash
519 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000520
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 NOTE: this is only a partial-fix, since if the new device is larger than
522 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000523 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
525 reconstruct the correct clips, so this approximation will have to do.
526 The caller really needs to restore() back to the base if they want to
527 accurately take advantage of the new device bounds.
528 */
529
530 if (NULL == device) {
reed@google.com00177082011-10-12 14:34:30 +0000531 rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000533 (void)rec->fRasterClip->setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000535 fClipStack.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 } else {
537 // compute our total bounds for all devices
538 SkIRect bounds;
reed@google.com4b226022011-01-11 18:32:13 +0000539
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 bounds.set(0, 0, device->width(), device->height());
541
542 // now jam our 1st clip to be bounds, and intersect the rest with that
reed@google.com00177082011-10-12 14:34:30 +0000543 rec->fRasterClip->setRect(bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 while ((rec = (MCRec*)iter.next()) != NULL) {
reed@google.com00177082011-10-12 14:34:30 +0000545 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 }
547 }
548 return device;
549}
550
reed@google.comaf951c92011-06-16 19:10:39 +0000551SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
552 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 device->unref();
554 return device;
555}
556
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000557bool SkCanvas::readPixels(SkBitmap* bitmap,
558 int x, int y,
559 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000560 SkDevice* device = this->getDevice();
561 if (!device) {
562 return false;
563 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000564 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000565}
566
bsalomon@google.comc6980972011-11-02 19:57:21 +0000567bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000568 SkDevice* device = this->getDevice();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000569
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000570 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000571 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000572 if (!bounds.intersect(srcRect)) {
573 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000574 }
575
576 SkBitmap tmp;
577 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
578 bounds.height());
579 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
580 bitmap->swap(tmp);
581 return true;
582 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000583 return false;
584 }
reed@google.com51df9e32010-12-23 19:29:18 +0000585}
586
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000587void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
588 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000589 SkDevice* device = this->getDevice();
590 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000591 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000592 }
593}
594
junov@google.com4370aed2012-01-18 16:21:08 +0000595SkCanvas* SkCanvas::canvasForDrawIter() {
596 return this;
597}
598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599//////////////////////////////////////////////////////////////////////////////
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601void SkCanvas::updateDeviceCMCache() {
602 if (fDeviceCMDirty) {
603 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000604 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000606
reed@android.com8a1c16f2008-12-17 15:59:43 +0000607 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000608 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000609 if (fUseExternalMatrix) {
610 layer->updateExternalMatrix(fExternalMatrix,
611 fExternalInverse);
612 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000614 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000616 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000617 if (fUseExternalMatrix) {
618 layer->updateExternalMatrix(fExternalMatrix,
619 fExternalInverse);
620 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 } while ((layer = layer->fNext) != NULL);
622 }
623 fDeviceCMDirty = false;
624 }
625}
626
reed@android.comf2b98d62010-12-20 18:26:13 +0000627void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000628 const SkRegion& clip,
629 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000631 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000632 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000633 fLastDeviceToGainFocus = device;
634 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000635}
636
637///////////////////////////////////////////////////////////////////////////////
638
639int SkCanvas::internalSave(SaveFlags flags) {
640 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000641
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642 MCRec* newTop = (MCRec*)fMCStack.push_back();
643 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000644
reed@android.com8a1c16f2008-12-17 15:59:43 +0000645 newTop->fNext = fMCRec;
646 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000647
reed@google.com5c3d1472011-02-22 19:12:23 +0000648 fClipStack.save();
649 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651 return saveCount;
652}
653
654int SkCanvas::save(SaveFlags flags) {
655 // call shared impl
656 return this->internalSave(flags);
657}
658
659#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
660#define C16MASK (1 << SkBitmap::kRGB_565_Config)
661#define C8MASK (1 << SkBitmap::kA8_Config)
662
663static SkBitmap::Config resolve_config(SkCanvas* canvas,
664 const SkIRect& bounds,
665 SkCanvas::SaveFlags flags,
666 bool* isOpaque) {
667 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
668
669#if 0
670 // loop through and union all the configs we may draw into
671 uint32_t configMask = 0;
672 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
673 {
674 SkDevice* device = canvas->getLayerDevice(i);
675 if (device->intersects(bounds))
676 configMask |= 1 << device->config();
677 }
678
679 // if the caller wants alpha or fullcolor, we can't return 565
680 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
681 SkCanvas::kHasAlphaLayer_SaveFlag))
682 configMask &= ~C16MASK;
683
684 switch (configMask) {
685 case C8MASK: // if we only have A8, return that
686 return SkBitmap::kA8_Config;
687
688 case C16MASK: // if we only have 565, return that
689 return SkBitmap::kRGB_565_Config;
690
691 default:
692 return SkBitmap::kARGB_8888_Config; // default answer
693 }
694#else
695 return SkBitmap::kARGB_8888_Config; // default answer
696#endif
697}
698
699static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
700 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
701}
702
703int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
704 SaveFlags flags) {
705 // do this before we create the layer. We don't call the public save() since
706 // that would invoke a possibly overridden virtual
707 int count = this->internalSave(flags);
708
709 fDeviceCMDirty = true;
710
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000711 SkIRect clipBounds;
712 if (!this->getClipDeviceBounds(&clipBounds)) {
reed@android.comf2b98d62010-12-20 18:26:13 +0000713 return count;
714 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000716 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 if (NULL != bounds) {
718 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000719
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 this->getTotalMatrix().mapRect(&r, *bounds);
721 r.roundOut(&ir);
722 // early exit if the layer's bounds are clipped out
723 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000724 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000725 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000726 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 return count;
728 }
729 } else { // no user bounds, so just use the clip
730 ir = clipBounds;
731 }
732
reed@google.com5c3d1472011-02-22 19:12:23 +0000733 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 // early exit if the clip is now empty
735 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000736 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737 return count;
738 }
739
reed@google.comb55deeb2012-01-06 14:43:09 +0000740 // Kill the imagefilter if our device doesn't allow it
741 SkLazyPaint lazyP;
742 if (paint && paint->getImageFilter()) {
743 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
744 SkPaint* p = lazyP.set(*paint);
745 p->setImageFilter(NULL);
746 paint = p;
747 }
748 }
749
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750 bool isOpaque;
751 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
752
reed@google.com76dd2772012-01-05 21:15:07 +0000753 SkDevice* device;
754 if (paint && paint->getImageFilter()) {
755 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
756 isOpaque);
757 } else {
758 device = this->createLayerDevice(config, ir.width(), ir.height(),
759 isOpaque);
760 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000761 if (NULL == device) {
762 SkDebugf("Unable to create device for layer.");
763 return count;
764 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000765
reed@google.com6f8f2922011-03-04 22:27:10 +0000766 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
768 device->unref();
769
770 layer->fNext = fMCRec->fTopLayer;
771 fMCRec->fLayer = layer;
772 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
773
reed@google.com7c202932011-12-14 18:48:05 +0000774 fLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 return count;
776}
777
778int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
779 SaveFlags flags) {
780 if (0xFF == alpha) {
781 return this->saveLayer(bounds, NULL, flags);
782 } else {
783 SkPaint tmpPaint;
784 tmpPaint.setAlpha(alpha);
785 return this->saveLayer(bounds, &tmpPaint, flags);
786 }
787}
788
789void SkCanvas::restore() {
790 // check for underflow
791 if (fMCStack.count() > 1) {
792 this->internalRestore();
793 }
794}
795
796void SkCanvas::internalRestore() {
797 SkASSERT(fMCStack.count() != 0);
798
799 fDeviceCMDirty = true;
800 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000801 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802
reed@google.com5c3d1472011-02-22 19:12:23 +0000803 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000804 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805 DeviceCM* layer = fMCRec->fLayer; // may be null
806 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
807 fMCRec->fLayer = NULL;
808
809 // now do the normal restore()
810 fMCRec->~MCRec(); // balanced in save()
811 fMCStack.pop_back();
812 fMCRec = (MCRec*)fMCStack.back();
813
814 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
815 since if we're being recorded, we don't want to record this (the
816 recorder will have already recorded the restore).
817 */
818 if (NULL != layer) {
819 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000820 const SkIPoint& origin = layer->fDevice->getOrigin();
821 this->drawDevice(layer->fDevice, origin.x(), origin.y(),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 layer->fPaint);
823 // reset this, since drawDevice will have set it to true
824 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000825
826 SkASSERT(fLayerCount > 0);
827 fLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 }
829 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000830 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000831
832 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833}
834
835int SkCanvas::getSaveCount() const {
836 return fMCStack.count();
837}
838
839void SkCanvas::restoreToCount(int count) {
840 // sanity check
841 if (count < 1) {
842 count = 1;
843 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000844
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000845 int n = this->getSaveCount() - count;
846 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000847 this->restore();
848 }
849}
850
reed@google.com7c202932011-12-14 18:48:05 +0000851bool SkCanvas::isDrawingToLayer() const {
852 return fLayerCount > 0;
853}
854
reed@android.com8a1c16f2008-12-17 15:59:43 +0000855/////////////////////////////////////////////////////////////////////////////
856
857// can't draw it if its empty, or its too big for a fixed-point width or height
858static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000859 return bitmap.width() <= 0 || bitmap.height() <= 0
860#ifndef SK_ALLOW_OVER_32K_BITMAPS
861 || bitmap.width() > 32767 || bitmap.height() > 32767
862#endif
863 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000864}
865
reed@android.comf2b98d62010-12-20 18:26:13 +0000866void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 const SkMatrix& matrix, const SkPaint* paint) {
868 if (reject_bitmap(bitmap)) {
869 return;
870 }
871
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000872 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000874 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000876 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877}
878
reed@google.com76dd2772012-01-05 21:15:07 +0000879#include "SkImageFilter.h"
880
881class DeviceImageFilterProxy : public SkImageFilter::Proxy {
882public:
883 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000884
reed@google.com76dd2772012-01-05 21:15:07 +0000885 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE;
886 virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
887 const SkMatrix& ctm,
888 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
889
890private:
891 SkDevice* fDevice;
892};
893
894SkDevice* DeviceImageFilterProxy::createDevice(int w, int h) {
895 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
896 w, h, false);
897}
898
899bool DeviceImageFilterProxy::filterImage(SkImageFilter* filter,
900 const SkBitmap& src,
901 const SkMatrix& ctm,
902 SkBitmap* result,
903 SkIPoint* offset) {
904 return fDevice->filterImage(filter, src, ctm, result, offset);
905}
906
reed@google.comb55deeb2012-01-06 14:43:09 +0000907void SkCanvas::drawDevice(SkDevice* srcDev, int x, int y,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 const SkPaint* paint) {
909 SkPaint tmp;
910 if (NULL == paint) {
911 tmp.setDither(true);
912 paint = &tmp;
913 }
reed@google.com4b226022011-01-11 18:32:13 +0000914
reed@google.com4e2b3d32011-04-07 14:18:59 +0000915 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000917 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000918 paint = &looper.paint();
919 SkImageFilter* filter = paint->getImageFilter();
920 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
921 if (filter) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000922 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000923 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000924 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000925 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
926 SkPaint tmp(*paint);
927 tmp.setImageFilter(NULL);
reed@google.comb55deeb2012-01-06 14:43:09 +0000928 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
reed@google.com76dd2772012-01-05 21:15:07 +0000929 }
930 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000931 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000932 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000934 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935}
936
937/////////////////////////////////////////////////////////////////////////////
938
939bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
940 fDeviceCMDirty = true;
941 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000942 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 return fMCRec->fMatrix->preTranslate(dx, dy);
944}
945
946bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
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->preScale(sx, sy);
951}
952
953bool SkCanvas::rotate(SkScalar degrees) {
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->preRotate(degrees);
958}
959
960bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
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->preSkew(sx, sy);
965}
966
967bool SkCanvas::concat(const SkMatrix& matrix) {
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->preConcat(matrix);
972}
973
974void SkCanvas::setMatrix(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 *fMCRec->fMatrix = matrix;
979}
980
981// this is not virtual, so it must call a virtual method so that subclasses
982// will see its action
983void SkCanvas::resetMatrix() {
984 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +0000985
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 matrix.reset();
987 this->setMatrix(matrix);
988}
989
990//////////////////////////////////////////////////////////////////////////////
991
reed@google.comc42d35d2011-10-12 11:57:42 +0000992bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000993 AutoValidateClip avc(this);
994
reed@android.com8a1c16f2008-12-17 15:59:43 +0000995 fDeviceCMDirty = true;
996 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000997 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998
999 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001000 // for these simpler matrices, we can stay a rect ever after applying
1001 // the matrix. This means we don't have to a) make a path, and b) tell
1002 // the region code to scan-convert the path, only to discover that it
1003 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005
1006 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001007 fClipStack.clipDevRect(r, op, doAA);
1008 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001009 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001010 // since we're rotate or some such thing, we convert the rect to a path
1011 // and clip against that, since it can handle any matrix. However, to
1012 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1013 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001014 SkPath path;
1015
1016 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001017 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018 }
1019}
1020
reed@google.com00177082011-10-12 14:34:30 +00001021static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001022 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001023 // base is used to limit the size (and therefore memory allocation) of the
1024 // region that results from scan converting devPath.
1025 SkRegion base;
1026
reed@google.com819c9212011-02-23 18:56:55 +00001027 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001028 // since we are intersect, we can do better (tighter) with currRgn's
1029 // bounds, than just using the device. However, if currRgn is complex,
1030 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001031 if (currClip->isRect()) {
1032 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001033 } else {
reed@google.com00177082011-10-12 14:34:30 +00001034 base.setRect(currClip->getBounds());
1035 SkRasterClip clip;
1036 clip.setPath(devPath, base, doAA);
1037 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001038 }
reed@google.com819c9212011-02-23 18:56:55 +00001039 } else {
reed@google.com819c9212011-02-23 18:56:55 +00001040 const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
1041 base.setRect(0, 0, bm.width(), bm.height());
1042
1043 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001044 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001045 } else {
reed@google.com00177082011-10-12 14:34:30 +00001046 SkRasterClip clip;
1047 clip.setPath(devPath, base, doAA);
1048 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001049 }
1050 }
1051}
1052
reed@google.comc42d35d2011-10-12 11:57:42 +00001053bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001054 AutoValidateClip avc(this);
1055
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056 fDeviceCMDirty = true;
1057 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001058 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059
1060 SkPath devPath;
1061 path.transform(*fMCRec->fMatrix, &devPath);
1062
reed@google.comfe701122011-11-08 19:41:23 +00001063 // Check if the transfomation, or the original path itself
1064 // made us empty. Note this can also happen if we contained NaN
1065 // values. computing the bounds detects this, and will set our
1066 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1067 if (devPath.getBounds().isEmpty()) {
1068 // resetting the path will remove any NaN or other wanky values
1069 // that might upset our scan converter.
1070 devPath.reset();
1071 }
1072
reed@google.com5c3d1472011-02-22 19:12:23 +00001073 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001074 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001075
reed@google.com00177082011-10-12 14:34:30 +00001076 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077}
1078
1079bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001080 AutoValidateClip avc(this);
1081
reed@android.com8a1c16f2008-12-17 15:59:43 +00001082 fDeviceCMDirty = true;
1083 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001084 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085
reed@google.com5c3d1472011-02-22 19:12:23 +00001086 // todo: signal fClipStack that we have a region, and therefore (I guess)
1087 // we have to ignore it, and use the region directly?
1088 fClipStack.clipDevRect(rgn.getBounds());
1089
reed@google.com00177082011-10-12 14:34:30 +00001090 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091}
1092
reed@google.com819c9212011-02-23 18:56:55 +00001093#ifdef SK_DEBUG
1094void SkCanvas::validateClip() const {
1095 // construct clipRgn from the clipstack
1096 const SkDevice* device = this->getDevice();
1097 SkIRect ir;
1098 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001099 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001100
1101 SkClipStack::B2FIter iter(fClipStack);
1102 const SkClipStack::B2FIter::Clip* clip;
1103 while ((clip = iter.next()) != NULL) {
1104 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001105 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001106 } else if (clip->fRect) {
1107 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001108 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001109 } else {
reed@google.com00177082011-10-12 14:34:30 +00001110 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001111 }
1112 }
1113
reed@google.com6f8f2922011-03-04 22:27:10 +00001114#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001115 // now compare against the current rgn
1116 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001117 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001118#endif
reed@google.com819c9212011-02-23 18:56:55 +00001119}
1120#endif
1121
reed@google.com5c3d1472011-02-22 19:12:23 +00001122///////////////////////////////////////////////////////////////////////////////
1123
reed@android.comba09de42010-02-05 20:46:05 +00001124void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001126 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1127 fLocalBoundsCompareTypeBW;
1128
1129 if (!this->getClipBounds(&r, et)) {
1130 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001131 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001132 rCompare.set(SkScalarToCompareType(r.fLeft),
1133 SkScalarToCompareType(r.fTop),
1134 SkScalarToCompareType(r.fRight),
1135 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001136 }
1137}
1138
reed@android.comd252db02009-04-01 18:31:44 +00001139/* current impl ignores edgetype, and relies on
1140 getLocalClipBoundsCompareType(), which always returns a value assuming
1141 antialiasing (worst case)
1142 */
reed@android.comba09de42010-02-05 20:46:05 +00001143bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001144
reed@google.com16078632011-12-06 18:56:37 +00001145 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001146 return true;
1147
reed@google.com00177082011-10-12 14:34:30 +00001148 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 return true;
1150 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151
tomhudson@google.com8d430182011-06-06 19:11:19 +00001152 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001153 SkRect dst;
1154 fMCRec->fMatrix->mapRect(&dst, rect);
1155 SkIRect idst;
1156 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001157 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001158 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001159 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001160
reed@android.coma380ae42009-07-21 01:17:02 +00001161 // for speed, do the most likely reject compares first
1162 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1163 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1164 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1165 return true;
1166 }
1167 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1168 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1169 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1170 return true;
1171 }
1172 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174}
1175
1176bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001177 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001178}
1179
1180bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
1181 /* current impl ignores edgetype, and relies on
1182 getLocalClipBoundsCompareType(), which always returns a value assuming
1183 antialiasing (worst case)
1184 */
1185
reed@google.com00177082011-10-12 14:34:30 +00001186 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001187 return true;
1188 }
reed@google.com4b226022011-01-11 18:32:13 +00001189
reed@android.comaefd2bc2009-03-30 21:02:14 +00001190 SkScalarCompareType userT = SkScalarToCompareType(top);
1191 SkScalarCompareType userB = SkScalarToCompareType(bottom);
reed@google.com4b226022011-01-11 18:32:13 +00001192
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193 // check for invalid user Y coordinates (i.e. empty)
reed@android.comd252db02009-04-01 18:31:44 +00001194 // reed: why do we need to do this check, since it slows us down?
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 if (userT >= userB) {
1196 return true;
1197 }
reed@google.com4b226022011-01-11 18:32:13 +00001198
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199 // check if we are above or below the local clip bounds
1200 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
1201 return userT >= clipR.fBottom || userB <= clipR.fTop;
1202}
1203
1204bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001205 SkIRect ibounds;
1206 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 return false;
1208 }
1209
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001210 SkMatrix inverse;
1211 // if we can't invert the CTM, we can't return local clip bounds
1212 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001213 if (bounds) {
1214 bounds->setEmpty();
1215 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001216 return false;
1217 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001219 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001220 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001221 // adjust it outwards if we are antialiasing
1222 int inset = (kAA_EdgeType == et);
1223 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1224 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 inverse.mapRect(bounds, r);
1226 }
1227 return true;
1228}
1229
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001230bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001231 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001232 if (clip.isEmpty()) {
1233 if (bounds) {
1234 bounds->setEmpty();
1235 }
1236 return false;
1237 }
1238
1239 if (NULL != bounds) {
1240 *bounds = clip.getBounds();
1241 }
1242 return true;
1243}
1244
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245const SkMatrix& SkCanvas::getTotalMatrix() const {
1246 return *fMCRec->fMatrix;
1247}
1248
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001249SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001250 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1251 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001252 return kComplex_ClipType;
1253}
1254
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001256 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001257}
1258
reed@google.com7d7ca792011-02-23 22:39:18 +00001259const SkClipStack& SkCanvas::getTotalClipStack() const {
1260 return fClipStack;
1261}
1262
reed@android.comf2b98d62010-12-20 18:26:13 +00001263void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1264 if (NULL == matrix || matrix->isIdentity()) {
1265 if (fUseExternalMatrix) {
1266 fDeviceCMDirty = true;
1267 }
1268 fUseExternalMatrix = false;
1269 } else {
1270 fUseExternalMatrix = true;
1271 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.com4b226022011-01-11 18:32:13 +00001272
reed@android.comf2b98d62010-12-20 18:26:13 +00001273 fExternalMatrix = *matrix;
1274 matrix->invert(&fExternalInverse);
1275 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001276}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277
bsalomon@google.come97f0852011-06-17 13:10:25 +00001278SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1279 int width, int height,
1280 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001281 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001282 if (device) {
1283 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1284 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001285 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001286 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001287 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288}
1289
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001290SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001291 int width, int height,
1292 bool isOpaque) {
1293 SkDevice* device = this->getDevice();
1294 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001295 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001296 } else {
1297 return NULL;
1298 }
1299}
1300
1301
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302//////////////////////////////////////////////////////////////////////////////
1303// These are the virtual drawing methods
1304//////////////////////////////////////////////////////////////////////////////
1305
reed@google.com2a981812011-04-14 18:59:28 +00001306void SkCanvas::clear(SkColor color) {
1307 SkDrawIter iter(this);
1308
1309 while (iter.next()) {
1310 iter.fDevice->clear(color);
1311 }
1312}
1313
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001315 this->internalDrawPaint(paint);
1316}
1317
1318void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001319 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320
1321 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001322 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 }
1324
reed@google.com4e2b3d32011-04-07 14:18:59 +00001325 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326}
1327
1328void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1329 const SkPaint& paint) {
1330 if ((long)count <= 0) {
1331 return;
1332 }
1333
1334 SkASSERT(pts != NULL);
1335
reed@google.com4e2b3d32011-04-07 14:18:59 +00001336 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001337
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001339 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340 }
reed@google.com4b226022011-01-11 18:32:13 +00001341
reed@google.com4e2b3d32011-04-07 14:18:59 +00001342 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343}
1344
1345void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1346 if (paint.canComputeFastBounds()) {
1347 SkRect storage;
1348 if (this->quickReject(paint.computeFastBounds(r, &storage),
1349 paint2EdgeType(&paint))) {
1350 return;
1351 }
1352 }
reed@google.com4b226022011-01-11 18:32:13 +00001353
reed@google.com4e2b3d32011-04-07 14:18:59 +00001354 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355
1356 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001357 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358 }
1359
reed@google.com4e2b3d32011-04-07 14:18:59 +00001360 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361}
1362
1363void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001364 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001365 SkRect storage;
1366 const SkRect& bounds = path.getBounds();
1367 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 paint2EdgeType(&paint))) {
1369 return;
1370 }
1371 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001372 if (path.isEmpty()) {
1373 if (path.isInverseFillType()) {
1374 this->internalDrawPaint(paint);
1375 }
1376 return;
1377 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378
reed@google.com4e2b3d32011-04-07 14:18:59 +00001379 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001380
1381 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001382 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383 }
1384
reed@google.com4e2b3d32011-04-07 14:18:59 +00001385 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386}
1387
1388void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1389 const SkPaint* paint) {
1390 SkDEBUGCODE(bitmap.validate();)
1391
reed@google.com3d608122011-11-21 15:16:16 +00001392 if (NULL == paint || paint->canComputeFastBounds()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001393 SkRect fastBounds;
1394 fastBounds.set(x, y,
1395 x + SkIntToScalar(bitmap.width()),
1396 y + SkIntToScalar(bitmap.height()));
1397 if (this->quickReject(fastBounds, paint2EdgeType(paint))) {
1398 return;
1399 }
1400 }
reed@google.com4b226022011-01-11 18:32:13 +00001401
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402 SkMatrix matrix;
1403 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001404 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405}
1406
reed@google.com9987ec32011-09-07 11:57:52 +00001407// this one is non-virtual, so it can be called safely by other canvas apis
1408void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1409 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1411 return;
1412 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001413
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001415 if (NULL == paint || paint->canComputeFastBounds()) {
1416 if (this->quickReject(dst, paint2EdgeType(paint))) {
1417 return;
1418 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 }
reed@google.com3d608122011-11-21 15:16:16 +00001420
reed@android.com8a1c16f2008-12-17 15:59:43 +00001421 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001422
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001424 SkRect tmpSrc;
1425 if (src) {
1426 tmpSrc.set(*src);
1427 // if the extract process clipped off the top or left of the
1428 // original, we adjust for that here to get the position right.
1429 if (tmpSrc.fLeft > 0) {
1430 tmpSrc.fRight -= tmpSrc.fLeft;
1431 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001432 }
reed@android.com87899992009-10-16 14:48:38 +00001433 if (tmpSrc.fTop > 0) {
1434 tmpSrc.fBottom -= tmpSrc.fTop;
1435 tmpSrc.fTop = 0;
1436 }
1437 } else {
1438 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1439 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 }
reed@android.com87899992009-10-16 14:48:38 +00001441 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001442
reed@android.comf2b98d62010-12-20 18:26:13 +00001443 // ensure that src is "valid" before we pass it to our internal routines
1444 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1445 SkIRect tmpISrc;
1446 if (src) {
1447 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001448 if (!tmpISrc.intersect(*src)) {
1449 return;
1450 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001451 src = &tmpISrc;
1452 }
1453 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454}
1455
reed@google.com9987ec32011-09-07 11:57:52 +00001456void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1457 const SkRect& dst, const SkPaint* paint) {
1458 SkDEBUGCODE(bitmap.validate();)
1459 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1460}
1461
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1463 const SkPaint* paint) {
1464 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001465 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466}
1467
reed@android.comf2b98d62010-12-20 18:26:13 +00001468void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1469 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001470 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001471
reed@google.com4e2b3d32011-04-07 14:18:59 +00001472 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001473
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001475 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 }
reed@android.com9b039062009-02-11 15:09:58 +00001477
reed@google.com4e2b3d32011-04-07 14:18:59 +00001478 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479}
1480
reed@google.com9987ec32011-09-07 11:57:52 +00001481void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1482 const SkIRect& center, const SkRect& dst,
1483 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001484 if (NULL == paint || paint->canComputeFastBounds()) {
1485 if (this->quickReject(dst, paint2EdgeType(paint))) {
1486 return;
1487 }
1488 }
1489
reed@google.com9987ec32011-09-07 11:57:52 +00001490 const int32_t w = bitmap.width();
1491 const int32_t h = bitmap.height();
1492
1493 SkIRect c = center;
1494 // pin center to the bounds of the bitmap
1495 c.fLeft = SkMax32(0, center.fLeft);
1496 c.fTop = SkMax32(0, center.fTop);
1497 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1498 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1499
1500 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1501 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1502 SkScalar dstX[4] = {
1503 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1504 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1505 };
1506 SkScalar dstY[4] = {
1507 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1508 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1509 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001510
reed@google.com9987ec32011-09-07 11:57:52 +00001511 if (dstX[1] > dstX[2]) {
1512 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1513 dstX[2] = dstX[1];
1514 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001515
reed@google.com9987ec32011-09-07 11:57:52 +00001516 if (dstY[1] > dstY[2]) {
1517 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1518 dstY[2] = dstY[1];
1519 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001520
reed@google.com9987ec32011-09-07 11:57:52 +00001521 SkIRect s;
1522 SkRect d;
1523 for (int y = 0; y < 3; y++) {
1524 s.fTop = srcY[y];
1525 s.fBottom = srcY[y+1];
1526 d.fTop = dstY[y];
1527 d.fBottom = dstY[y+1];
1528 for (int x = 0; x < 3; x++) {
1529 s.fLeft = srcX[x];
1530 s.fRight = srcX[x+1];
1531 d.fLeft = dstX[x];
1532 d.fRight = dstX[x+1];
1533 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1534 }
1535 }
1536}
1537
1538void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1539 const SkRect& dst, const SkPaint* paint) {
1540 SkDEBUGCODE(bitmap.validate();)
1541
1542 // Need a device entry-point, so gpu can use a mesh
1543 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1544}
1545
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1547 const SkPaint* paint) {
1548 SkDEBUGCODE(bitmap.validate();)
reed@google.com4b226022011-01-11 18:32:13 +00001549
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550 if (reject_bitmap(bitmap)) {
1551 return;
1552 }
reed@google.com4b226022011-01-11 18:32:13 +00001553
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 SkPaint tmp;
1555 if (NULL == paint) {
1556 paint = &tmp;
1557 }
reed@google.com4b226022011-01-11 18:32:13 +00001558
reed@google.com4e2b3d32011-04-07 14:18:59 +00001559 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001560
reed@android.com8a1c16f2008-12-17 15:59:43 +00001561 while (iter.next()) {
1562 iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
reed@google.com4e2b3d32011-04-07 14:18:59 +00001563 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001564 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001565 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001566}
1567
reed@google.comf67e4cf2011-03-15 20:56:58 +00001568class SkDeviceFilteredPaint {
1569public:
1570 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1571 SkDevice::TextFlags flags;
1572 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001573 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001574 newPaint->setFlags(flags.fFlags);
1575 newPaint->setHinting(flags.fHinting);
1576 fPaint = newPaint;
1577 } else {
1578 fPaint = &paint;
1579 }
1580 }
1581
reed@google.comf67e4cf2011-03-15 20:56:58 +00001582 const SkPaint& paint() const { return *fPaint; }
1583
1584private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001585 const SkPaint* fPaint;
1586 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001587};
1588
bungeman@google.com52c748b2011-08-22 21:30:43 +00001589void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1590 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001591 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001592 draw.fDevice->drawRect(draw, r, paint);
1593 } else {
1594 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001595 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001596 draw.fDevice->drawRect(draw, r, p);
1597 }
1598}
1599
1600void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1601 const char text[], size_t byteLength,
1602 SkScalar x, SkScalar y) {
1603 SkASSERT(byteLength == 0 || text != NULL);
1604
1605 // nothing to draw
1606 if (text == NULL || byteLength == 0 ||
1607 draw.fClip->isEmpty() ||
1608 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1609 return;
1610 }
1611
1612 SkScalar width = 0;
1613 SkPoint start;
1614
1615 start.set(0, 0); // to avoid warning
1616 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1617 SkPaint::kStrikeThruText_Flag)) {
1618 width = paint.measureText(text, byteLength);
1619
1620 SkScalar offsetX = 0;
1621 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1622 offsetX = SkScalarHalf(width);
1623 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1624 offsetX = width;
1625 }
1626 start.set(x - offsetX, y);
1627 }
1628
1629 if (0 == width) {
1630 return;
1631 }
1632
1633 uint32_t flags = paint.getFlags();
1634
1635 if (flags & (SkPaint::kUnderlineText_Flag |
1636 SkPaint::kStrikeThruText_Flag)) {
1637 SkScalar textSize = paint.getTextSize();
1638 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1639 SkRect r;
1640
1641 r.fLeft = start.fX;
1642 r.fRight = start.fX + width;
1643
1644 if (flags & SkPaint::kUnderlineText_Flag) {
1645 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1646 start.fY);
1647 r.fTop = offset;
1648 r.fBottom = offset + height;
1649 DrawRect(draw, paint, r, textSize);
1650 }
1651 if (flags & SkPaint::kStrikeThruText_Flag) {
1652 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1653 start.fY);
1654 r.fTop = offset;
1655 r.fBottom = offset + height;
1656 DrawRect(draw, paint, r, textSize);
1657 }
1658 }
1659}
1660
reed@android.com8a1c16f2008-12-17 15:59:43 +00001661void SkCanvas::drawText(const void* text, size_t byteLength,
1662 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001663 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001664
1665 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001666 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001667 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001668 DrawTextDecorations(iter, dfp.paint(),
1669 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001670 }
1671
reed@google.com4e2b3d32011-04-07 14:18:59 +00001672 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673}
1674
1675void SkCanvas::drawPosText(const void* text, size_t byteLength,
1676 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001677 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001678
reed@android.com8a1c16f2008-12-17 15:59:43 +00001679 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001680 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001682 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683 }
reed@google.com4b226022011-01-11 18:32:13 +00001684
reed@google.com4e2b3d32011-04-07 14:18:59 +00001685 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686}
1687
1688void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1689 const SkScalar xpos[], SkScalar constY,
1690 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001691 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001692
reed@android.com8a1c16f2008-12-17 15:59:43 +00001693 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001694 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001696 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697 }
reed@google.com4b226022011-01-11 18:32:13 +00001698
reed@google.com4e2b3d32011-04-07 14:18:59 +00001699 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001700}
1701
1702void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1703 const SkPath& path, const SkMatrix* matrix,
1704 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001705 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706
1707 while (iter.next()) {
1708 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001709 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710 }
1711
reed@google.com4e2b3d32011-04-07 14:18:59 +00001712 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713}
1714
djsollen@google.com56c69772011-11-08 19:00:26 +00001715#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001716void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1717 const SkPoint pos[], const SkPaint& paint,
1718 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001719 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001720
1721 while (iter.next()) {
1722 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001723 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001724 }
1725
reed@google.com4e2b3d32011-04-07 14:18:59 +00001726 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001727}
1728#endif
1729
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1731 const SkPoint verts[], const SkPoint texs[],
1732 const SkColor colors[], SkXfermode* xmode,
1733 const uint16_t indices[], int indexCount,
1734 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001735 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001736
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737 while (iter.next()) {
1738 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001739 colors, xmode, indices, indexCount,
1740 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741 }
reed@google.com4b226022011-01-11 18:32:13 +00001742
reed@google.com4e2b3d32011-04-07 14:18:59 +00001743 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744}
1745
reed@android.comcb608442009-12-04 21:32:27 +00001746void SkCanvas::drawData(const void* data, size_t length) {
1747 // do nothing. Subclasses may do something with the data
1748}
1749
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750//////////////////////////////////////////////////////////////////////////////
1751// These methods are NOT virtual, and therefore must call back into virtual
1752// methods, rather than actually drawing themselves.
1753//////////////////////////////////////////////////////////////////////////////
1754
1755void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001756 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757 SkPaint paint;
1758
1759 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001760 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001761 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 }
1763 this->drawPaint(paint);
1764}
1765
reed@android.com845fdac2009-06-23 03:01:32 +00001766void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767 SkPaint paint;
1768
1769 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001770 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001771 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 }
1773 this->drawPaint(paint);
1774}
1775
1776void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1777 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001778
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779 pt.set(x, y);
1780 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1781}
1782
1783void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1784 SkPoint pt;
1785 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001786
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787 pt.set(x, y);
1788 paint.setColor(color);
1789 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1790}
1791
1792void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1793 const SkPaint& paint) {
1794 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001795
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796 pts[0].set(x0, y0);
1797 pts[1].set(x1, y1);
1798 this->drawPoints(kLines_PointMode, 2, pts, paint);
1799}
1800
1801void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1802 SkScalar right, SkScalar bottom,
1803 const SkPaint& paint) {
1804 SkRect r;
1805
1806 r.set(left, top, right, bottom);
1807 this->drawRect(r, paint);
1808}
1809
1810void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1811 const SkPaint& paint) {
1812 if (radius < 0) {
1813 radius = 0;
1814 }
1815
1816 SkRect r;
1817 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001818
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819 if (paint.canComputeFastBounds()) {
1820 SkRect storage;
1821 if (this->quickReject(paint.computeFastBounds(r, &storage),
1822 paint2EdgeType(&paint))) {
1823 return;
1824 }
1825 }
reed@google.com4b226022011-01-11 18:32:13 +00001826
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 SkPath path;
1828 path.addOval(r);
1829 this->drawPath(path, paint);
1830}
1831
1832void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1833 const SkPaint& paint) {
1834 if (rx > 0 && ry > 0) {
1835 if (paint.canComputeFastBounds()) {
1836 SkRect storage;
1837 if (this->quickReject(paint.computeFastBounds(r, &storage),
1838 paint2EdgeType(&paint))) {
1839 return;
1840 }
1841 }
1842
1843 SkPath path;
1844 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1845 this->drawPath(path, paint);
1846 } else {
1847 this->drawRect(r, paint);
1848 }
1849}
1850
1851void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1852 if (paint.canComputeFastBounds()) {
1853 SkRect storage;
1854 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1855 paint2EdgeType(&paint))) {
1856 return;
1857 }
1858 }
1859
1860 SkPath path;
1861 path.addOval(oval);
1862 this->drawPath(path, paint);
1863}
1864
1865void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1866 SkScalar sweepAngle, bool useCenter,
1867 const SkPaint& paint) {
1868 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1869 this->drawOval(oval, paint);
1870 } else {
1871 SkPath path;
1872 if (useCenter) {
1873 path.moveTo(oval.centerX(), oval.centerY());
1874 }
1875 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1876 if (useCenter) {
1877 path.close();
1878 }
1879 this->drawPath(path, paint);
1880 }
1881}
1882
1883void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1884 const SkPath& path, SkScalar hOffset,
1885 SkScalar vOffset, const SkPaint& paint) {
1886 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001887
reed@android.com8a1c16f2008-12-17 15:59:43 +00001888 matrix.setTranslate(hOffset, vOffset);
1889 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1890}
1891
reed@android.comf76bacf2009-05-13 14:00:33 +00001892///////////////////////////////////////////////////////////////////////////////
1893
reed@android.com8a1c16f2008-12-17 15:59:43 +00001894void SkCanvas::drawPicture(SkPicture& picture) {
1895 int saveCount = save();
1896 picture.draw(this);
1897 restoreToCount(saveCount);
1898}
1899
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900///////////////////////////////////////////////////////////////////////////////
1901///////////////////////////////////////////////////////////////////////////////
1902
1903SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001904 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905
1906 SkASSERT(canvas);
1907
1908 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1909 fDone = !fImpl->next();
1910}
1911
1912SkCanvas::LayerIter::~LayerIter() {
1913 fImpl->~SkDrawIter();
1914}
1915
1916void SkCanvas::LayerIter::next() {
1917 fDone = !fImpl->next();
1918}
1919
1920SkDevice* SkCanvas::LayerIter::device() const {
1921 return fImpl->getDevice();
1922}
1923
1924const SkMatrix& SkCanvas::LayerIter::matrix() const {
1925 return fImpl->getMatrix();
1926}
1927
1928const SkPaint& SkCanvas::LayerIter::paint() const {
1929 const SkPaint* paint = fImpl->getPaint();
1930 if (NULL == paint) {
1931 paint = &fDefaultPaint;
1932 }
1933 return *paint;
1934}
1935
1936const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
1937int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
1938int SkCanvas::LayerIter::y() const { return fImpl->getY(); }