blob: c38ed7639a7ae7adba9c4a25e873f21534a2aa93 [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"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000013#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
15#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000019#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkScalarCompare.h"
reed@google.com97af1a62012-08-28 12:19:02 +000021#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000023#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000024#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026
reed@google.com82ce2b82012-06-26 17:43:26 +000027SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000028SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000029SK_DEFINE_INST_COUNT(SkDrawFilter)
30
reed@google.comda17f752012-08-16 18:27:05 +000031// experimental for faster tiled drawing...
32//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000033
reed@android.com8a1c16f2008-12-17 15:59:43 +000034//#define SK_TRACE_SAVERESTORE
35
36#ifdef SK_TRACE_SAVERESTORE
37 static int gLayerCounter;
38 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
39 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
40
41 static int gRecCounter;
42 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
43 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
44
45 static int gCanvasCounter;
46 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
47 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
48#else
49 #define inc_layer()
50 #define dec_layer()
51 #define inc_rec()
52 #define dec_rec()
53 #define inc_canvas()
54 #define dec_canvas()
55#endif
56
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000057typedef SkTLazy<SkPaint> SkLazyPaint;
58
reed@google.com97af1a62012-08-28 12:19:02 +000059void SkCanvas::predrawNotify() {
60 if (fSurfaceBase) {
61 fSurfaceBase->aboutToDraw(this);
62 }
63}
64
reed@android.com8a1c16f2008-12-17 15:59:43 +000065///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000066
67/* This is the record we keep for each SkDevice that the user installs.
68 The clip/matrix/proc are fields that reflect the top of the save/restore
69 stack. Whenever the canvas changes, it marks a dirty flag, and then before
70 these are used (assuming we're not on a layer) we rebuild these cache
71 values: they reflect the top of the save stack, but translated and clipped
72 by the device's XY offset and bitmap-bounds.
73*/
74struct DeviceCM {
75 DeviceCM* fNext;
76 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000077 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000079 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000081 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 : fNext(NULL) {
83 if (NULL != device) {
84 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000085 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 }
reed@google.com4b226022011-01-11 18:32:13 +000087 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000089 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000090
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000093 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 fDevice->unref();
95 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000096 SkDELETE(fPaint);
97 }
reed@google.com4b226022011-01-11 18:32:13 +000098
reed@google.com045e62d2011-10-24 12:19:46 +000099 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
100 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000101 int x = fDevice->getOrigin().x();
102 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 int width = fDevice->width();
104 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000105
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 if ((x | y) == 0) {
107 fMatrix = &totalMatrix;
108 fClip = totalClip;
109 } else {
110 fMatrixStorage = totalMatrix;
111 fMatrixStorage.postTranslate(SkIntToScalar(-x),
112 SkIntToScalar(-y));
113 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 totalClip.translate(-x, -y, &fClip);
116 }
117
reed@google.com045e62d2011-10-24 12:19:46 +0000118 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119
120 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000123 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 SkRegion::kDifference_Op);
125 }
reed@google.com4b226022011-01-11 18:32:13 +0000126
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000127 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129#ifdef SK_DEBUG
130 if (!fClip.isEmpty()) {
131 SkIRect deviceR;
132 deviceR.set(0, 0, width, height);
133 SkASSERT(deviceR.contains(fClip.getBounds()));
134 }
135#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000136 }
137
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000139 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140};
141
142/* This is the record we keep for each save/restore level in the stack.
143 Since a level optionally copies the matrix and/or stack, we have pointers
144 for these fields. If the value is copied for this level, the copy is
145 stored in the ...Storage field, and the pointer points to that. If the
146 value is not copied for this level, we ignore ...Storage, and just point
147 at the corresponding value in the previous level in the stack.
148*/
149class SkCanvas::MCRec {
150public:
151 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000152 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
153 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
154 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000155
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 DeviceCM* fLayer;
157 /* If there are any layers in the stack, this points to the top-most
158 one that is at or below this level in the stack (so we know what
159 bitmap/device to draw into from this level. This value is NOT
160 reference counted, since the real owner is either our fLayer field,
161 or a previous one in a lower level.)
162 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000163 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164
165 MCRec(const MCRec* prev, int flags) {
166 if (NULL != prev) {
167 if (flags & SkCanvas::kMatrix_SaveFlag) {
168 fMatrixStorage = *prev->fMatrix;
169 fMatrix = &fMatrixStorage;
170 } else {
171 fMatrix = prev->fMatrix;
172 }
reed@google.com4b226022011-01-11 18:32:13 +0000173
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000175 fRasterClipStorage = *prev->fRasterClip;
176 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 } else {
reed@google.com00177082011-10-12 14:34:30 +0000178 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 }
180
181 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000182 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
184 fTopLayer = prev->fTopLayer;
185 } else { // no prev
186 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000187
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000189 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 fFilter = NULL;
191 fTopLayer = NULL;
192 }
193 fLayer = NULL;
194
195 // don't bother initializing fNext
196 inc_rec();
197 }
198 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000199 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 SkDELETE(fLayer);
201 dec_rec();
202 }
reed@google.com4b226022011-01-11 18:32:13 +0000203
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204private:
reed@google.com00177082011-10-12 14:34:30 +0000205 SkMatrix fMatrixStorage;
206 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207};
208
209class SkDrawIter : public SkDraw {
210public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000211 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000212 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000213 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 canvas->updateDeviceCMCache();
215
reed@google.com90c07ea2012-04-13 13:50:27 +0000216 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 fBounder = canvas->getBounder();
218 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000219 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 }
reed@google.com4b226022011-01-11 18:32:13 +0000221
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 bool next() {
223 // skip over recs with empty clips
224 if (fSkipEmptyClips) {
225 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
226 fCurrLayer = fCurrLayer->fNext;
227 }
228 }
229
reed@google.comf68c5e22012-02-24 16:38:58 +0000230 const DeviceCM* rec = fCurrLayer;
231 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232
233 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000234 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
235 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 fDevice = rec->fDevice;
237 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000239 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240
241 fCurrLayer = rec->fNext;
242 if (fBounder) {
243 fBounder->setClip(fClip);
244 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 return true;
248 }
249 return false;
250 }
reed@google.com4b226022011-01-11 18:32:13 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000253 int getX() const { return fDevice->getOrigin().x(); }
254 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 const SkMatrix& getMatrix() const { return *fMatrix; }
256 const SkRegion& getClip() const { return *fClip; }
257 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000258
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259private:
260 SkCanvas* fCanvas;
261 const DeviceCM* fCurrLayer;
262 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 SkBool8 fSkipEmptyClips;
264
265 typedef SkDraw INHERITED;
266};
267
268/////////////////////////////////////////////////////////////////////////////
269
270class AutoDrawLooper {
271public:
reed@google.com8926b162012-03-23 15:36:36 +0000272 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
273 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000274 fCanvas = canvas;
275 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000277 fPaint = NULL;
278 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000279 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000280 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281
reed@google.com8926b162012-03-23 15:36:36 +0000282 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
283 SkPaint tmp;
284 tmp.setImageFilter(fOrigPaint.getImageFilter());
285 // it would be nice if we had a guess at the bounds, instead of null
286 (void)canvas->internalSaveLayer(NULL, &tmp,
287 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
288 // we'll clear the imageFilter for the actual draws in next(), so
289 // it will only be applied during the restore().
290 fDoClearImageFilter = true;
291 }
292
reed@google.com4e2b3d32011-04-07 14:18:59 +0000293 if (fLooper) {
294 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000295 fIsSimple = false;
296 } else {
297 // can we be marked as simple?
298 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000299 }
300 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000301
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000303 if (fDoClearImageFilter) {
304 fCanvas->internalRestore();
305 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000306 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000308
reed@google.com4e2b3d32011-04-07 14:18:59 +0000309 const SkPaint& paint() const {
310 SkASSERT(fPaint);
311 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000313
reed@google.com129ec222012-05-15 13:24:09 +0000314 bool next(SkDrawFilter::Type drawType) {
315 if (fDone) {
316 return false;
317 } else if (fIsSimple) {
318 fDone = true;
319 fPaint = &fOrigPaint;
320 return !fPaint->nothingToDraw();
321 } else {
322 return this->doNext(drawType);
323 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000324 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000325
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000327 SkLazyPaint fLazyPaint;
328 SkCanvas* fCanvas;
329 const SkPaint& fOrigPaint;
330 SkDrawLooper* fLooper;
331 SkDrawFilter* fFilter;
332 const SkPaint* fPaint;
333 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000334 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000335 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000336 bool fIsSimple;
337
338 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339};
340
reed@google.com129ec222012-05-15 13:24:09 +0000341bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000342 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000343 SkASSERT(!fIsSimple);
344 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
345
346 SkPaint* paint = fLazyPaint.set(fOrigPaint);
347
348 if (fDoClearImageFilter) {
349 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000350 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000351
reed@google.com129ec222012-05-15 13:24:09 +0000352 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000353 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000354 return false;
355 }
356 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000357 if (!fFilter->filter(paint, drawType)) {
358 fDone = true;
359 return false;
360 }
reed@google.com129ec222012-05-15 13:24:09 +0000361 if (NULL == fLooper) {
362 // no looper means we only draw once
363 fDone = true;
364 }
365 }
366 fPaint = paint;
367
368 // if we only came in here for the imagefilter, mark us as done
369 if (!fLooper && !fFilter) {
370 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000371 }
372
373 // call this after any possible paint modifiers
374 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 fPaint = NULL;
376 return false;
377 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000378 return true;
379}
380
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381/* Stack helper for managing a SkBounder. In the destructor, if we were
382 given a bounder, we call its commit() method, signifying that we are
383 done accumulating bounds for that draw.
384*/
385class SkAutoBounderCommit {
386public:
387 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
388 ~SkAutoBounderCommit() {
389 if (NULL != fBounder) {
390 fBounder->commit();
391 }
392 }
393private:
394 SkBounder* fBounder;
395};
396
397#include "SkColorPriv.h"
398
399class AutoValidator {
400public:
401 AutoValidator(SkDevice* device) : fDevice(device) {}
402 ~AutoValidator() {
403#ifdef SK_DEBUG
404 const SkBitmap& bm = fDevice->accessBitmap(false);
405 if (bm.config() == SkBitmap::kARGB_4444_Config) {
406 for (int y = 0; y < bm.height(); y++) {
407 const SkPMColor16* p = bm.getAddr16(0, y);
408 for (int x = 0; x < bm.width(); x++) {
409 SkPMColor16 c = p[x];
410 SkPMColor16Assert(c);
411 }
412 }
413 }
414#endif
415 }
416private:
417 SkDevice* fDevice;
418};
419
420////////// macros to place around the internal draw calls //////////////////
421
reed@google.com8926b162012-03-23 15:36:36 +0000422#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
423/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000424 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000425 AutoDrawLooper looper(this, paint, true); \
426 while (looper.next(type)) { \
427 SkAutoBounderCommit ac(fBounder); \
428 SkDrawIter iter(this);
429
reed@google.com4e2b3d32011-04-07 14:18:59 +0000430#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000432 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000433 AutoDrawLooper looper(this, paint); \
434 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435 SkAutoBounderCommit ac(fBounder); \
436 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000437
reed@google.com4e2b3d32011-04-07 14:18:59 +0000438#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439
440////////////////////////////////////////////////////////////////////////////
441
442SkDevice* SkCanvas::init(SkDevice* device) {
443 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000444 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000446 fAllowSoftClip = true;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000447 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000448 fSaveLayerCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000449 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450
451 fMCRec = (MCRec*)fMCStack.push_back();
452 new (fMCRec) MCRec(NULL, 0);
453
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000454 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 fMCRec->fTopLayer = fMCRec->fLayer;
456 fMCRec->fNext = NULL;
457
reed@google.com97af1a62012-08-28 12:19:02 +0000458 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000459
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460 return this->setDevice(device);
461}
462
reed@google.comcde92112011-07-06 20:00:52 +0000463SkCanvas::SkCanvas()
464: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000465 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000466
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000467 this->init(NULL);
468}
469
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000471 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000472 inc_canvas();
473
474 this->init(device);
475}
476
477SkCanvas::SkCanvas(const SkBitmap& bitmap)
478 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
479 inc_canvas();
480
reed@google.comcde92112011-07-06 20:00:52 +0000481 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482}
483
484SkCanvas::~SkCanvas() {
485 // free up the contents of our deque
486 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000487 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000488
reed@android.com8a1c16f2008-12-17 15:59:43 +0000489 this->internalRestore(); // restore the last, since we're going away
490
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000491 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000492 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000493
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 dec_canvas();
495}
496
497SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
498 SkRefCnt_SafeAssign(fBounder, bounder);
499 return bounder;
500}
501
502SkDrawFilter* SkCanvas::getDrawFilter() const {
503 return fMCRec->fFilter;
504}
505
506SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
507 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
508 return filter;
509}
510
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000511SkMetaData& SkCanvas::getMetaData() {
512 // metadata users are rare, so we lazily allocate it. If that changes we
513 // can decide to just make it a field in the device (rather than a ptr)
514 if (NULL == fMetaData) {
515 fMetaData = new SkMetaData;
516 }
517 return *fMetaData;
518}
519
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520///////////////////////////////////////////////////////////////////////////////
521
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000522void SkCanvas::flush() {
523 SkDevice* device = this->getDevice();
524 if (device) {
525 device->flush();
526 }
527}
528
reed@google.com210ce002011-11-01 14:24:23 +0000529SkISize SkCanvas::getDeviceSize() const {
530 SkDevice* d = this->getDevice();
531 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
532}
533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534SkDevice* SkCanvas::getDevice() const {
535 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000536 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 SkASSERT(rec && rec->fLayer);
538 return rec->fLayer->fDevice;
539}
540
reed@google.com0b53d592012-03-19 18:26:34 +0000541SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
542 if (updateMatrixClip) {
543 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
544 }
reed@google.com9266fed2011-03-30 00:18:03 +0000545 return fMCRec->fTopLayer->fDevice;
546}
547
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548SkDevice* SkCanvas::setDevice(SkDevice* device) {
549 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000550 SkDeque::F2BIter iter(fMCStack);
551 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552 SkASSERT(rec && rec->fLayer);
553 SkDevice* rootDevice = rec->fLayer->fDevice;
554
555 if (rootDevice == device) {
556 return device;
557 }
reed@google.com4b226022011-01-11 18:32:13 +0000558
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000560 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 }
562 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000563 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 }
565
566 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
567 rootDevice = device;
568
569 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000570
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 /* Now we update our initial region to have the bounds of the new device,
572 and then intersect all of the clips in our stack with these bounds,
573 to ensure that we can't draw outside of the device's bounds (and trash
574 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000575
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 NOTE: this is only a partial-fix, since if the new device is larger than
577 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000578 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
580 reconstruct the correct clips, so this approximation will have to do.
581 The caller really needs to restore() back to the base if they want to
582 accurately take advantage of the new device bounds.
583 */
584
reed@google.com42aea282012-03-28 16:19:15 +0000585 SkIRect bounds;
586 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000588 } else {
589 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 }
reed@google.com42aea282012-03-28 16:19:15 +0000591 // now jam our 1st clip to be bounds, and intersect the rest with that
592 rec->fRasterClip->setRect(bounds);
593 while ((rec = (MCRec*)iter.next()) != NULL) {
594 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
595 }
596
reed@android.com8a1c16f2008-12-17 15:59:43 +0000597 return device;
598}
599
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000600bool SkCanvas::readPixels(SkBitmap* bitmap,
601 int x, int y,
602 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000603 SkDevice* device = this->getDevice();
604 if (!device) {
605 return false;
606 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000607 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000608}
609
bsalomon@google.comc6980972011-11-02 19:57:21 +0000610bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000611 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000612 if (!device) {
613 return false;
614 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000615
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000616 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000617 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000618 if (!bounds.intersect(srcRect)) {
619 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000620 }
621
622 SkBitmap tmp;
623 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
624 bounds.height());
625 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
626 bitmap->swap(tmp);
627 return true;
628 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000629 return false;
630 }
reed@google.com51df9e32010-12-23 19:29:18 +0000631}
632
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000633void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
634 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000635 SkDevice* device = this->getDevice();
636 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000637 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
638 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
639 device->accessBitmap(true);
640 device->writePixels(bitmap, x, y, config8888);
641 }
reed@google.com51df9e32010-12-23 19:29:18 +0000642 }
643}
644
junov@google.com4370aed2012-01-18 16:21:08 +0000645SkCanvas* SkCanvas::canvasForDrawIter() {
646 return this;
647}
648
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649//////////////////////////////////////////////////////////////////////////////
650
reed@android.com8a1c16f2008-12-17 15:59:43 +0000651void SkCanvas::updateDeviceCMCache() {
652 if (fDeviceCMDirty) {
653 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000654 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000656
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000658 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000659 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000660 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000662 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 } while ((layer = layer->fNext) != NULL);
664 }
665 fDeviceCMDirty = false;
666 }
667}
668
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669///////////////////////////////////////////////////////////////////////////////
670
671int SkCanvas::internalSave(SaveFlags flags) {
672 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000673
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 MCRec* newTop = (MCRec*)fMCStack.push_back();
675 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000676
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 newTop->fNext = fMCRec;
678 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000679
reed@google.com5c3d1472011-02-22 19:12:23 +0000680 fClipStack.save();
681 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
682
reed@android.com8a1c16f2008-12-17 15:59:43 +0000683 return saveCount;
684}
685
686int SkCanvas::save(SaveFlags flags) {
687 // call shared impl
688 return this->internalSave(flags);
689}
690
691#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
692#define C16MASK (1 << SkBitmap::kRGB_565_Config)
693#define C8MASK (1 << SkBitmap::kA8_Config)
694
695static SkBitmap::Config resolve_config(SkCanvas* canvas,
696 const SkIRect& bounds,
697 SkCanvas::SaveFlags flags,
698 bool* isOpaque) {
699 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
700
701#if 0
702 // loop through and union all the configs we may draw into
703 uint32_t configMask = 0;
704 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
705 {
706 SkDevice* device = canvas->getLayerDevice(i);
707 if (device->intersects(bounds))
708 configMask |= 1 << device->config();
709 }
710
711 // if the caller wants alpha or fullcolor, we can't return 565
712 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
713 SkCanvas::kHasAlphaLayer_SaveFlag))
714 configMask &= ~C16MASK;
715
716 switch (configMask) {
717 case C8MASK: // if we only have A8, return that
718 return SkBitmap::kA8_Config;
719
720 case C16MASK: // if we only have 565, return that
721 return SkBitmap::kRGB_565_Config;
722
723 default:
724 return SkBitmap::kARGB_8888_Config; // default answer
725 }
726#else
727 return SkBitmap::kARGB_8888_Config; // default answer
728#endif
729}
730
731static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
732 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
733}
734
junov@chromium.orga907ac32012-02-24 21:54:07 +0000735bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
736 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000737 SkIRect clipBounds;
738 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000739 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000740 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000741 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 if (NULL != bounds) {
743 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000744
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 this->getTotalMatrix().mapRect(&r, *bounds);
746 r.roundOut(&ir);
747 // early exit if the layer's bounds are clipped out
748 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000749 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000750 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000751 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000752 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753 }
754 } else { // no user bounds, so just use the clip
755 ir = clipBounds;
756 }
757
reed@google.com5c3d1472011-02-22 19:12:23 +0000758 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000759
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760 // early exit if the clip is now empty
761 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000762 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000763 return false;
764 }
765
766 if (intersection) {
767 *intersection = ir;
768 }
769 return true;
770}
771
772int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
773 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000774 return this->internalSaveLayer(bounds, paint, flags, false);
775}
776
777int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
778 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000779 // do this before we create the layer. We don't call the public save() since
780 // that would invoke a possibly overridden virtual
781 int count = this->internalSave(flags);
782
783 fDeviceCMDirty = true;
784
785 SkIRect ir;
786 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 return count;
788 }
789
reed@google.comb55deeb2012-01-06 14:43:09 +0000790 // Kill the imagefilter if our device doesn't allow it
791 SkLazyPaint lazyP;
792 if (paint && paint->getImageFilter()) {
793 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000794 if (justForImageFilter) {
795 // early exit if the layer was just for the imageFilter
796 return count;
797 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000798 SkPaint* p = lazyP.set(*paint);
799 p->setImageFilter(NULL);
800 paint = p;
801 }
802 }
803
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804 bool isOpaque;
805 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
806
reed@google.com76dd2772012-01-05 21:15:07 +0000807 SkDevice* device;
808 if (paint && paint->getImageFilter()) {
809 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
810 isOpaque);
811 } else {
812 device = this->createLayerDevice(config, ir.width(), ir.height(),
813 isOpaque);
814 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000815 if (NULL == device) {
816 SkDebugf("Unable to create device for layer.");
817 return count;
818 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000819
reed@google.com6f8f2922011-03-04 22:27:10 +0000820 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000821 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000822 device->unref();
823
824 layer->fNext = fMCRec->fTopLayer;
825 fMCRec->fLayer = layer;
826 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
827
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000828 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 return count;
830}
831
832int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
833 SaveFlags flags) {
834 if (0xFF == alpha) {
835 return this->saveLayer(bounds, NULL, flags);
836 } else {
837 SkPaint tmpPaint;
838 tmpPaint.setAlpha(alpha);
839 return this->saveLayer(bounds, &tmpPaint, flags);
840 }
841}
842
843void SkCanvas::restore() {
844 // check for underflow
845 if (fMCStack.count() > 1) {
846 this->internalRestore();
847 }
848}
849
850void SkCanvas::internalRestore() {
851 SkASSERT(fMCStack.count() != 0);
852
853 fDeviceCMDirty = true;
854 fLocalBoundsCompareTypeDirty = true;
855
reed@google.com5c3d1472011-02-22 19:12:23 +0000856 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000857 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000858 DeviceCM* layer = fMCRec->fLayer; // may be null
859 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
860 fMCRec->fLayer = NULL;
861
862 // now do the normal restore()
863 fMCRec->~MCRec(); // balanced in save()
864 fMCStack.pop_back();
865 fMCRec = (MCRec*)fMCStack.back();
866
867 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
868 since if we're being recorded, we don't want to record this (the
869 recorder will have already recorded the restore).
870 */
871 if (NULL != layer) {
872 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000873 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000874 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
875 layer->fPaint);
876 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000878
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000879 SkASSERT(fSaveLayerCount > 0);
880 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 }
882 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000883 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000884
885 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000886}
887
888int SkCanvas::getSaveCount() const {
889 return fMCStack.count();
890}
891
892void SkCanvas::restoreToCount(int count) {
893 // sanity check
894 if (count < 1) {
895 count = 1;
896 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000897
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000898 int n = this->getSaveCount() - count;
899 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 this->restore();
901 }
902}
903
reed@google.com7c202932011-12-14 18:48:05 +0000904bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000905 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000906}
907
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908/////////////////////////////////////////////////////////////////////////////
909
910// can't draw it if its empty, or its too big for a fixed-point width or height
911static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000912 return bitmap.width() <= 0 || bitmap.height() <= 0
913#ifndef SK_ALLOW_OVER_32K_BITMAPS
914 || bitmap.width() > 32767 || bitmap.height() > 32767
915#endif
916 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917}
918
reed@android.comf2b98d62010-12-20 18:26:13 +0000919void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 const SkMatrix& matrix, const SkPaint* paint) {
921 if (reject_bitmap(bitmap)) {
922 return;
923 }
924
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000925 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000926 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000927 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000929 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930}
931
reed@google.com8926b162012-03-23 15:36:36 +0000932void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
933 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 SkPaint tmp;
935 if (NULL == paint) {
936 tmp.setDither(true);
937 paint = &tmp;
938 }
reed@google.com4b226022011-01-11 18:32:13 +0000939
reed@google.com8926b162012-03-23 15:36:36 +0000940 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000941 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000942 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000943 paint = &looper.paint();
944 SkImageFilter* filter = paint->getImageFilter();
945 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000946 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +0000947 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000948 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000949 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000950 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000951 SkPaint tmpUnfiltered(*paint);
952 tmpUnfiltered.setImageFilter(NULL);
953 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +0000954 }
955 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +0000956 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +0000957 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000958 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000959 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +0000960}
961
reed@google.com8926b162012-03-23 15:36:36 +0000962void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
963 const SkPaint* paint) {
964 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000965
reed@google.com8926b162012-03-23 15:36:36 +0000966 if (reject_bitmap(bitmap)) {
967 return;
968 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000969
reed@google.com8926b162012-03-23 15:36:36 +0000970 SkPaint tmp;
971 if (NULL == paint) {
972 paint = &tmp;
973 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000974
reed@google.com8926b162012-03-23 15:36:36 +0000975 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000976
reed@google.com8926b162012-03-23 15:36:36 +0000977 while (iter.next()) {
978 paint = &looper.paint();
979 SkImageFilter* filter = paint->getImageFilter();
980 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
981 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +0000982 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +0000983 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000984 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
985 &dst, &pos)) {
986 SkPaint tmpUnfiltered(*paint);
987 tmpUnfiltered.setImageFilter(NULL);
988 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
989 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +0000990 }
991 } else {
992 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
993 }
994 }
995 LOOPER_END
996}
997
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998/////////////////////////////////////////////////////////////////////////////
999
1000bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1001 fDeviceCMDirty = true;
1002 fLocalBoundsCompareTypeDirty = true;
1003 return fMCRec->fMatrix->preTranslate(dx, dy);
1004}
1005
1006bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1007 fDeviceCMDirty = true;
1008 fLocalBoundsCompareTypeDirty = true;
1009 return fMCRec->fMatrix->preScale(sx, sy);
1010}
1011
1012bool SkCanvas::rotate(SkScalar degrees) {
1013 fDeviceCMDirty = true;
1014 fLocalBoundsCompareTypeDirty = true;
1015 return fMCRec->fMatrix->preRotate(degrees);
1016}
1017
1018bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1019 fDeviceCMDirty = true;
1020 fLocalBoundsCompareTypeDirty = true;
1021 return fMCRec->fMatrix->preSkew(sx, sy);
1022}
1023
1024bool SkCanvas::concat(const SkMatrix& matrix) {
1025 fDeviceCMDirty = true;
1026 fLocalBoundsCompareTypeDirty = true;
1027 return fMCRec->fMatrix->preConcat(matrix);
1028}
1029
1030void SkCanvas::setMatrix(const SkMatrix& matrix) {
1031 fDeviceCMDirty = true;
1032 fLocalBoundsCompareTypeDirty = true;
1033 *fMCRec->fMatrix = matrix;
1034}
1035
1036// this is not virtual, so it must call a virtual method so that subclasses
1037// will see its action
1038void SkCanvas::resetMatrix() {
1039 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001040
reed@android.com8a1c16f2008-12-17 15:59:43 +00001041 matrix.reset();
1042 this->setMatrix(matrix);
1043}
1044
1045//////////////////////////////////////////////////////////////////////////////
1046
reed@google.comc42d35d2011-10-12 11:57:42 +00001047bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001048#ifdef SK_ENABLE_CLIP_QUICKREJECT
1049 if (SkRegion::kIntersect_Op == op) {
1050 if (fMCRec->fRasterClip->isEmpty()) {
1051 return false;
1052 }
1053
reed@google.com3b3e8952012-08-16 20:53:31 +00001054 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001055 fDeviceCMDirty = true;
1056 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001057
1058 fClipStack.clipEmpty();
1059 return fMCRec->fRasterClip->setEmpty();
1060 }
1061 }
1062#endif
1063
reed@google.com5c3d1472011-02-22 19:12:23 +00001064 AutoValidateClip avc(this);
1065
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066 fDeviceCMDirty = true;
1067 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001068 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001069
1070 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001071 // for these simpler matrices, we can stay a rect ever after applying
1072 // the matrix. This means we don't have to a) make a path, and b) tell
1073 // the region code to scan-convert the path, only to discover that it
1074 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076
1077 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001078 fClipStack.clipDevRect(r, op, doAA);
1079 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001081 // since we're rotate or some such thing, we convert the rect to a path
1082 // and clip against that, since it can handle any matrix. However, to
1083 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1084 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 SkPath path;
1086
1087 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001088 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 }
1090}
1091
reed@google.com00177082011-10-12 14:34:30 +00001092static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001093 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001094 // base is used to limit the size (and therefore memory allocation) of the
1095 // region that results from scan converting devPath.
1096 SkRegion base;
1097
reed@google.com819c9212011-02-23 18:56:55 +00001098 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001099 // since we are intersect, we can do better (tighter) with currRgn's
1100 // bounds, than just using the device. However, if currRgn is complex,
1101 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001102 if (currClip->isRect()) {
1103 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001104 } else {
reed@google.com00177082011-10-12 14:34:30 +00001105 base.setRect(currClip->getBounds());
1106 SkRasterClip clip;
1107 clip.setPath(devPath, base, doAA);
1108 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001109 }
reed@google.com819c9212011-02-23 18:56:55 +00001110 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001111 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001112 if (!device) {
1113 return currClip->setEmpty();
1114 }
1115
junov@chromium.orga907ac32012-02-24 21:54:07 +00001116 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001117
1118 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001119 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001120 } else {
reed@google.com00177082011-10-12 14:34:30 +00001121 SkRasterClip clip;
1122 clip.setPath(devPath, base, doAA);
1123 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001124 }
1125 }
1126}
1127
reed@google.comc42d35d2011-10-12 11:57:42 +00001128bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001129#ifdef SK_ENABLE_CLIP_QUICKREJECT
1130 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1131 if (fMCRec->fRasterClip->isEmpty()) {
1132 return false;
1133 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001134
reed@google.com3b3e8952012-08-16 20:53:31 +00001135 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001136 fDeviceCMDirty = true;
1137 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001138
reed@google.comda17f752012-08-16 18:27:05 +00001139 fClipStack.clipEmpty();
1140 return fMCRec->fRasterClip->setEmpty();
1141 }
1142 }
1143#endif
1144
reed@google.com5c3d1472011-02-22 19:12:23 +00001145 AutoValidateClip avc(this);
1146
reed@android.com8a1c16f2008-12-17 15:59:43 +00001147 fDeviceCMDirty = true;
1148 fLocalBoundsCompareTypeDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +00001149 doAA &= fAllowSoftClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150
1151 SkPath devPath;
1152 path.transform(*fMCRec->fMatrix, &devPath);
1153
reed@google.comfe701122011-11-08 19:41:23 +00001154 // Check if the transfomation, or the original path itself
1155 // made us empty. Note this can also happen if we contained NaN
1156 // values. computing the bounds detects this, and will set our
1157 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1158 if (devPath.getBounds().isEmpty()) {
1159 // resetting the path will remove any NaN or other wanky values
1160 // that might upset our scan converter.
1161 devPath.reset();
1162 }
1163
reed@google.com5c3d1472011-02-22 19:12:23 +00001164 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001165 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001166
reed@google.com00177082011-10-12 14:34:30 +00001167 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001168}
1169
1170bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001171 AutoValidateClip avc(this);
1172
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 fDeviceCMDirty = true;
1174 fLocalBoundsCompareTypeDirty = true;
1175
reed@google.com5c3d1472011-02-22 19:12:23 +00001176 // todo: signal fClipStack that we have a region, and therefore (I guess)
1177 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001178 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001179
reed@google.com00177082011-10-12 14:34:30 +00001180 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181}
1182
reed@google.com819c9212011-02-23 18:56:55 +00001183#ifdef SK_DEBUG
1184void SkCanvas::validateClip() const {
1185 // construct clipRgn from the clipstack
1186 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001187 if (!device) {
1188 SkASSERT(this->getTotalClip().isEmpty());
1189 return;
1190 }
1191
reed@google.com819c9212011-02-23 18:56:55 +00001192 SkIRect ir;
1193 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001194 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001195
robertphillips@google.com80214e22012-07-20 15:33:18 +00001196 SkClipStack::B2TIter iter(fClipStack);
1197 const SkClipStack::B2TIter::Clip* clip;
reed@google.com819c9212011-02-23 18:56:55 +00001198 while ((clip = iter.next()) != NULL) {
1199 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001200 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001201 } else if (clip->fRect) {
1202 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001203 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001204 } else {
reed@google.com00177082011-10-12 14:34:30 +00001205 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001206 }
1207 }
1208
reed@google.com6f8f2922011-03-04 22:27:10 +00001209#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001210 // now compare against the current rgn
1211 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001212 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001213#endif
reed@google.com819c9212011-02-23 18:56:55 +00001214}
1215#endif
1216
reed@google.com90c07ea2012-04-13 13:50:27 +00001217void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001218 SkClipStack::B2TIter iter(fClipStack);
1219 const SkClipStack::B2TIter::Clip* clip;
reed@google.com90c07ea2012-04-13 13:50:27 +00001220
robertphillips@google.com7460b372012-04-25 16:54:51 +00001221 SkRect empty = { 0, 0, 0, 0 };
reed@google.com90c07ea2012-04-13 13:50:27 +00001222 while ((clip = iter.next()) != NULL) {
1223 if (clip->fPath) {
1224 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1225 } else if (clip->fRect) {
1226 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1227 } else {
1228 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1229 }
1230 }
1231}
1232
reed@google.com5c3d1472011-02-22 19:12:23 +00001233///////////////////////////////////////////////////////////////////////////////
1234
reed@google.com3b3e8952012-08-16 20:53:31 +00001235void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001237
reed@google.com3b3e8952012-08-16 20:53:31 +00001238 if (!this->getClipBounds(&r)) {
1239 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001241 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1242 SkScalarToCompareType(r.fTop),
1243 SkScalarToCompareType(r.fRight),
1244 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245 }
1246}
1247
reed@google.com3b3e8952012-08-16 20:53:31 +00001248bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001249
reed@google.com16078632011-12-06 18:56:37 +00001250 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001251 return true;
1252
reed@google.com00177082011-10-12 14:34:30 +00001253 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001254 return true;
1255 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256
tomhudson@google.com8d430182011-06-06 19:11:19 +00001257 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001258 SkRect dst;
1259 fMCRec->fMatrix->mapRect(&dst, rect);
1260 SkIRect idst;
1261 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001262 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001263 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001264 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001265
reed@android.coma380ae42009-07-21 01:17:02 +00001266 // for speed, do the most likely reject compares first
1267 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1268 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1269 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1270 return true;
1271 }
1272 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1273 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1274 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1275 return true;
1276 }
1277 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279}
1280
reed@google.com3b3e8952012-08-16 20:53:31 +00001281bool SkCanvas::quickReject(const SkPath& path) const {
1282 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283}
1284
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001285static inline int pinIntForScalar(int x) {
1286#ifdef SK_SCALAR_IS_FIXED
1287 if (x < SK_MinS16) {
1288 x = SK_MinS16;
1289 } else if (x > SK_MaxS16) {
1290 x = SK_MaxS16;
1291 }
1292#endif
1293 return x;
1294}
1295
reed@google.com3b3e8952012-08-16 20:53:31 +00001296bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001297 SkIRect ibounds;
1298 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 return false;
1300 }
1301
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001302 SkMatrix inverse;
1303 // if we can't invert the CTM, we can't return local clip bounds
1304 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001305 if (bounds) {
1306 bounds->setEmpty();
1307 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001308 return false;
1309 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001311 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001312 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001313 // adjust it outwards in case we are antialiasing
1314 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001315
1316 // SkRect::iset() will correctly assert if we pass a value out of range
1317 // (when SkScalar==fixed), so we pin to legal values. This does not
1318 // really returnt the correct answer, but its the best we can do given
1319 // that we've promised to return SkRect (even though we support devices
1320 // that can be larger than 32K in width or height).
1321 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1322 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001323 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001324 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001325 inverse.mapRect(bounds, r);
1326 }
1327 return true;
1328}
1329
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001330bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001331 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001332 if (clip.isEmpty()) {
1333 if (bounds) {
1334 bounds->setEmpty();
1335 }
1336 return false;
1337 }
1338
1339 if (NULL != bounds) {
1340 *bounds = clip.getBounds();
1341 }
1342 return true;
1343}
1344
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345const SkMatrix& SkCanvas::getTotalMatrix() const {
1346 return *fMCRec->fMatrix;
1347}
1348
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001349SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001350 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1351 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001352 return kComplex_ClipType;
1353}
1354
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001356 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357}
1358
bsalomon@google.come97f0852011-06-17 13:10:25 +00001359SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1360 int width, int height,
1361 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001362 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001363 if (device) {
1364 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1365 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001366 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001367 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001368 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369}
1370
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001371SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001372 int width, int height,
1373 bool isOpaque) {
1374 SkDevice* device = this->getDevice();
1375 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001376 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001377 } else {
1378 return NULL;
1379 }
1380}
1381
1382
reed@android.com8a1c16f2008-12-17 15:59:43 +00001383//////////////////////////////////////////////////////////////////////////////
1384// These are the virtual drawing methods
1385//////////////////////////////////////////////////////////////////////////////
1386
reed@google.com2a981812011-04-14 18:59:28 +00001387void SkCanvas::clear(SkColor color) {
1388 SkDrawIter iter(this);
1389
1390 while (iter.next()) {
1391 iter.fDevice->clear(color);
1392 }
1393}
1394
reed@android.com8a1c16f2008-12-17 15:59:43 +00001395void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001396 this->internalDrawPaint(paint);
1397}
1398
1399void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001400 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401
1402 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001403 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 }
1405
reed@google.com4e2b3d32011-04-07 14:18:59 +00001406 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407}
1408
1409void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1410 const SkPaint& paint) {
1411 if ((long)count <= 0) {
1412 return;
1413 }
1414
reed@google.coma584aed2012-05-16 14:06:02 +00001415 if (paint.canComputeFastBounds()) {
1416 SkRect r;
1417 // special-case 2 points (common for drawing a single line)
1418 if (2 == count) {
1419 r.set(pts[0], pts[1]);
1420 } else {
1421 r.set(pts, count);
1422 }
1423 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001424 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001425 return;
1426 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001427 }
reed@google.coma584aed2012-05-16 14:06:02 +00001428
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 SkASSERT(pts != NULL);
1430
reed@google.com4e2b3d32011-04-07 14:18:59 +00001431 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001432
reed@android.com8a1c16f2008-12-17 15:59:43 +00001433 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001434 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435 }
reed@google.com4b226022011-01-11 18:32:13 +00001436
reed@google.com4e2b3d32011-04-07 14:18:59 +00001437 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001438}
1439
1440void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1441 if (paint.canComputeFastBounds()) {
1442 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001443 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 return;
1445 }
1446 }
reed@google.com4b226022011-01-11 18:32:13 +00001447
reed@google.com4e2b3d32011-04-07 14:18:59 +00001448 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449
1450 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001451 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452 }
1453
reed@google.com4e2b3d32011-04-07 14:18:59 +00001454 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455}
1456
1457void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001458 if (!path.isFinite()) {
1459 return;
1460 }
1461
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001462 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001463 SkRect storage;
1464 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001465 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466 return;
1467 }
1468 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001469 if (path.isEmpty()) {
1470 if (path.isInverseFillType()) {
1471 this->internalDrawPaint(paint);
1472 }
1473 return;
1474 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475
reed@google.com4e2b3d32011-04-07 14:18:59 +00001476 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477
1478 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001479 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480 }
1481
reed@google.com4e2b3d32011-04-07 14:18:59 +00001482 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483}
1484
1485void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1486 const SkPaint* paint) {
1487 SkDEBUGCODE(bitmap.validate();)
1488
reed@google.com3d608122011-11-21 15:16:16 +00001489 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001490 SkRect bounds = {
1491 x, y,
1492 x + SkIntToScalar(bitmap.width()),
1493 y + SkIntToScalar(bitmap.height())
1494 };
1495 if (paint) {
1496 (void)paint->computeFastBounds(bounds, &bounds);
1497 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001498 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001499 return;
1500 }
1501 }
reed@google.com4b226022011-01-11 18:32:13 +00001502
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503 SkMatrix matrix;
1504 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001505 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506}
1507
reed@google.com9987ec32011-09-07 11:57:52 +00001508// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001509void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed@google.com9987ec32011-09-07 11:57:52 +00001510 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1512 return;
1513 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001514
reed@google.com3d608122011-11-21 15:16:16 +00001515 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001516 SkRect storage;
1517 const SkRect* bounds = &dst;
1518 if (paint) {
1519 bounds = &paint->computeFastBounds(dst, &storage);
1520 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001521 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001522 return;
1523 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524 }
reed@google.com3d608122011-11-21 15:16:16 +00001525
reed@google.com33535f32012-09-25 15:37:50 +00001526 SkLazyPaint lazy;
1527 if (NULL == paint) {
1528 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001530
reed@google.com33535f32012-09-25 15:37:50 +00001531 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001532
reed@google.com33535f32012-09-25 15:37:50 +00001533 while (iter.next()) {
1534 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
reed@android.comf2b98d62010-12-20 18:26:13 +00001535 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001536
reed@google.com33535f32012-09-25 15:37:50 +00001537 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538}
1539
reed@google.com71121732012-09-18 15:14:33 +00001540void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
1541 const SkRect& dst, const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00001542 SkDEBUGCODE(bitmap.validate();)
1543 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1544}
1545
reed@android.com8a1c16f2008-12-17 15:59:43 +00001546void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1547 const SkPaint* paint) {
1548 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001549 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001550}
1551
reed@android.comf2b98d62010-12-20 18:26:13 +00001552void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1553 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001555
reed@google.com4e2b3d32011-04-07 14:18:59 +00001556 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001557
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001559 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001560 }
reed@android.com9b039062009-02-11 15:09:58 +00001561
reed@google.com4e2b3d32011-04-07 14:18:59 +00001562 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001563}
1564
reed@google.com9987ec32011-09-07 11:57:52 +00001565void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1566 const SkIRect& center, const SkRect& dst,
1567 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001568 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001569 SkRect storage;
1570 const SkRect* bounds = &dst;
1571 if (paint) {
1572 bounds = &paint->computeFastBounds(dst, &storage);
1573 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001574 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001575 return;
1576 }
1577 }
1578
reed@google.com9987ec32011-09-07 11:57:52 +00001579 const int32_t w = bitmap.width();
1580 const int32_t h = bitmap.height();
1581
1582 SkIRect c = center;
1583 // pin center to the bounds of the bitmap
1584 c.fLeft = SkMax32(0, center.fLeft);
1585 c.fTop = SkMax32(0, center.fTop);
1586 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1587 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1588
reed@google.com71121732012-09-18 15:14:33 +00001589 const SkScalar srcX[4] = {
1590 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), w
1591 };
1592 const SkScalar srcY[4] = {
1593 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), h
1594 };
reed@google.com9987ec32011-09-07 11:57:52 +00001595 SkScalar dstX[4] = {
1596 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1597 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1598 };
1599 SkScalar dstY[4] = {
1600 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1601 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1602 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001603
reed@google.com9987ec32011-09-07 11:57:52 +00001604 if (dstX[1] > dstX[2]) {
1605 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1606 dstX[2] = dstX[1];
1607 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001608
reed@google.com9987ec32011-09-07 11:57:52 +00001609 if (dstY[1] > dstY[2]) {
1610 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1611 dstY[2] = dstY[1];
1612 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001613
reed@google.com9987ec32011-09-07 11:57:52 +00001614 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001615 SkRect s, d;
1616
reed@google.com9987ec32011-09-07 11:57:52 +00001617 s.fTop = srcY[y];
1618 s.fBottom = srcY[y+1];
1619 d.fTop = dstY[y];
1620 d.fBottom = dstY[y+1];
1621 for (int x = 0; x < 3; x++) {
1622 s.fLeft = srcX[x];
1623 s.fRight = srcX[x+1];
1624 d.fLeft = dstX[x];
1625 d.fRight = dstX[x+1];
1626 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1627 }
1628 }
1629}
1630
1631void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1632 const SkRect& dst, const SkPaint* paint) {
1633 SkDEBUGCODE(bitmap.validate();)
1634
1635 // Need a device entry-point, so gpu can use a mesh
1636 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1637}
1638
reed@google.comf67e4cf2011-03-15 20:56:58 +00001639class SkDeviceFilteredPaint {
1640public:
1641 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1642 SkDevice::TextFlags flags;
1643 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001644 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001645 newPaint->setFlags(flags.fFlags);
1646 newPaint->setHinting(flags.fHinting);
1647 fPaint = newPaint;
1648 } else {
1649 fPaint = &paint;
1650 }
1651 }
1652
reed@google.comf67e4cf2011-03-15 20:56:58 +00001653 const SkPaint& paint() const { return *fPaint; }
1654
1655private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001656 const SkPaint* fPaint;
1657 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001658};
1659
bungeman@google.com52c748b2011-08-22 21:30:43 +00001660void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1661 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001662 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001663 draw.fDevice->drawRect(draw, r, paint);
1664 } else {
1665 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001666 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001667 draw.fDevice->drawRect(draw, r, p);
1668 }
1669}
1670
1671void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1672 const char text[], size_t byteLength,
1673 SkScalar x, SkScalar y) {
1674 SkASSERT(byteLength == 0 || text != NULL);
1675
1676 // nothing to draw
1677 if (text == NULL || byteLength == 0 ||
1678 draw.fClip->isEmpty() ||
1679 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1680 return;
1681 }
1682
1683 SkScalar width = 0;
1684 SkPoint start;
1685
1686 start.set(0, 0); // to avoid warning
1687 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1688 SkPaint::kStrikeThruText_Flag)) {
1689 width = paint.measureText(text, byteLength);
1690
1691 SkScalar offsetX = 0;
1692 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1693 offsetX = SkScalarHalf(width);
1694 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1695 offsetX = width;
1696 }
1697 start.set(x - offsetX, y);
1698 }
1699
1700 if (0 == width) {
1701 return;
1702 }
1703
1704 uint32_t flags = paint.getFlags();
1705
1706 if (flags & (SkPaint::kUnderlineText_Flag |
1707 SkPaint::kStrikeThruText_Flag)) {
1708 SkScalar textSize = paint.getTextSize();
1709 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1710 SkRect r;
1711
1712 r.fLeft = start.fX;
1713 r.fRight = start.fX + width;
1714
1715 if (flags & SkPaint::kUnderlineText_Flag) {
1716 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1717 start.fY);
1718 r.fTop = offset;
1719 r.fBottom = offset + height;
1720 DrawRect(draw, paint, r, textSize);
1721 }
1722 if (flags & SkPaint::kStrikeThruText_Flag) {
1723 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1724 start.fY);
1725 r.fTop = offset;
1726 r.fBottom = offset + height;
1727 DrawRect(draw, paint, r, textSize);
1728 }
1729 }
1730}
1731
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732void SkCanvas::drawText(const void* text, size_t byteLength,
1733 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001734 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735
1736 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001737 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001738 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001739 DrawTextDecorations(iter, dfp.paint(),
1740 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001741 }
1742
reed@google.com4e2b3d32011-04-07 14:18:59 +00001743 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744}
1745
1746void SkCanvas::drawPosText(const void* text, size_t byteLength,
1747 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001748 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001749
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001751 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001752 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001753 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754 }
reed@google.com4b226022011-01-11 18:32:13 +00001755
reed@google.com4e2b3d32011-04-07 14:18:59 +00001756 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757}
1758
1759void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1760 const SkScalar xpos[], SkScalar constY,
1761 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001762 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001763
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001765 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001767 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768 }
reed@google.com4b226022011-01-11 18:32:13 +00001769
reed@google.com4e2b3d32011-04-07 14:18:59 +00001770 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771}
1772
1773void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1774 const SkPath& path, const SkMatrix* matrix,
1775 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001776 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777
1778 while (iter.next()) {
1779 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001780 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 }
1782
reed@google.com4e2b3d32011-04-07 14:18:59 +00001783 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784}
1785
djsollen@google.com56c69772011-11-08 19:00:26 +00001786#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001787void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1788 const SkPoint pos[], const SkPaint& paint,
1789 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001790 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001791
1792 while (iter.next()) {
1793 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001794 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001795 }
1796
reed@google.com4e2b3d32011-04-07 14:18:59 +00001797 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001798}
1799#endif
1800
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1802 const SkPoint verts[], const SkPoint texs[],
1803 const SkColor colors[], SkXfermode* xmode,
1804 const uint16_t indices[], int indexCount,
1805 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001806 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001807
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808 while (iter.next()) {
1809 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001810 colors, xmode, indices, indexCount,
1811 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 }
reed@google.com4b226022011-01-11 18:32:13 +00001813
reed@google.com4e2b3d32011-04-07 14:18:59 +00001814 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815}
1816
reed@android.comcb608442009-12-04 21:32:27 +00001817void SkCanvas::drawData(const void* data, size_t length) {
1818 // do nothing. Subclasses may do something with the data
1819}
1820
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821//////////////////////////////////////////////////////////////////////////////
1822// These methods are NOT virtual, and therefore must call back into virtual
1823// methods, rather than actually drawing themselves.
1824//////////////////////////////////////////////////////////////////////////////
1825
1826void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001827 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828 SkPaint paint;
1829
1830 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001831 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001832 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 }
1834 this->drawPaint(paint);
1835}
1836
reed@android.com845fdac2009-06-23 03:01:32 +00001837void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838 SkPaint paint;
1839
1840 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001841 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001842 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843 }
1844 this->drawPaint(paint);
1845}
1846
1847void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1848 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001849
reed@android.com8a1c16f2008-12-17 15:59:43 +00001850 pt.set(x, y);
1851 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1852}
1853
1854void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1855 SkPoint pt;
1856 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001857
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858 pt.set(x, y);
1859 paint.setColor(color);
1860 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1861}
1862
1863void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1864 const SkPaint& paint) {
1865 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001866
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 pts[0].set(x0, y0);
1868 pts[1].set(x1, y1);
1869 this->drawPoints(kLines_PointMode, 2, pts, paint);
1870}
1871
1872void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1873 SkScalar right, SkScalar bottom,
1874 const SkPaint& paint) {
1875 SkRect r;
1876
1877 r.set(left, top, right, bottom);
1878 this->drawRect(r, paint);
1879}
1880
1881void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1882 const SkPaint& paint) {
1883 if (radius < 0) {
1884 radius = 0;
1885 }
1886
1887 SkRect r;
1888 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001889
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 if (paint.canComputeFastBounds()) {
1891 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001892 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001893 return;
1894 }
1895 }
reed@google.com4b226022011-01-11 18:32:13 +00001896
reed@android.com8a1c16f2008-12-17 15:59:43 +00001897 SkPath path;
1898 path.addOval(r);
1899 this->drawPath(path, paint);
1900}
1901
1902void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1903 const SkPaint& paint) {
1904 if (rx > 0 && ry > 0) {
1905 if (paint.canComputeFastBounds()) {
1906 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001907 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001908 return;
1909 }
1910 }
1911
1912 SkPath path;
1913 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1914 this->drawPath(path, paint);
1915 } else {
1916 this->drawRect(r, paint);
1917 }
1918}
1919
1920void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1921 if (paint.canComputeFastBounds()) {
1922 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001923 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001924 return;
1925 }
1926 }
1927
1928 SkPath path;
1929 path.addOval(oval);
1930 this->drawPath(path, paint);
1931}
1932
1933void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
1934 SkScalar sweepAngle, bool useCenter,
1935 const SkPaint& paint) {
1936 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
1937 this->drawOval(oval, paint);
1938 } else {
1939 SkPath path;
1940 if (useCenter) {
1941 path.moveTo(oval.centerX(), oval.centerY());
1942 }
1943 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
1944 if (useCenter) {
1945 path.close();
1946 }
1947 this->drawPath(path, paint);
1948 }
1949}
1950
1951void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
1952 const SkPath& path, SkScalar hOffset,
1953 SkScalar vOffset, const SkPaint& paint) {
1954 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001955
reed@android.com8a1c16f2008-12-17 15:59:43 +00001956 matrix.setTranslate(hOffset, vOffset);
1957 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
1958}
1959
reed@android.comf76bacf2009-05-13 14:00:33 +00001960///////////////////////////////////////////////////////////////////////////////
1961
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001963 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001964}
1965
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966///////////////////////////////////////////////////////////////////////////////
1967///////////////////////////////////////////////////////////////////////////////
1968
1969SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00001970 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001971
1972 SkASSERT(canvas);
1973
1974 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
1975 fDone = !fImpl->next();
1976}
1977
1978SkCanvas::LayerIter::~LayerIter() {
1979 fImpl->~SkDrawIter();
1980}
1981
1982void SkCanvas::LayerIter::next() {
1983 fDone = !fImpl->next();
1984}
1985
1986SkDevice* SkCanvas::LayerIter::device() const {
1987 return fImpl->getDevice();
1988}
1989
1990const SkMatrix& SkCanvas::LayerIter::matrix() const {
1991 return fImpl->getMatrix();
1992}
1993
1994const SkPaint& SkCanvas::LayerIter::paint() const {
1995 const SkPaint* paint = fImpl->getPaint();
1996 if (NULL == paint) {
1997 paint = &fDefaultPaint;
1998 }
1999 return *paint;
2000}
2001
2002const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2003int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2004int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002005
2006///////////////////////////////////////////////////////////////////////////////
2007
2008SkCanvas::ClipVisitor::~ClipVisitor() { }
reed@google.com97af1a62012-08-28 12:19:02 +00002009
2010