blob: b0f85d968b72f02bc177beb0d32c0c2e2047d5dd [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.com90c07ea2012-04-13 13:50:27 +0000221 fClipStack = &canvas->fClipStack;
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
reed@google.comf68c5e22012-02-24 16:38:58 +0000235 const DeviceCM* rec = fCurrLayer;
236 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
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.com8926b162012-03-23 15:36:36 +0000280 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
281 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000282 fCanvas = canvas;
283 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000285 fPaint = NULL;
286 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000287 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289
reed@google.com8926b162012-03-23 15:36:36 +0000290 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
291 SkPaint tmp;
292 tmp.setImageFilter(fOrigPaint.getImageFilter());
293 // it would be nice if we had a guess at the bounds, instead of null
294 (void)canvas->internalSaveLayer(NULL, &tmp,
295 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
296 // we'll clear the imageFilter for the actual draws in next(), so
297 // it will only be applied during the restore().
298 fDoClearImageFilter = true;
299 }
300
reed@google.com4e2b3d32011-04-07 14:18:59 +0000301 if (fLooper) {
302 fLooper->init(canvas);
303 }
304 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000305
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000307 if (fDoClearImageFilter) {
308 fCanvas->internalRestore();
309 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000310 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000312
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313 const SkPaint& paint() const {
314 SkASSERT(fPaint);
315 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000317
reed@google.com4e2b3d32011-04-07 14:18:59 +0000318 bool next(SkDrawFilter::Type drawType);
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000319
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000321 SkLazyPaint fLazyPaint;
322 SkCanvas* fCanvas;
323 const SkPaint& fOrigPaint;
324 SkDrawLooper* fLooper;
325 SkDrawFilter* fFilter;
326 const SkPaint* fPaint;
327 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000328 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000329 bool fDone;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330};
331
reed@google.com4e2b3d32011-04-07 14:18:59 +0000332bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000333 fPaint = NULL;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000334 if (fDone) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335 return false;
336 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000337
reed@google.com8926b162012-03-23 15:36:36 +0000338 if (fLooper || fFilter || fDoClearImageFilter) {
reed@google.com632e1a22011-10-06 12:37:00 +0000339 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed@google.com8926b162012-03-23 15:36:36 +0000340
341 if (fDoClearImageFilter) {
342 paint->setImageFilter(NULL);
343 }
344
reed@google.com632e1a22011-10-06 12:37:00 +0000345 if (fLooper && !fLooper->next(fCanvas, paint)) {
346 fDone = true;
347 return false;
348 }
349 if (fFilter) {
350 fFilter->filter(paint, drawType);
351 if (NULL == fLooper) {
352 // no looper means we only draw once
353 fDone = true;
354 }
355 }
356 fPaint = paint;
reed@google.com8926b162012-03-23 15:36:36 +0000357
358 // if we only came in here for the imagefilter, mark us as done
359 if (!fLooper && !fFilter) {
360 fDone = true;
361 }
reed@google.com632e1a22011-10-06 12:37:00 +0000362 } else {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000363 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000364 fPaint = &fOrigPaint;
365 }
366
367 // call this after any possible paint modifiers
368 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000369 fPaint = NULL;
370 return false;
371 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000372 return true;
373}
374
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375/* Stack helper for managing a SkBounder. In the destructor, if we were
376 given a bounder, we call its commit() method, signifying that we are
377 done accumulating bounds for that draw.
378*/
379class SkAutoBounderCommit {
380public:
381 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
382 ~SkAutoBounderCommit() {
383 if (NULL != fBounder) {
384 fBounder->commit();
385 }
386 }
387private:
388 SkBounder* fBounder;
389};
390
391#include "SkColorPriv.h"
392
393class AutoValidator {
394public:
395 AutoValidator(SkDevice* device) : fDevice(device) {}
396 ~AutoValidator() {
397#ifdef SK_DEBUG
398 const SkBitmap& bm = fDevice->accessBitmap(false);
399 if (bm.config() == SkBitmap::kARGB_4444_Config) {
400 for (int y = 0; y < bm.height(); y++) {
401 const SkPMColor16* p = bm.getAddr16(0, y);
402 for (int x = 0; x < bm.width(); x++) {
403 SkPMColor16 c = p[x];
404 SkPMColor16Assert(c);
405 }
406 }
407 }
408#endif
409 }
410private:
411 SkDevice* fDevice;
412};
413
414////////// macros to place around the internal draw calls //////////////////
415
reed@google.com8926b162012-03-23 15:36:36 +0000416#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
417/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
418 AutoDrawLooper looper(this, paint, true); \
419 while (looper.next(type)) { \
420 SkAutoBounderCommit ac(fBounder); \
421 SkDrawIter iter(this);
422
reed@google.com4e2b3d32011-04-07 14:18:59 +0000423#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000425 AutoDrawLooper looper(this, paint); \
426 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000427 SkAutoBounderCommit ac(fBounder); \
428 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000429
reed@google.com4e2b3d32011-04-07 14:18:59 +0000430#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431
432////////////////////////////////////////////////////////////////////////////
433
434SkDevice* SkCanvas::init(SkDevice* device) {
435 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000436 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000438 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000439 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000440 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000441 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000442 fSaveLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443
444 fMCRec = (MCRec*)fMCStack.push_back();
445 new (fMCRec) MCRec(NULL, 0);
446
447 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
448 fMCRec->fTopLayer = fMCRec->fLayer;
449 fMCRec->fNext = NULL;
450
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000451 fExternalMatrix.reset();
452 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000453 fUseExternalMatrix = false;
454
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 return this->setDevice(device);
456}
457
reed@google.comcde92112011-07-06 20:00:52 +0000458SkCanvas::SkCanvas()
459: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000460 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000461
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000462 this->init(NULL);
463}
464
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000466 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 inc_canvas();
468
469 this->init(device);
470}
471
472SkCanvas::SkCanvas(const SkBitmap& bitmap)
473 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
474 inc_canvas();
475
reed@google.comcde92112011-07-06 20:00:52 +0000476 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477}
478
479SkCanvas::~SkCanvas() {
480 // free up the contents of our deque
481 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000482 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 this->internalRestore(); // restore the last, since we're going away
485
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000486 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000487
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 dec_canvas();
489}
490
491SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
492 SkRefCnt_SafeAssign(fBounder, bounder);
493 return bounder;
494}
495
496SkDrawFilter* SkCanvas::getDrawFilter() const {
497 return fMCRec->fFilter;
498}
499
500SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
501 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
502 return filter;
503}
504
505///////////////////////////////////////////////////////////////////////////////
506
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000507void SkCanvas::flush() {
508 SkDevice* device = this->getDevice();
509 if (device) {
510 device->flush();
511 }
512}
513
reed@google.com210ce002011-11-01 14:24:23 +0000514SkISize SkCanvas::getDeviceSize() const {
515 SkDevice* d = this->getDevice();
516 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
517}
518
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519SkDevice* SkCanvas::getDevice() const {
520 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000521 SkDeque::F2BIter iter(fMCStack);
522 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 SkASSERT(rec && rec->fLayer);
524 return rec->fLayer->fDevice;
525}
526
reed@google.com0b53d592012-03-19 18:26:34 +0000527SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
528 if (updateMatrixClip) {
529 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
530 }
reed@google.com9266fed2011-03-30 00:18:03 +0000531 return fMCRec->fTopLayer->fDevice;
532}
533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534SkDevice* SkCanvas::setDevice(SkDevice* device) {
535 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000536 SkDeque::F2BIter iter(fMCStack);
537 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 SkASSERT(rec && rec->fLayer);
539 SkDevice* rootDevice = rec->fLayer->fDevice;
540
541 if (rootDevice == device) {
542 return device;
543 }
reed@google.com4b226022011-01-11 18:32:13 +0000544
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 /* Notify the devices that they are going in/out of scope, so they can do
546 things like lock/unlock their pixels, etc.
547 */
548 if (device) {
549 device->lockPixels();
550 }
551 if (rootDevice) {
552 rootDevice->unlockPixels();
553 }
554
555 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
556 rootDevice = device;
557
558 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 /* Now we update our initial region to have the bounds of the new device,
561 and then intersect all of the clips in our stack with these bounds,
562 to ensure that we can't draw outside of the device's bounds (and trash
563 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000564
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 NOTE: this is only a partial-fix, since if the new device is larger than
566 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000567 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
569 reconstruct the correct clips, so this approximation will have to do.
570 The caller really needs to restore() back to the base if they want to
571 accurately take advantage of the new device bounds.
572 */
573
reed@google.com42aea282012-03-28 16:19:15 +0000574 SkIRect bounds;
575 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000577 } else {
578 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 }
reed@google.com42aea282012-03-28 16:19:15 +0000580 // now jam our 1st clip to be bounds, and intersect the rest with that
581 rec->fRasterClip->setRect(bounds);
582 while ((rec = (MCRec*)iter.next()) != NULL) {
583 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
584 }
585
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 return device;
587}
588
reed@google.comaf951c92011-06-16 19:10:39 +0000589SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
590 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 device->unref();
592 return device;
593}
594
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000595bool SkCanvas::readPixels(SkBitmap* bitmap,
596 int x, int y,
597 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000598 SkDevice* device = this->getDevice();
599 if (!device) {
600 return false;
601 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000602 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000603}
604
bsalomon@google.comc6980972011-11-02 19:57:21 +0000605bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000606 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000607 if (!device) {
608 return false;
609 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000610
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000611 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000612 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000613 if (!bounds.intersect(srcRect)) {
614 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000615 }
616
617 SkBitmap tmp;
618 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
619 bounds.height());
620 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
621 bitmap->swap(tmp);
622 return true;
623 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000624 return false;
625 }
reed@google.com51df9e32010-12-23 19:29:18 +0000626}
627
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000628void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
629 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000630 SkDevice* device = this->getDevice();
631 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000632 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000633 }
634}
635
junov@google.com4370aed2012-01-18 16:21:08 +0000636SkCanvas* SkCanvas::canvasForDrawIter() {
637 return this;
638}
639
reed@android.com8a1c16f2008-12-17 15:59:43 +0000640//////////////////////////////////////////////////////////////////////////////
641
reed@android.com8a1c16f2008-12-17 15:59:43 +0000642void SkCanvas::updateDeviceCMCache() {
643 if (fDeviceCMDirty) {
644 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000645 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000646 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000647
reed@android.com8a1c16f2008-12-17 15:59:43 +0000648 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000649 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000650 if (fUseExternalMatrix) {
651 layer->updateExternalMatrix(fExternalMatrix,
652 fExternalInverse);
653 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000655 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000657 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000658 if (fUseExternalMatrix) {
659 layer->updateExternalMatrix(fExternalMatrix,
660 fExternalInverse);
661 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 } while ((layer = layer->fNext) != NULL);
663 }
664 fDeviceCMDirty = false;
665 }
666}
667
reed@android.comf2b98d62010-12-20 18:26:13 +0000668void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
bsalomon@google.comd302f142011-03-03 13:54:13 +0000669 const SkRegion& clip,
670 const SkClipStack& clipStack) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000672 if (fLastDeviceToGainFocus != device) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000673 device->gainFocus(this, matrix, clip, clipStack);
reed@android.com199f1082009-06-10 02:12:47 +0000674 fLastDeviceToGainFocus = device;
675 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676}
677
678///////////////////////////////////////////////////////////////////////////////
679
680int SkCanvas::internalSave(SaveFlags flags) {
681 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000682
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 MCRec* newTop = (MCRec*)fMCStack.push_back();
684 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000685
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686 newTop->fNext = fMCRec;
687 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000688
reed@google.com5c3d1472011-02-22 19:12:23 +0000689 fClipStack.save();
690 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
691
reed@android.com8a1c16f2008-12-17 15:59:43 +0000692 return saveCount;
693}
694
695int SkCanvas::save(SaveFlags flags) {
696 // call shared impl
697 return this->internalSave(flags);
698}
699
700#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
701#define C16MASK (1 << SkBitmap::kRGB_565_Config)
702#define C8MASK (1 << SkBitmap::kA8_Config)
703
704static SkBitmap::Config resolve_config(SkCanvas* canvas,
705 const SkIRect& bounds,
706 SkCanvas::SaveFlags flags,
707 bool* isOpaque) {
708 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
709
710#if 0
711 // loop through and union all the configs we may draw into
712 uint32_t configMask = 0;
713 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
714 {
715 SkDevice* device = canvas->getLayerDevice(i);
716 if (device->intersects(bounds))
717 configMask |= 1 << device->config();
718 }
719
720 // if the caller wants alpha or fullcolor, we can't return 565
721 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
722 SkCanvas::kHasAlphaLayer_SaveFlag))
723 configMask &= ~C16MASK;
724
725 switch (configMask) {
726 case C8MASK: // if we only have A8, return that
727 return SkBitmap::kA8_Config;
728
729 case C16MASK: // if we only have 565, return that
730 return SkBitmap::kRGB_565_Config;
731
732 default:
733 return SkBitmap::kARGB_8888_Config; // default answer
734 }
735#else
736 return SkBitmap::kARGB_8888_Config; // default answer
737#endif
738}
739
740static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
741 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
742}
743
junov@chromium.orga907ac32012-02-24 21:54:07 +0000744bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
745 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000746 SkIRect clipBounds;
747 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000748 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000749 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000750 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751 if (NULL != bounds) {
752 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000753
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 this->getTotalMatrix().mapRect(&r, *bounds);
755 r.roundOut(&ir);
756 // early exit if the layer's bounds are clipped out
757 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000758 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000759 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000760 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000761 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000762 }
763 } else { // no user bounds, so just use the clip
764 ir = clipBounds;
765 }
766
reed@google.com5c3d1472011-02-22 19:12:23 +0000767 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000768
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 // early exit if the clip is now empty
770 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000771 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000772 return false;
773 }
774
775 if (intersection) {
776 *intersection = ir;
777 }
778 return true;
779}
780
781int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
782 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000783 return this->internalSaveLayer(bounds, paint, flags, false);
784}
785
786int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
787 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000788 // do this before we create the layer. We don't call the public save() since
789 // that would invoke a possibly overridden virtual
790 int count = this->internalSave(flags);
791
792 fDeviceCMDirty = true;
793
794 SkIRect ir;
795 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 return count;
797 }
798
reed@google.comb55deeb2012-01-06 14:43:09 +0000799 // Kill the imagefilter if our device doesn't allow it
800 SkLazyPaint lazyP;
801 if (paint && paint->getImageFilter()) {
802 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000803 if (justForImageFilter) {
804 // early exit if the layer was just for the imageFilter
805 return count;
806 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000807 SkPaint* p = lazyP.set(*paint);
808 p->setImageFilter(NULL);
809 paint = p;
810 }
811 }
812
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 bool isOpaque;
814 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
815
reed@google.com76dd2772012-01-05 21:15:07 +0000816 SkDevice* device;
817 if (paint && paint->getImageFilter()) {
818 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
819 isOpaque);
820 } else {
821 device = this->createLayerDevice(config, ir.width(), ir.height(),
822 isOpaque);
823 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000824 if (NULL == device) {
825 SkDebugf("Unable to create device for layer.");
826 return count;
827 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000828
reed@google.com6f8f2922011-03-04 22:27:10 +0000829 device->setOrigin(ir.fLeft, ir.fTop);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
831 device->unref();
832
833 layer->fNext = fMCRec->fTopLayer;
834 fMCRec->fLayer = layer;
835 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
836
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000837 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000838 return count;
839}
840
841int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
842 SaveFlags flags) {
843 if (0xFF == alpha) {
844 return this->saveLayer(bounds, NULL, flags);
845 } else {
846 SkPaint tmpPaint;
847 tmpPaint.setAlpha(alpha);
848 return this->saveLayer(bounds, &tmpPaint, flags);
849 }
850}
851
852void SkCanvas::restore() {
853 // check for underflow
854 if (fMCStack.count() > 1) {
855 this->internalRestore();
856 }
857}
858
859void SkCanvas::internalRestore() {
860 SkASSERT(fMCStack.count() != 0);
861
862 fDeviceCMDirty = true;
863 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000864 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000865
reed@google.com5c3d1472011-02-22 19:12:23 +0000866 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000867 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 DeviceCM* layer = fMCRec->fLayer; // may be null
869 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
870 fMCRec->fLayer = NULL;
871
872 // now do the normal restore()
873 fMCRec->~MCRec(); // balanced in save()
874 fMCStack.pop_back();
875 fMCRec = (MCRec*)fMCStack.back();
876
877 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
878 since if we're being recorded, we don't want to record this (the
879 recorder will have already recorded the restore).
880 */
881 if (NULL != layer) {
882 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000883 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000884 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
885 layer->fPaint);
886 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000888
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000889 SkASSERT(fSaveLayerCount > 0);
890 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000891 }
892 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000893 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000894
895 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896}
897
898int SkCanvas::getSaveCount() const {
899 return fMCStack.count();
900}
901
902void SkCanvas::restoreToCount(int count) {
903 // sanity check
904 if (count < 1) {
905 count = 1;
906 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000907
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000908 int n = this->getSaveCount() - count;
909 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 this->restore();
911 }
912}
913
reed@google.com7c202932011-12-14 18:48:05 +0000914bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000915 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000916}
917
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918/////////////////////////////////////////////////////////////////////////////
919
920// can't draw it if its empty, or its too big for a fixed-point width or height
921static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000922 return bitmap.width() <= 0 || bitmap.height() <= 0
923#ifndef SK_ALLOW_OVER_32K_BITMAPS
924 || bitmap.width() > 32767 || bitmap.height() > 32767
925#endif
926 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927}
928
reed@android.comf2b98d62010-12-20 18:26:13 +0000929void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 const SkMatrix& matrix, const SkPaint* paint) {
931 if (reject_bitmap(bitmap)) {
932 return;
933 }
934
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000935 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000937 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000938 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000939 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940}
941
reed@google.com76dd2772012-01-05 21:15:07 +0000942#include "SkImageFilter.h"
943
944class DeviceImageFilterProxy : public SkImageFilter::Proxy {
945public:
946 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000947
reed@google.com8926b162012-03-23 15:36:36 +0000948 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
949 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
950 w, h, false);
951 }
952 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
953 return fDevice->canHandleImageFilter(filter);
954 }
955 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000956 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000957 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
958 return fDevice->filterImage(filter, src, ctm, result, offset);
959 }
960
reed@google.com76dd2772012-01-05 21:15:07 +0000961private:
962 SkDevice* fDevice;
963};
964
reed@google.com8926b162012-03-23 15:36:36 +0000965void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
966 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 SkPaint tmp;
968 if (NULL == paint) {
969 tmp.setDither(true);
970 paint = &tmp;
971 }
reed@google.com4b226022011-01-11 18:32:13 +0000972
reed@google.com8926b162012-03-23 15:36:36 +0000973 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000975 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000976 paint = &looper.paint();
977 SkImageFilter* filter = paint->getImageFilter();
978 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000979 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000980 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000981 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000982 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000983 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000984 SkPaint tmpUnfiltered(*paint);
985 tmpUnfiltered.setImageFilter(NULL);
986 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +0000987 }
988 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000989 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000990 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000992 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993}
994
reed@google.com8926b162012-03-23 15:36:36 +0000995void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
996 const SkPaint* paint) {
997 SkDEBUGCODE(bitmap.validate();)
998
999 if (reject_bitmap(bitmap)) {
1000 return;
1001 }
1002
1003 SkPaint tmp;
1004 if (NULL == paint) {
1005 paint = &tmp;
1006 }
1007
1008 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
1009
1010 while (iter.next()) {
1011 paint = &looper.paint();
1012 SkImageFilter* filter = paint->getImageFilter();
1013 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1014 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1015 DeviceImageFilterProxy proxy(iter.fDevice);
1016 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001017 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1018 &dst, &pos)) {
1019 SkPaint tmpUnfiltered(*paint);
1020 tmpUnfiltered.setImageFilter(NULL);
1021 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1022 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001023 }
1024 } else {
1025 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1026 }
1027 }
1028 LOOPER_END
1029}
1030
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031/////////////////////////////////////////////////////////////////////////////
1032
1033bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1034 fDeviceCMDirty = true;
1035 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001036 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037 return fMCRec->fMatrix->preTranslate(dx, dy);
1038}
1039
1040bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1041 fDeviceCMDirty = true;
1042 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001043 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044 return fMCRec->fMatrix->preScale(sx, sy);
1045}
1046
1047bool SkCanvas::rotate(SkScalar degrees) {
1048 fDeviceCMDirty = true;
1049 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001050 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 return fMCRec->fMatrix->preRotate(degrees);
1052}
1053
1054bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1055 fDeviceCMDirty = true;
1056 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001057 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 return fMCRec->fMatrix->preSkew(sx, sy);
1059}
1060
1061bool SkCanvas::concat(const SkMatrix& matrix) {
1062 fDeviceCMDirty = true;
1063 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001064 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065 return fMCRec->fMatrix->preConcat(matrix);
1066}
1067
1068void SkCanvas::setMatrix(const SkMatrix& matrix) {
1069 fDeviceCMDirty = true;
1070 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001071 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001072 *fMCRec->fMatrix = matrix;
1073}
1074
1075// this is not virtual, so it must call a virtual method so that subclasses
1076// will see its action
1077void SkCanvas::resetMatrix() {
1078 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001079
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 matrix.reset();
1081 this->setMatrix(matrix);
1082}
1083
1084//////////////////////////////////////////////////////////////////////////////
1085
reed@google.comc42d35d2011-10-12 11:57:42 +00001086bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
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
1093 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001094 // for these simpler matrices, we can stay a rect ever after applying
1095 // the matrix. This means we don't have to a) make a path, and b) tell
1096 // the region code to scan-convert the path, only to discover that it
1097 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099
1100 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001101 fClipStack.clipDevRect(r, op, doAA);
1102 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001104 // since we're rotate or some such thing, we convert the rect to a path
1105 // and clip against that, since it can handle any matrix. However, to
1106 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1107 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001108 SkPath path;
1109
1110 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001111 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 }
1113}
1114
reed@google.com00177082011-10-12 14:34:30 +00001115static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001116 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001117 // base is used to limit the size (and therefore memory allocation) of the
1118 // region that results from scan converting devPath.
1119 SkRegion base;
1120
reed@google.com819c9212011-02-23 18:56:55 +00001121 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001122 // since we are intersect, we can do better (tighter) with currRgn's
1123 // bounds, than just using the device. However, if currRgn is complex,
1124 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001125 if (currClip->isRect()) {
1126 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001127 } else {
reed@google.com00177082011-10-12 14:34:30 +00001128 base.setRect(currClip->getBounds());
1129 SkRasterClip clip;
1130 clip.setPath(devPath, base, doAA);
1131 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001132 }
reed@google.com819c9212011-02-23 18:56:55 +00001133 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001134 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001135 if (!device) {
1136 return currClip->setEmpty();
1137 }
1138
junov@chromium.orga907ac32012-02-24 21:54:07 +00001139 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001140
1141 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001142 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001143 } else {
reed@google.com00177082011-10-12 14:34:30 +00001144 SkRasterClip clip;
1145 clip.setPath(devPath, base, doAA);
1146 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001147 }
1148 }
1149}
1150
reed@google.comc42d35d2011-10-12 11:57:42 +00001151bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001152 AutoValidateClip avc(this);
1153
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154 fDeviceCMDirty = true;
1155 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001156 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157
1158 SkPath devPath;
1159 path.transform(*fMCRec->fMatrix, &devPath);
1160
reed@google.comfe701122011-11-08 19:41:23 +00001161 // Check if the transfomation, or the original path itself
1162 // made us empty. Note this can also happen if we contained NaN
1163 // values. computing the bounds detects this, and will set our
1164 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1165 if (devPath.getBounds().isEmpty()) {
1166 // resetting the path will remove any NaN or other wanky values
1167 // that might upset our scan converter.
1168 devPath.reset();
1169 }
1170
reed@google.com5c3d1472011-02-22 19:12:23 +00001171 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001172 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001173
reed@google.com00177082011-10-12 14:34:30 +00001174 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175}
1176
1177bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001178 AutoValidateClip avc(this);
1179
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 fDeviceCMDirty = true;
1181 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001182 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001183
reed@google.com5c3d1472011-02-22 19:12:23 +00001184 // todo: signal fClipStack that we have a region, and therefore (I guess)
1185 // we have to ignore it, and use the region directly?
1186 fClipStack.clipDevRect(rgn.getBounds());
1187
reed@google.com00177082011-10-12 14:34:30 +00001188 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189}
1190
reed@google.com819c9212011-02-23 18:56:55 +00001191#ifdef SK_DEBUG
1192void SkCanvas::validateClip() const {
1193 // construct clipRgn from the clipstack
1194 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001195 if (!device) {
1196 SkASSERT(this->getTotalClip().isEmpty());
1197 return;
1198 }
1199
reed@google.com819c9212011-02-23 18:56:55 +00001200 SkIRect ir;
1201 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001202 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001203
1204 SkClipStack::B2FIter iter(fClipStack);
1205 const SkClipStack::B2FIter::Clip* clip;
1206 while ((clip = iter.next()) != NULL) {
1207 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001208 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001209 } else if (clip->fRect) {
1210 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001211 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001212 } else {
reed@google.com00177082011-10-12 14:34:30 +00001213 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001214 }
1215 }
1216
reed@google.com6f8f2922011-03-04 22:27:10 +00001217#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001218 // now compare against the current rgn
1219 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001220 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001221#endif
reed@google.com819c9212011-02-23 18:56:55 +00001222}
1223#endif
1224
reed@google.com90c07ea2012-04-13 13:50:27 +00001225void SkCanvas::replayClips(ClipVisitor* visitor) const {
1226 SkClipStack::B2FIter iter(fClipStack);
1227 const SkClipStack::B2FIter::Clip* clip;
1228
robertphillips@google.com7460b372012-04-25 16:54:51 +00001229 SkRect empty = { 0, 0, 0, 0 };
reed@google.com90c07ea2012-04-13 13:50:27 +00001230 while ((clip = iter.next()) != NULL) {
1231 if (clip->fPath) {
1232 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1233 } else if (clip->fRect) {
1234 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1235 } else {
1236 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1237 }
1238 }
1239}
1240
reed@google.com5c3d1472011-02-22 19:12:23 +00001241///////////////////////////////////////////////////////////////////////////////
1242
reed@android.comba09de42010-02-05 20:46:05 +00001243void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001245 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1246 fLocalBoundsCompareTypeBW;
1247
1248 if (!this->getClipBounds(&r, et)) {
1249 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001251 rCompare.set(SkScalarToCompareType(r.fLeft),
1252 SkScalarToCompareType(r.fTop),
1253 SkScalarToCompareType(r.fRight),
1254 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001255 }
1256}
1257
reed@android.comd252db02009-04-01 18:31:44 +00001258/* current impl ignores edgetype, and relies on
1259 getLocalClipBoundsCompareType(), which always returns a value assuming
1260 antialiasing (worst case)
1261 */
reed@android.comba09de42010-02-05 20:46:05 +00001262bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001263
reed@google.com16078632011-12-06 18:56:37 +00001264 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001265 return true;
1266
reed@google.com00177082011-10-12 14:34:30 +00001267 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268 return true;
1269 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270
tomhudson@google.com8d430182011-06-06 19:11:19 +00001271 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001272 SkRect dst;
1273 fMCRec->fMatrix->mapRect(&dst, rect);
1274 SkIRect idst;
1275 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001276 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001277 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001278 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001279
reed@android.coma380ae42009-07-21 01:17:02 +00001280 // for speed, do the most likely reject compares first
1281 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1282 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1283 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1284 return true;
1285 }
1286 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1287 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1288 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1289 return true;
1290 }
1291 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293}
1294
1295bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001296 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297}
1298
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001299static inline int pinIntForScalar(int x) {
1300#ifdef SK_SCALAR_IS_FIXED
1301 if (x < SK_MinS16) {
1302 x = SK_MinS16;
1303 } else if (x > SK_MaxS16) {
1304 x = SK_MaxS16;
1305 }
1306#endif
1307 return x;
1308}
1309
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001311 SkIRect ibounds;
1312 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 return false;
1314 }
1315
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001316 SkMatrix inverse;
1317 // if we can't invert the CTM, we can't return local clip bounds
1318 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001319 if (bounds) {
1320 bounds->setEmpty();
1321 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001322 return false;
1323 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001325 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001326 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 // adjust it outwards if we are antialiasing
1328 int inset = (kAA_EdgeType == et);
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001329
1330 // SkRect::iset() will correctly assert if we pass a value out of range
1331 // (when SkScalar==fixed), so we pin to legal values. This does not
1332 // really returnt the correct answer, but its the best we can do given
1333 // that we've promised to return SkRect (even though we support devices
1334 // that can be larger than 32K in width or height).
1335 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1336 pinIntForScalar(ibounds.fTop - inset),
1337 pinIntForScalar(ibounds.fRight + inset),
1338 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 inverse.mapRect(bounds, r);
1340 }
1341 return true;
1342}
1343
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001344bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001345 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001346 if (clip.isEmpty()) {
1347 if (bounds) {
1348 bounds->setEmpty();
1349 }
1350 return false;
1351 }
1352
1353 if (NULL != bounds) {
1354 *bounds = clip.getBounds();
1355 }
1356 return true;
1357}
1358
reed@android.com8a1c16f2008-12-17 15:59:43 +00001359const SkMatrix& SkCanvas::getTotalMatrix() const {
1360 return *fMCRec->fMatrix;
1361}
1362
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001363SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001364 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1365 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001366 return kComplex_ClipType;
1367}
1368
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001370 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371}
1372
reed@android.comf2b98d62010-12-20 18:26:13 +00001373void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1374 if (NULL == matrix || matrix->isIdentity()) {
1375 if (fUseExternalMatrix) {
1376 fDeviceCMDirty = true;
1377 }
1378 fUseExternalMatrix = false;
1379 } else {
mike@reedtribe.org90bf4272012-04-14 19:06:16 +00001380 if (matrix->invert(&fExternalInverse)) {
1381 fExternalMatrix = *matrix;
1382 fUseExternalMatrix = true;
1383 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.comfc9a3be2012-04-12 13:52:14 +00001384 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001385 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001386}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001387
bsalomon@google.come97f0852011-06-17 13:10:25 +00001388SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1389 int width, int height,
1390 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001391 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001392 if (device) {
1393 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1394 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001395 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001396 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001397 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398}
1399
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001400SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001401 int width, int height,
1402 bool isOpaque) {
1403 SkDevice* device = this->getDevice();
1404 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001405 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001406 } else {
1407 return NULL;
1408 }
1409}
1410
1411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412//////////////////////////////////////////////////////////////////////////////
1413// These are the virtual drawing methods
1414//////////////////////////////////////////////////////////////////////////////
1415
reed@google.com2a981812011-04-14 18:59:28 +00001416void SkCanvas::clear(SkColor color) {
1417 SkDrawIter iter(this);
1418
1419 while (iter.next()) {
1420 iter.fDevice->clear(color);
1421 }
1422}
1423
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001425 this->internalDrawPaint(paint);
1426}
1427
1428void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001429 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430
1431 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001432 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 }
1434
reed@google.com4e2b3d32011-04-07 14:18:59 +00001435 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436}
1437
1438void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1439 const SkPaint& paint) {
1440 if ((long)count <= 0) {
1441 return;
1442 }
1443
1444 SkASSERT(pts != NULL);
1445
reed@google.com4e2b3d32011-04-07 14:18:59 +00001446 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001447
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001449 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450 }
reed@google.com4b226022011-01-11 18:32:13 +00001451
reed@google.com4e2b3d32011-04-07 14:18:59 +00001452 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453}
1454
1455void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1456 if (paint.canComputeFastBounds()) {
1457 SkRect storage;
1458 if (this->quickReject(paint.computeFastBounds(r, &storage),
1459 paint2EdgeType(&paint))) {
1460 return;
1461 }
1462 }
reed@google.com4b226022011-01-11 18:32:13 +00001463
reed@google.com4e2b3d32011-04-07 14:18:59 +00001464 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465
1466 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001467 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468 }
1469
reed@google.com4e2b3d32011-04-07 14:18:59 +00001470 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471}
1472
1473void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001474 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001475 SkRect storage;
1476 const SkRect& bounds = path.getBounds();
1477 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 paint2EdgeType(&paint))) {
1479 return;
1480 }
1481 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001482 if (path.isEmpty()) {
1483 if (path.isInverseFillType()) {
1484 this->internalDrawPaint(paint);
1485 }
1486 return;
1487 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488
reed@google.com4e2b3d32011-04-07 14:18:59 +00001489 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490
1491 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001492 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493 }
1494
reed@google.com4e2b3d32011-04-07 14:18:59 +00001495 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496}
1497
1498void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1499 const SkPaint* paint) {
1500 SkDEBUGCODE(bitmap.validate();)
1501
reed@google.com3d608122011-11-21 15:16:16 +00001502 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001503 SkRect bounds = {
1504 x, y,
1505 x + SkIntToScalar(bitmap.width()),
1506 y + SkIntToScalar(bitmap.height())
1507 };
1508 if (paint) {
1509 (void)paint->computeFastBounds(bounds, &bounds);
1510 }
1511 if (this->quickReject(bounds, paint2EdgeType(paint))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512 return;
1513 }
1514 }
reed@google.com4b226022011-01-11 18:32:13 +00001515
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516 SkMatrix matrix;
1517 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001518 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519}
1520
reed@google.com9987ec32011-09-07 11:57:52 +00001521// this one is non-virtual, so it can be called safely by other canvas apis
1522void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1523 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1525 return;
1526 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001527
reed@android.com8a1c16f2008-12-17 15:59:43 +00001528 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001529 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001530 SkRect storage;
1531 const SkRect* bounds = &dst;
1532 if (paint) {
1533 bounds = &paint->computeFastBounds(dst, &storage);
1534 }
1535 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001536 return;
1537 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538 }
reed@google.com3d608122011-11-21 15:16:16 +00001539
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001541
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001543 SkRect tmpSrc;
1544 if (src) {
1545 tmpSrc.set(*src);
1546 // if the extract process clipped off the top or left of the
1547 // original, we adjust for that here to get the position right.
1548 if (tmpSrc.fLeft > 0) {
1549 tmpSrc.fRight -= tmpSrc.fLeft;
1550 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001551 }
reed@android.com87899992009-10-16 14:48:38 +00001552 if (tmpSrc.fTop > 0) {
1553 tmpSrc.fBottom -= tmpSrc.fTop;
1554 tmpSrc.fTop = 0;
1555 }
1556 } else {
1557 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1558 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559 }
reed@android.com87899992009-10-16 14:48:38 +00001560 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001561
reed@android.comf2b98d62010-12-20 18:26:13 +00001562 // ensure that src is "valid" before we pass it to our internal routines
1563 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1564 SkIRect tmpISrc;
1565 if (src) {
1566 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001567 if (!tmpISrc.intersect(*src)) {
1568 return;
1569 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001570 src = &tmpISrc;
1571 }
1572 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001573}
1574
reed@google.com9987ec32011-09-07 11:57:52 +00001575void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1576 const SkRect& dst, const SkPaint* paint) {
1577 SkDEBUGCODE(bitmap.validate();)
1578 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1579}
1580
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1582 const SkPaint* paint) {
1583 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001584 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585}
1586
reed@android.comf2b98d62010-12-20 18:26:13 +00001587void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1588 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001590
reed@google.com4e2b3d32011-04-07 14:18:59 +00001591 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001592
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001594 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001595 }
reed@android.com9b039062009-02-11 15:09:58 +00001596
reed@google.com4e2b3d32011-04-07 14:18:59 +00001597 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001598}
1599
reed@google.com9987ec32011-09-07 11:57:52 +00001600void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1601 const SkIRect& center, const SkRect& dst,
1602 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001603 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001604 SkRect storage;
1605 const SkRect* bounds = &dst;
1606 if (paint) {
1607 bounds = &paint->computeFastBounds(dst, &storage);
1608 }
1609 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001610 return;
1611 }
1612 }
1613
reed@google.com9987ec32011-09-07 11:57:52 +00001614 const int32_t w = bitmap.width();
1615 const int32_t h = bitmap.height();
1616
1617 SkIRect c = center;
1618 // pin center to the bounds of the bitmap
1619 c.fLeft = SkMax32(0, center.fLeft);
1620 c.fTop = SkMax32(0, center.fTop);
1621 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1622 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1623
1624 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1625 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1626 SkScalar dstX[4] = {
1627 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1628 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1629 };
1630 SkScalar dstY[4] = {
1631 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1632 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1633 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001634
reed@google.com9987ec32011-09-07 11:57:52 +00001635 if (dstX[1] > dstX[2]) {
1636 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1637 dstX[2] = dstX[1];
1638 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001639
reed@google.com9987ec32011-09-07 11:57:52 +00001640 if (dstY[1] > dstY[2]) {
1641 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1642 dstY[2] = dstY[1];
1643 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001644
reed@google.com9987ec32011-09-07 11:57:52 +00001645 SkIRect s;
1646 SkRect d;
1647 for (int y = 0; y < 3; y++) {
1648 s.fTop = srcY[y];
1649 s.fBottom = srcY[y+1];
1650 d.fTop = dstY[y];
1651 d.fBottom = dstY[y+1];
1652 for (int x = 0; x < 3; x++) {
1653 s.fLeft = srcX[x];
1654 s.fRight = srcX[x+1];
1655 d.fLeft = dstX[x];
1656 d.fRight = dstX[x+1];
1657 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1658 }
1659 }
1660}
1661
1662void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1663 const SkRect& dst, const SkPaint* paint) {
1664 SkDEBUGCODE(bitmap.validate();)
1665
1666 // Need a device entry-point, so gpu can use a mesh
1667 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1668}
1669
reed@google.comf67e4cf2011-03-15 20:56:58 +00001670class SkDeviceFilteredPaint {
1671public:
1672 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1673 SkDevice::TextFlags flags;
1674 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001675 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001676 newPaint->setFlags(flags.fFlags);
1677 newPaint->setHinting(flags.fHinting);
1678 fPaint = newPaint;
1679 } else {
1680 fPaint = &paint;
1681 }
1682 }
1683
reed@google.comf67e4cf2011-03-15 20:56:58 +00001684 const SkPaint& paint() const { return *fPaint; }
1685
1686private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001687 const SkPaint* fPaint;
1688 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001689};
1690
bungeman@google.com52c748b2011-08-22 21:30:43 +00001691void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1692 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001693 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001694 draw.fDevice->drawRect(draw, r, paint);
1695 } else {
1696 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001697 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001698 draw.fDevice->drawRect(draw, r, p);
1699 }
1700}
1701
1702void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1703 const char text[], size_t byteLength,
1704 SkScalar x, SkScalar y) {
1705 SkASSERT(byteLength == 0 || text != NULL);
1706
1707 // nothing to draw
1708 if (text == NULL || byteLength == 0 ||
1709 draw.fClip->isEmpty() ||
1710 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1711 return;
1712 }
1713
1714 SkScalar width = 0;
1715 SkPoint start;
1716
1717 start.set(0, 0); // to avoid warning
1718 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1719 SkPaint::kStrikeThruText_Flag)) {
1720 width = paint.measureText(text, byteLength);
1721
1722 SkScalar offsetX = 0;
1723 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1724 offsetX = SkScalarHalf(width);
1725 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1726 offsetX = width;
1727 }
1728 start.set(x - offsetX, y);
1729 }
1730
1731 if (0 == width) {
1732 return;
1733 }
1734
1735 uint32_t flags = paint.getFlags();
1736
1737 if (flags & (SkPaint::kUnderlineText_Flag |
1738 SkPaint::kStrikeThruText_Flag)) {
1739 SkScalar textSize = paint.getTextSize();
1740 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1741 SkRect r;
1742
1743 r.fLeft = start.fX;
1744 r.fRight = start.fX + width;
1745
1746 if (flags & SkPaint::kUnderlineText_Flag) {
1747 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1748 start.fY);
1749 r.fTop = offset;
1750 r.fBottom = offset + height;
1751 DrawRect(draw, paint, r, textSize);
1752 }
1753 if (flags & SkPaint::kStrikeThruText_Flag) {
1754 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1755 start.fY);
1756 r.fTop = offset;
1757 r.fBottom = offset + height;
1758 DrawRect(draw, paint, r, textSize);
1759 }
1760 }
1761}
1762
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763void SkCanvas::drawText(const void* text, size_t byteLength,
1764 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001765 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766
1767 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001768 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001769 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001770 DrawTextDecorations(iter, dfp.paint(),
1771 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 }
1773
reed@google.com4e2b3d32011-04-07 14:18:59 +00001774 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775}
1776
1777void SkCanvas::drawPosText(const void* text, size_t byteLength,
1778 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001779 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001780
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001782 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001784 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785 }
reed@google.com4b226022011-01-11 18:32:13 +00001786
reed@google.com4e2b3d32011-04-07 14:18:59 +00001787 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788}
1789
1790void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1791 const SkScalar xpos[], SkScalar constY,
1792 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001793 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001794
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001796 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001798 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799 }
reed@google.com4b226022011-01-11 18:32:13 +00001800
reed@google.com4e2b3d32011-04-07 14:18:59 +00001801 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802}
1803
1804void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1805 const SkPath& path, const SkMatrix* matrix,
1806 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001807 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808
1809 while (iter.next()) {
1810 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001811 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 }
1813
reed@google.com4e2b3d32011-04-07 14:18:59 +00001814 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815}
1816
djsollen@google.com56c69772011-11-08 19:00:26 +00001817#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001818void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1819 const SkPoint pos[], const SkPaint& paint,
1820 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001821 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001822
1823 while (iter.next()) {
1824 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001825 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001826 }
1827
reed@google.com4e2b3d32011-04-07 14:18:59 +00001828 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001829}
1830#endif
1831
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1833 const SkPoint verts[], const SkPoint texs[],
1834 const SkColor colors[], SkXfermode* xmode,
1835 const uint16_t indices[], int indexCount,
1836 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001837 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001838
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839 while (iter.next()) {
1840 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001841 colors, xmode, indices, indexCount,
1842 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843 }
reed@google.com4b226022011-01-11 18:32:13 +00001844
reed@google.com4e2b3d32011-04-07 14:18:59 +00001845 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001846}
1847
reed@android.comcb608442009-12-04 21:32:27 +00001848void SkCanvas::drawData(const void* data, size_t length) {
1849 // do nothing. Subclasses may do something with the data
1850}
1851
reed@android.com8a1c16f2008-12-17 15:59:43 +00001852//////////////////////////////////////////////////////////////////////////////
1853// These methods are NOT virtual, and therefore must call back into virtual
1854// methods, rather than actually drawing themselves.
1855//////////////////////////////////////////////////////////////////////////////
1856
1857void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001858 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001859 SkPaint paint;
1860
1861 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001862 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001863 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001864 }
1865 this->drawPaint(paint);
1866}
1867
reed@android.com845fdac2009-06-23 03:01:32 +00001868void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001869 SkPaint paint;
1870
1871 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001872 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001873 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874 }
1875 this->drawPaint(paint);
1876}
1877
1878void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1879 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001880
reed@android.com8a1c16f2008-12-17 15:59:43 +00001881 pt.set(x, y);
1882 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1883}
1884
1885void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1886 SkPoint pt;
1887 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001888
reed@android.com8a1c16f2008-12-17 15:59:43 +00001889 pt.set(x, y);
1890 paint.setColor(color);
1891 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1892}
1893
1894void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1895 const SkPaint& paint) {
1896 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001897
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898 pts[0].set(x0, y0);
1899 pts[1].set(x1, y1);
1900 this->drawPoints(kLines_PointMode, 2, pts, paint);
1901}
1902
1903void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1904 SkScalar right, SkScalar bottom,
1905 const SkPaint& paint) {
1906 SkRect r;
1907
1908 r.set(left, top, right, bottom);
1909 this->drawRect(r, paint);
1910}
1911
1912void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1913 const SkPaint& paint) {
1914 if (radius < 0) {
1915 radius = 0;
1916 }
1917
1918 SkRect r;
1919 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001920
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921 if (paint.canComputeFastBounds()) {
1922 SkRect storage;
1923 if (this->quickReject(paint.computeFastBounds(r, &storage),
1924 paint2EdgeType(&paint))) {
1925 return;
1926 }
1927 }
reed@google.com4b226022011-01-11 18:32:13 +00001928
reed@android.com8a1c16f2008-12-17 15:59:43 +00001929 SkPath path;
1930 path.addOval(r);
1931 this->drawPath(path, paint);
1932}
1933
1934void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1935 const SkPaint& paint) {
1936 if (rx > 0 && ry > 0) {
1937 if (paint.canComputeFastBounds()) {
1938 SkRect storage;
1939 if (this->quickReject(paint.computeFastBounds(r, &storage),
1940 paint2EdgeType(&paint))) {
1941 return;
1942 }
1943 }
1944
1945 SkPath path;
1946 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1947 this->drawPath(path, paint);
1948 } else {
1949 this->drawRect(r, paint);
1950 }
1951}
1952
1953void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1954 if (paint.canComputeFastBounds()) {
1955 SkRect storage;
1956 if (this->quickReject(paint.computeFastBounds(oval, &storage),
1957 paint2EdgeType(&paint))) {
1958 return;
1959 }
1960 }
1961
1962 SkPath path;
1963 path.addOval(oval);
1964 this->drawPath(path, paint);
1965}
1966
1967void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1968 SkScalar sweepAngle, bool useCenter,
1969 const SkPaint& paint) {
1970 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1971 this->drawOval(oval, paint);
1972 } else {
1973 SkPath path;
1974 if (useCenter) {
1975 path.moveTo(oval.centerX(), oval.centerY());
1976 }
1977 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1978 if (useCenter) {
1979 path.close();
1980 }
1981 this->drawPath(path, paint);
1982 }
1983}
1984
1985void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1986 const SkPath& path, SkScalar hOffset,
1987 SkScalar vOffset, const SkPaint& paint) {
1988 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001989
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990 matrix.setTranslate(hOffset, vOffset);
1991 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1992}
1993
reed@android.comf76bacf2009-05-13 14:00:33 +00001994///////////////////////////////////////////////////////////////////////////////
1995
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996void SkCanvas::drawPicture(SkPicture& picture) {
1997 int saveCount = save();
1998 picture.draw(this);
1999 restoreToCount(saveCount);
2000}
2001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002002///////////////////////////////////////////////////////////////////////////////
2003///////////////////////////////////////////////////////////////////////////////
2004
2005SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002006 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002007
2008 SkASSERT(canvas);
2009
2010 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2011 fDone = !fImpl->next();
2012}
2013
2014SkCanvas::LayerIter::~LayerIter() {
2015 fImpl->~SkDrawIter();
2016}
2017
2018void SkCanvas::LayerIter::next() {
2019 fDone = !fImpl->next();
2020}
2021
2022SkDevice* SkCanvas::LayerIter::device() const {
2023 return fImpl->getDevice();
2024}
2025
2026const SkMatrix& SkCanvas::LayerIter::matrix() const {
2027 return fImpl->getMatrix();
2028}
2029
2030const SkPaint& SkCanvas::LayerIter::paint() const {
2031 const SkPaint* paint = fImpl->getPaint();
2032 if (NULL == paint) {
2033 paint = &fDefaultPaint;
2034 }
2035 return *paint;
2036}
2037
2038const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2039int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2040int SkCanvas::LayerIter::y() const { return fImpl->getY(); }