blob: 4e4d430f21ff1a0632a71e6056510347c0ca2e5a [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
11#include "SkBounder.h"
12#include "SkDevice.h"
13#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
16#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000017#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkScalarCompare.h"
19#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000020#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000021#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023
reed@google.com82ce2b82012-06-26 17:43:26 +000024SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000025SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000026SK_DEFINE_INST_COUNT(SkDrawFilter)
27
reed@google.comda17f752012-08-16 18:27:05 +000028// experimental for faster tiled drawing...
29//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000030
reed@android.com8a1c16f2008-12-17 15:59:43 +000031//#define SK_TRACE_SAVERESTORE
32
33#ifdef SK_TRACE_SAVERESTORE
34 static int gLayerCounter;
35 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
36 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
37
38 static int gRecCounter;
39 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
40 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
41
42 static int gCanvasCounter;
43 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
44 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
45#else
46 #define inc_layer()
47 #define dec_layer()
48 #define inc_rec()
49 #define dec_rec()
50 #define inc_canvas()
51 #define dec_canvas()
52#endif
53
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000054typedef SkTLazy<SkPaint> SkLazyPaint;
55
reed@android.com8a1c16f2008-12-17 15:59:43 +000056///////////////////////////////////////////////////////////////////////////////
57// Helpers for computing fast bounds for quickReject tests
58
59static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
60 return paint != NULL && paint->isAntiAlias() ?
61 SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
62}
63
64///////////////////////////////////////////////////////////////////////////////
65
66/* This is the record we keep for each SkDevice that the user installs.
67 The clip/matrix/proc are fields that reflect the top of the save/restore
68 stack. Whenever the canvas changes, it marks a dirty flag, and then before
69 these are used (assuming we're not on a layer) we rebuild these cache
70 values: they reflect the top of the save stack, but translated and clipped
71 by the device's XY offset and bitmap-bounds.
72*/
73struct DeviceCM {
74 DeviceCM* fNext;
75 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000076 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000078 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000079 // optional, related to canvas' external matrix
80 const SkMatrix* fMVMatrix;
81 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000082
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000083 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 : fNext(NULL) {
85 if (NULL != device) {
86 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000087 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 }
reed@google.com4b226022011-01-11 18:32:13 +000089 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000092
bungeman@google.com88edf1e2011-08-08 19:41:56 +000093 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000095 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 fDevice->unref();
97 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000098 SkDELETE(fPaint);
99 }
reed@google.com4b226022011-01-11 18:32:13 +0000100
reed@google.com045e62d2011-10-24 12:19:46 +0000101 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
102 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000103 int x = fDevice->getOrigin().x();
104 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 int width = fDevice->width();
106 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 if ((x | y) == 0) {
109 fMatrix = &totalMatrix;
110 fClip = totalClip;
111 } else {
112 fMatrixStorage = totalMatrix;
113 fMatrixStorage.postTranslate(SkIntToScalar(-x),
114 SkIntToScalar(-y));
115 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 totalClip.translate(-x, -y, &fClip);
118 }
119
reed@google.com045e62d2011-10-24 12:19:46 +0000120 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121
122 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000125 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 SkRegion::kDifference_Op);
127 }
reed@google.com4b226022011-01-11 18:32:13 +0000128
reed@google.com045e62d2011-10-24 12:19:46 +0000129 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130
131#ifdef SK_DEBUG
132 if (!fClip.isEmpty()) {
133 SkIRect deviceR;
134 deviceR.set(0, 0, width, height);
135 SkASSERT(deviceR.contains(fClip.getBounds()));
136 }
137#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000138 // default is to assume no external matrix
139 fMVMatrix = NULL;
140 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000142
143 // can only be called after calling updateMC()
144 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
145 fMVMatrixStorage.setConcat(extI, *fMatrix);
146 fMVMatrix = &fMVMatrixStorage;
147 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
148 }
149
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000151 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000152};
153
154/* This is the record we keep for each save/restore level in the stack.
155 Since a level optionally copies the matrix and/or stack, we have pointers
156 for these fields. If the value is copied for this level, the copy is
157 stored in the ...Storage field, and the pointer points to that. If the
158 value is not copied for this level, we ignore ...Storage, and just point
159 at the corresponding value in the previous level in the stack.
160*/
161class SkCanvas::MCRec {
162public:
163 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000164 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
165 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
166 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 DeviceCM* fLayer;
169 /* If there are any layers in the stack, this points to the top-most
170 one that is at or below this level in the stack (so we know what
171 bitmap/device to draw into from this level. This value is NOT
172 reference counted, since the real owner is either our fLayer field,
173 or a previous one in a lower level.)
174 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000175 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176
177 MCRec(const MCRec* prev, int flags) {
178 if (NULL != prev) {
179 if (flags & SkCanvas::kMatrix_SaveFlag) {
180 fMatrixStorage = *prev->fMatrix;
181 fMatrix = &fMatrixStorage;
182 } else {
183 fMatrix = prev->fMatrix;
184 }
reed@google.com4b226022011-01-11 18:32:13 +0000185
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000187 fRasterClipStorage = *prev->fRasterClip;
188 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 } else {
reed@google.com00177082011-10-12 14:34:30 +0000190 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 }
192
193 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000194 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195
196 fTopLayer = prev->fTopLayer;
197 } else { // no prev
198 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000199
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000201 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 fFilter = NULL;
203 fTopLayer = NULL;
204 }
205 fLayer = NULL;
206
207 // don't bother initializing fNext
208 inc_rec();
209 }
210 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000211 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 SkDELETE(fLayer);
213 dec_rec();
214 }
reed@google.com4b226022011-01-11 18:32:13 +0000215
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216private:
reed@google.com00177082011-10-12 14:34:30 +0000217 SkMatrix fMatrixStorage;
218 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219};
220
221class SkDrawIter : public SkDraw {
222public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000223 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000224 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000225 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 canvas->updateDeviceCMCache();
227
reed@google.com90c07ea2012-04-13 13:50:27 +0000228 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229 fBounder = canvas->getBounder();
230 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000231 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 }
reed@google.com4b226022011-01-11 18:32:13 +0000233
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 bool next() {
235 // skip over recs with empty clips
236 if (fSkipEmptyClips) {
237 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
238 fCurrLayer = fCurrLayer->fNext;
239 }
240 }
241
reed@google.comf68c5e22012-02-24 16:38:58 +0000242 const DeviceCM* rec = fCurrLayer;
243 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
245 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000246 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
247 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 fDevice = rec->fDevice;
249 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000251 fMVMatrix = rec->fMVMatrix;
252 fExtMatrix = rec->fExtMatrix;
253 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254
255 fCurrLayer = rec->fNext;
256 if (fBounder) {
257 fBounder->setClip(fClip);
258 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000260
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000261 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262 return true;
263 }
264 return false;
265 }
reed@google.com4b226022011-01-11 18:32:13 +0000266
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000268 int getX() const { return fDevice->getOrigin().x(); }
269 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 const SkMatrix& getMatrix() const { return *fMatrix; }
271 const SkRegion& getClip() const { return *fClip; }
272 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000273
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274private:
275 SkCanvas* fCanvas;
276 const DeviceCM* fCurrLayer;
277 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000278 SkBool8 fSkipEmptyClips;
279
280 typedef SkDraw INHERITED;
281};
282
283/////////////////////////////////////////////////////////////////////////////
284
285class AutoDrawLooper {
286public:
reed@google.com8926b162012-03-23 15:36:36 +0000287 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
288 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000289 fCanvas = canvas;
290 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000292 fPaint = NULL;
293 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000294 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000295 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296
reed@google.com8926b162012-03-23 15:36:36 +0000297 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
298 SkPaint tmp;
299 tmp.setImageFilter(fOrigPaint.getImageFilter());
300 // it would be nice if we had a guess at the bounds, instead of null
301 (void)canvas->internalSaveLayer(NULL, &tmp,
302 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
303 // we'll clear the imageFilter for the actual draws in next(), so
304 // it will only be applied during the restore().
305 fDoClearImageFilter = true;
306 }
307
reed@google.com4e2b3d32011-04-07 14:18:59 +0000308 if (fLooper) {
309 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000310 fIsSimple = false;
311 } else {
312 // can we be marked as simple?
313 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000314 }
315 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000318 if (fDoClearImageFilter) {
319 fCanvas->internalRestore();
320 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000321 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000323
reed@google.com4e2b3d32011-04-07 14:18:59 +0000324 const SkPaint& paint() const {
325 SkASSERT(fPaint);
326 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000328
reed@google.com129ec222012-05-15 13:24:09 +0000329 bool next(SkDrawFilter::Type drawType) {
330 if (fDone) {
331 return false;
332 } else if (fIsSimple) {
333 fDone = true;
334 fPaint = &fOrigPaint;
335 return !fPaint->nothingToDraw();
336 } else {
337 return this->doNext(drawType);
338 }
339 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000342 SkLazyPaint fLazyPaint;
343 SkCanvas* fCanvas;
344 const SkPaint& fOrigPaint;
345 SkDrawLooper* fLooper;
346 SkDrawFilter* fFilter;
347 const SkPaint* fPaint;
348 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000349 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000350 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000351 bool fIsSimple;
352
353 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354};
355
reed@google.com129ec222012-05-15 13:24:09 +0000356bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000357 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000358 SkASSERT(!fIsSimple);
359 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
360
361 SkPaint* paint = fLazyPaint.set(fOrigPaint);
362
363 if (fDoClearImageFilter) {
364 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000365 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000366
reed@google.com129ec222012-05-15 13:24:09 +0000367 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000368 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000369 return false;
370 }
371 if (fFilter) {
372 fFilter->filter(paint, drawType);
373 if (NULL == fLooper) {
374 // no looper means we only draw once
375 fDone = true;
376 }
377 }
378 fPaint = paint;
379
380 // if we only came in here for the imagefilter, mark us as done
381 if (!fLooper && !fFilter) {
382 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000383 }
384
385 // call this after any possible paint modifiers
386 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000387 fPaint = NULL;
388 return false;
389 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000390 return true;
391}
392
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393/* Stack helper for managing a SkBounder. In the destructor, if we were
394 given a bounder, we call its commit() method, signifying that we are
395 done accumulating bounds for that draw.
396*/
397class SkAutoBounderCommit {
398public:
399 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
400 ~SkAutoBounderCommit() {
401 if (NULL != fBounder) {
402 fBounder->commit();
403 }
404 }
405private:
406 SkBounder* fBounder;
407};
408
409#include "SkColorPriv.h"
410
411class AutoValidator {
412public:
413 AutoValidator(SkDevice* device) : fDevice(device) {}
414 ~AutoValidator() {
415#ifdef SK_DEBUG
416 const SkBitmap& bm = fDevice->accessBitmap(false);
417 if (bm.config() == SkBitmap::kARGB_4444_Config) {
418 for (int y = 0; y < bm.height(); y++) {
419 const SkPMColor16* p = bm.getAddr16(0, y);
420 for (int x = 0; x < bm.width(); x++) {
421 SkPMColor16 c = p[x];
422 SkPMColor16Assert(c);
423 }
424 }
425 }
426#endif
427 }
428private:
429 SkDevice* fDevice;
430};
431
432////////// macros to place around the internal draw calls //////////////////
433
reed@google.com8926b162012-03-23 15:36:36 +0000434#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
435/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
436 AutoDrawLooper looper(this, paint, true); \
437 while (looper.next(type)) { \
438 SkAutoBounderCommit ac(fBounder); \
439 SkDrawIter iter(this);
440
reed@google.com4e2b3d32011-04-07 14:18:59 +0000441#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000443 AutoDrawLooper looper(this, paint); \
444 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 SkAutoBounderCommit ac(fBounder); \
446 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000447
reed@google.com4e2b3d32011-04-07 14:18:59 +0000448#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449
450////////////////////////////////////////////////////////////////////////////
451
452SkDevice* SkCanvas::init(SkDevice* device) {
453 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000454 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455 fLocalBoundsCompareTypeDirty = true;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000456 fLocalBoundsCompareTypeBW.setEmpty();
reed@android.comba09de42010-02-05 20:46:05 +0000457 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com199f1082009-06-10 02:12:47 +0000458 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000459 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000460 fSaveLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461
462 fMCRec = (MCRec*)fMCStack.push_back();
463 new (fMCRec) MCRec(NULL, 0);
464
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000465 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 fMCRec->fTopLayer = fMCRec->fLayer;
467 fMCRec->fNext = NULL;
468
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000469 fExternalMatrix.reset();
470 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000471 fUseExternalMatrix = false;
472
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473 return this->setDevice(device);
474}
475
reed@google.comcde92112011-07-06 20:00:52 +0000476SkCanvas::SkCanvas()
477: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000478 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000479
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000480 this->init(NULL);
481}
482
reed@android.com8a1c16f2008-12-17 15:59:43 +0000483SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000484 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000485 inc_canvas();
486
487 this->init(device);
488}
489
490SkCanvas::SkCanvas(const SkBitmap& bitmap)
491 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
492 inc_canvas();
493
reed@google.comcde92112011-07-06 20:00:52 +0000494 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495}
496
497SkCanvas::~SkCanvas() {
498 // free up the contents of our deque
499 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000500 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000501
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502 this->internalRestore(); // restore the last, since we're going away
503
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000504 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000505
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 dec_canvas();
507}
508
509SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
510 SkRefCnt_SafeAssign(fBounder, bounder);
511 return bounder;
512}
513
514SkDrawFilter* SkCanvas::getDrawFilter() const {
515 return fMCRec->fFilter;
516}
517
518SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
519 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
520 return filter;
521}
522
523///////////////////////////////////////////////////////////////////////////////
524
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000525void SkCanvas::flush() {
526 SkDevice* device = this->getDevice();
527 if (device) {
528 device->flush();
529 }
530}
531
reed@google.com210ce002011-11-01 14:24:23 +0000532SkISize SkCanvas::getDeviceSize() const {
533 SkDevice* d = this->getDevice();
534 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
535}
536
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537SkDevice* SkCanvas::getDevice() const {
538 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000539 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 SkASSERT(rec && rec->fLayer);
541 return rec->fLayer->fDevice;
542}
543
reed@google.com0b53d592012-03-19 18:26:34 +0000544SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
545 if (updateMatrixClip) {
546 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
547 }
reed@google.com9266fed2011-03-30 00:18:03 +0000548 return fMCRec->fTopLayer->fDevice;
549}
550
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551SkDevice* SkCanvas::setDevice(SkDevice* device) {
552 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000553 SkDeque::F2BIter iter(fMCStack);
554 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 SkASSERT(rec && rec->fLayer);
556 SkDevice* rootDevice = rec->fLayer->fDevice;
557
558 if (rootDevice == device) {
559 return device;
560 }
reed@google.com4b226022011-01-11 18:32:13 +0000561
reed@android.com8a1c16f2008-12-17 15:59:43 +0000562 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000563 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564 }
565 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000566 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000567 }
568
569 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
570 rootDevice = device;
571
572 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000573
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 /* Now we update our initial region to have the bounds of the new device,
575 and then intersect all of the clips in our stack with these bounds,
576 to ensure that we can't draw outside of the device's bounds (and trash
577 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000578
reed@android.com8a1c16f2008-12-17 15:59:43 +0000579 NOTE: this is only a partial-fix, since if the new device is larger than
580 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000581 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
583 reconstruct the correct clips, so this approximation will have to do.
584 The caller really needs to restore() back to the base if they want to
585 accurately take advantage of the new device bounds.
586 */
587
reed@google.com42aea282012-03-28 16:19:15 +0000588 SkIRect bounds;
589 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000591 } else {
592 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 }
reed@google.com42aea282012-03-28 16:19:15 +0000594 // now jam our 1st clip to be bounds, and intersect the rest with that
595 rec->fRasterClip->setRect(bounds);
596 while ((rec = (MCRec*)iter.next()) != NULL) {
597 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
598 }
599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 return device;
601}
602
reed@google.comaf951c92011-06-16 19:10:39 +0000603SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
604 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 device->unref();
606 return device;
607}
608
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000609bool SkCanvas::readPixels(SkBitmap* bitmap,
610 int x, int y,
611 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000612 SkDevice* device = this->getDevice();
613 if (!device) {
614 return false;
615 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000616 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000617}
618
bsalomon@google.comc6980972011-11-02 19:57:21 +0000619bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000620 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000621 if (!device) {
622 return false;
623 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000624
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000625 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000626 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000627 if (!bounds.intersect(srcRect)) {
628 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000629 }
630
631 SkBitmap tmp;
632 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
633 bounds.height());
634 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
635 bitmap->swap(tmp);
636 return true;
637 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000638 return false;
639 }
reed@google.com51df9e32010-12-23 19:29:18 +0000640}
641
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000642void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
643 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000644 SkDevice* device = this->getDevice();
645 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000646 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000647 }
648}
649
junov@google.com4370aed2012-01-18 16:21:08 +0000650SkCanvas* SkCanvas::canvasForDrawIter() {
651 return this;
652}
653
reed@android.com8a1c16f2008-12-17 15:59:43 +0000654//////////////////////////////////////////////////////////////////////////////
655
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656void SkCanvas::updateDeviceCMCache() {
657 if (fDeviceCMDirty) {
658 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000659 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000660 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000661
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000663 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000664 if (fUseExternalMatrix) {
665 layer->updateExternalMatrix(fExternalMatrix,
666 fExternalInverse);
667 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000669 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000671 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000672 if (fUseExternalMatrix) {
673 layer->updateExternalMatrix(fExternalMatrix,
674 fExternalInverse);
675 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 } while ((layer = layer->fNext) != NULL);
677 }
678 fDeviceCMDirty = false;
679 }
680}
681
reed@android.comf2b98d62010-12-20 18:26:13 +0000682void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000683 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000685 if (fLastDeviceToGainFocus != device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000686 device->gainFocus(matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000687 fLastDeviceToGainFocus = device;
688 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689}
690
691///////////////////////////////////////////////////////////////////////////////
692
693int SkCanvas::internalSave(SaveFlags flags) {
694 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000695
reed@android.com8a1c16f2008-12-17 15:59:43 +0000696 MCRec* newTop = (MCRec*)fMCStack.push_back();
697 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000698
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 newTop->fNext = fMCRec;
700 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000701
reed@google.com5c3d1472011-02-22 19:12:23 +0000702 fClipStack.save();
703 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
704
reed@android.com8a1c16f2008-12-17 15:59:43 +0000705 return saveCount;
706}
707
708int SkCanvas::save(SaveFlags flags) {
709 // call shared impl
710 return this->internalSave(flags);
711}
712
713#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
714#define C16MASK (1 << SkBitmap::kRGB_565_Config)
715#define C8MASK (1 << SkBitmap::kA8_Config)
716
717static SkBitmap::Config resolve_config(SkCanvas* canvas,
718 const SkIRect& bounds,
719 SkCanvas::SaveFlags flags,
720 bool* isOpaque) {
721 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
722
723#if 0
724 // loop through and union all the configs we may draw into
725 uint32_t configMask = 0;
726 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
727 {
728 SkDevice* device = canvas->getLayerDevice(i);
729 if (device->intersects(bounds))
730 configMask |= 1 << device->config();
731 }
732
733 // if the caller wants alpha or fullcolor, we can't return 565
734 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
735 SkCanvas::kHasAlphaLayer_SaveFlag))
736 configMask &= ~C16MASK;
737
738 switch (configMask) {
739 case C8MASK: // if we only have A8, return that
740 return SkBitmap::kA8_Config;
741
742 case C16MASK: // if we only have 565, return that
743 return SkBitmap::kRGB_565_Config;
744
745 default:
746 return SkBitmap::kARGB_8888_Config; // default answer
747 }
748#else
749 return SkBitmap::kARGB_8888_Config; // default answer
750#endif
751}
752
753static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
754 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
755}
756
junov@chromium.orga907ac32012-02-24 21:54:07 +0000757bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
758 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000759 SkIRect clipBounds;
760 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000761 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000762 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000763 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000764 if (NULL != bounds) {
765 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000766
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767 this->getTotalMatrix().mapRect(&r, *bounds);
768 r.roundOut(&ir);
769 // early exit if the layer's bounds are clipped out
770 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000771 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000772 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000773 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000774 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 }
776 } else { // no user bounds, so just use the clip
777 ir = clipBounds;
778 }
779
reed@google.com5c3d1472011-02-22 19:12:23 +0000780 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000781
reed@android.com8a1c16f2008-12-17 15:59:43 +0000782 // early exit if the clip is now empty
783 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000784 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000785 return false;
786 }
787
788 if (intersection) {
789 *intersection = ir;
790 }
791 return true;
792}
793
794int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
795 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000796 return this->internalSaveLayer(bounds, paint, flags, false);
797}
798
799int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
800 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000801 // do this before we create the layer. We don't call the public save() since
802 // that would invoke a possibly overridden virtual
803 int count = this->internalSave(flags);
804
805 fDeviceCMDirty = true;
806
807 SkIRect ir;
808 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809 return count;
810 }
811
reed@google.comb55deeb2012-01-06 14:43:09 +0000812 // Kill the imagefilter if our device doesn't allow it
813 SkLazyPaint lazyP;
814 if (paint && paint->getImageFilter()) {
815 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000816 if (justForImageFilter) {
817 // early exit if the layer was just for the imageFilter
818 return count;
819 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000820 SkPaint* p = lazyP.set(*paint);
821 p->setImageFilter(NULL);
822 paint = p;
823 }
824 }
825
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 bool isOpaque;
827 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
828
reed@google.com76dd2772012-01-05 21:15:07 +0000829 SkDevice* device;
830 if (paint && paint->getImageFilter()) {
831 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
832 isOpaque);
833 } else {
834 device = this->createLayerDevice(config, ir.width(), ir.height(),
835 isOpaque);
836 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000837 if (NULL == device) {
838 SkDebugf("Unable to create device for layer.");
839 return count;
840 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000841
reed@google.com6f8f2922011-03-04 22:27:10 +0000842 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000843 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 device->unref();
845
846 layer->fNext = fMCRec->fTopLayer;
847 fMCRec->fLayer = layer;
848 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
849
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000850 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 return count;
852}
853
854int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
855 SaveFlags flags) {
856 if (0xFF == alpha) {
857 return this->saveLayer(bounds, NULL, flags);
858 } else {
859 SkPaint tmpPaint;
860 tmpPaint.setAlpha(alpha);
861 return this->saveLayer(bounds, &tmpPaint, flags);
862 }
863}
864
865void SkCanvas::restore() {
866 // check for underflow
867 if (fMCStack.count() > 1) {
868 this->internalRestore();
869 }
870}
871
872void SkCanvas::internalRestore() {
873 SkASSERT(fMCStack.count() != 0);
874
875 fDeviceCMDirty = true;
876 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +0000877 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000878
reed@google.com5c3d1472011-02-22 19:12:23 +0000879 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000880 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 DeviceCM* layer = fMCRec->fLayer; // may be null
882 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
883 fMCRec->fLayer = NULL;
884
885 // now do the normal restore()
886 fMCRec->~MCRec(); // balanced in save()
887 fMCStack.pop_back();
888 fMCRec = (MCRec*)fMCStack.back();
889
890 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
891 since if we're being recorded, we don't want to record this (the
892 recorder will have already recorded the restore).
893 */
894 if (NULL != layer) {
895 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000896 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000897 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
898 layer->fPaint);
899 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000901
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000902 SkASSERT(fSaveLayerCount > 0);
903 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 }
905 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000906 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000907
908 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909}
910
911int SkCanvas::getSaveCount() const {
912 return fMCStack.count();
913}
914
915void SkCanvas::restoreToCount(int count) {
916 // sanity check
917 if (count < 1) {
918 count = 1;
919 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000920
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000921 int n = this->getSaveCount() - count;
922 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 this->restore();
924 }
925}
926
reed@google.com7c202932011-12-14 18:48:05 +0000927bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000928 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000929}
930
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931/////////////////////////////////////////////////////////////////////////////
932
933// can't draw it if its empty, or its too big for a fixed-point width or height
934static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000935 return bitmap.width() <= 0 || bitmap.height() <= 0
936#ifndef SK_ALLOW_OVER_32K_BITMAPS
937 || bitmap.width() > 32767 || bitmap.height() > 32767
938#endif
939 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940}
941
reed@android.comf2b98d62010-12-20 18:26:13 +0000942void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 const SkMatrix& matrix, const SkPaint* paint) {
944 if (reject_bitmap(bitmap)) {
945 return;
946 }
947
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000948 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000950 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000952 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953}
954
reed@google.com76dd2772012-01-05 21:15:07 +0000955#include "SkImageFilter.h"
956
957class DeviceImageFilterProxy : public SkImageFilter::Proxy {
958public:
959 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000960
reed@google.com8926b162012-03-23 15:36:36 +0000961 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
962 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
963 w, h, false);
964 }
965 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
966 return fDevice->canHandleImageFilter(filter);
967 }
968 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000969 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000970 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
971 return fDevice->filterImage(filter, src, ctm, result, offset);
972 }
973
reed@google.com76dd2772012-01-05 21:15:07 +0000974private:
975 SkDevice* fDevice;
976};
977
reed@google.com8926b162012-03-23 15:36:36 +0000978void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
979 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 SkPaint tmp;
981 if (NULL == paint) {
982 tmp.setDither(true);
983 paint = &tmp;
984 }
reed@google.com4b226022011-01-11 18:32:13 +0000985
reed@google.com8926b162012-03-23 15:36:36 +0000986 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000988 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000989 paint = &looper.paint();
990 SkImageFilter* filter = paint->getImageFilter();
991 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000992 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000993 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000994 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000995 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000996 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000997 SkPaint tmpUnfiltered(*paint);
998 tmpUnfiltered.setImageFilter(NULL);
999 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001000 }
1001 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001002 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001003 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001005 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006}
1007
reed@google.com8926b162012-03-23 15:36:36 +00001008void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1009 const SkPaint* paint) {
1010 SkDEBUGCODE(bitmap.validate();)
1011
1012 if (reject_bitmap(bitmap)) {
1013 return;
1014 }
1015
1016 SkPaint tmp;
1017 if (NULL == paint) {
1018 paint = &tmp;
1019 }
1020
1021 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
1022
1023 while (iter.next()) {
1024 paint = &looper.paint();
1025 SkImageFilter* filter = paint->getImageFilter();
1026 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1027 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1028 DeviceImageFilterProxy proxy(iter.fDevice);
1029 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001030 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1031 &dst, &pos)) {
1032 SkPaint tmpUnfiltered(*paint);
1033 tmpUnfiltered.setImageFilter(NULL);
1034 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1035 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001036 }
1037 } else {
1038 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1039 }
1040 }
1041 LOOPER_END
1042}
1043
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044/////////////////////////////////////////////////////////////////////////////
1045
1046bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1047 fDeviceCMDirty = true;
1048 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001049 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001050 return fMCRec->fMatrix->preTranslate(dx, dy);
1051}
1052
1053bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1054 fDeviceCMDirty = true;
1055 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001056 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 return fMCRec->fMatrix->preScale(sx, sy);
1058}
1059
1060bool SkCanvas::rotate(SkScalar degrees) {
1061 fDeviceCMDirty = true;
1062 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001063 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 return fMCRec->fMatrix->preRotate(degrees);
1065}
1066
1067bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1068 fDeviceCMDirty = true;
1069 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001070 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071 return fMCRec->fMatrix->preSkew(sx, sy);
1072}
1073
1074bool SkCanvas::concat(const SkMatrix& matrix) {
1075 fDeviceCMDirty = true;
1076 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001077 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001078 return fMCRec->fMatrix->preConcat(matrix);
1079}
1080
1081void SkCanvas::setMatrix(const SkMatrix& matrix) {
1082 fDeviceCMDirty = true;
1083 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001084 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 *fMCRec->fMatrix = matrix;
1086}
1087
1088// this is not virtual, so it must call a virtual method so that subclasses
1089// will see its action
1090void SkCanvas::resetMatrix() {
1091 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001092
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 matrix.reset();
1094 this->setMatrix(matrix);
1095}
1096
1097//////////////////////////////////////////////////////////////////////////////
1098
reed@google.comc42d35d2011-10-12 11:57:42 +00001099bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001100#ifdef SK_ENABLE_CLIP_QUICKREJECT
1101 if (SkRegion::kIntersect_Op == op) {
1102 if (fMCRec->fRasterClip->isEmpty()) {
1103 return false;
1104 }
1105
1106 if (this->quickReject(rect, kAA_EdgeType)) {
1107 fDeviceCMDirty = true;
1108 fLocalBoundsCompareTypeDirty = true;
1109 fLocalBoundsCompareTypeDirtyBW = true;
1110
1111 fClipStack.clipEmpty();
1112 return fMCRec->fRasterClip->setEmpty();
1113 }
1114 }
1115#endif
1116
reed@google.com5c3d1472011-02-22 19:12:23 +00001117 AutoValidateClip avc(this);
1118
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 fDeviceCMDirty = true;
1120 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001121 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122
1123 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001124 // for these simpler matrices, we can stay a rect ever after applying
1125 // the matrix. This means we don't have to a) make a path, and b) tell
1126 // the region code to scan-convert the path, only to discover that it
1127 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001128 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129
1130 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001131 fClipStack.clipDevRect(r, op, doAA);
1132 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001134 // since we're rotate or some such thing, we convert the rect to a path
1135 // and clip against that, since it can handle any matrix. However, to
1136 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1137 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001138 SkPath path;
1139
1140 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001141 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001142 }
1143}
1144
reed@google.com00177082011-10-12 14:34:30 +00001145static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001146 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001147 // base is used to limit the size (and therefore memory allocation) of the
1148 // region that results from scan converting devPath.
1149 SkRegion base;
1150
reed@google.com819c9212011-02-23 18:56:55 +00001151 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001152 // since we are intersect, we can do better (tighter) with currRgn's
1153 // bounds, than just using the device. However, if currRgn is complex,
1154 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001155 if (currClip->isRect()) {
1156 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001157 } else {
reed@google.com00177082011-10-12 14:34:30 +00001158 base.setRect(currClip->getBounds());
1159 SkRasterClip clip;
1160 clip.setPath(devPath, base, doAA);
1161 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001162 }
reed@google.com819c9212011-02-23 18:56:55 +00001163 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001164 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001165 if (!device) {
1166 return currClip->setEmpty();
1167 }
1168
junov@chromium.orga907ac32012-02-24 21:54:07 +00001169 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001170
1171 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001172 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001173 } else {
reed@google.com00177082011-10-12 14:34:30 +00001174 SkRasterClip clip;
1175 clip.setPath(devPath, base, doAA);
1176 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001177 }
1178 }
1179}
1180
reed@google.comc42d35d2011-10-12 11:57:42 +00001181bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001182#ifdef SK_ENABLE_CLIP_QUICKREJECT
1183 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1184 if (fMCRec->fRasterClip->isEmpty()) {
1185 return false;
1186 }
1187
1188 if (this->quickReject(path.getBounds(), kAA_EdgeType)) {
1189 fDeviceCMDirty = true;
1190 fLocalBoundsCompareTypeDirty = true;
1191 fLocalBoundsCompareTypeDirtyBW = true;
1192
1193 fClipStack.clipEmpty();
1194 return fMCRec->fRasterClip->setEmpty();
1195 }
1196 }
1197#endif
1198
reed@google.com5c3d1472011-02-22 19:12:23 +00001199 AutoValidateClip avc(this);
1200
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201 fDeviceCMDirty = true;
1202 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001203 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204
1205 SkPath devPath;
1206 path.transform(*fMCRec->fMatrix, &devPath);
1207
reed@google.comfe701122011-11-08 19:41:23 +00001208 // Check if the transfomation, or the original path itself
1209 // made us empty. Note this can also happen if we contained NaN
1210 // values. computing the bounds detects this, and will set our
1211 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1212 if (devPath.getBounds().isEmpty()) {
1213 // resetting the path will remove any NaN or other wanky values
1214 // that might upset our scan converter.
1215 devPath.reset();
1216 }
1217
reed@google.com5c3d1472011-02-22 19:12:23 +00001218 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001219 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001220
reed@google.com00177082011-10-12 14:34:30 +00001221 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222}
1223
1224bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001225 AutoValidateClip avc(this);
1226
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227 fDeviceCMDirty = true;
1228 fLocalBoundsCompareTypeDirty = true;
reed@android.comba09de42010-02-05 20:46:05 +00001229 fLocalBoundsCompareTypeDirtyBW = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230
reed@google.com5c3d1472011-02-22 19:12:23 +00001231 // todo: signal fClipStack that we have a region, and therefore (I guess)
1232 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001233 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001234
reed@google.com00177082011-10-12 14:34:30 +00001235 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236}
1237
reed@google.com819c9212011-02-23 18:56:55 +00001238#ifdef SK_DEBUG
1239void SkCanvas::validateClip() const {
1240 // construct clipRgn from the clipstack
1241 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001242 if (!device) {
1243 SkASSERT(this->getTotalClip().isEmpty());
1244 return;
1245 }
1246
reed@google.com819c9212011-02-23 18:56:55 +00001247 SkIRect ir;
1248 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001249 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001250
robertphillips@google.com80214e22012-07-20 15:33:18 +00001251 SkClipStack::B2TIter iter(fClipStack);
1252 const SkClipStack::B2TIter::Clip* clip;
reed@google.com819c9212011-02-23 18:56:55 +00001253 while ((clip = iter.next()) != NULL) {
1254 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001255 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001256 } else if (clip->fRect) {
1257 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001258 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001259 } else {
reed@google.com00177082011-10-12 14:34:30 +00001260 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001261 }
1262 }
1263
reed@google.com6f8f2922011-03-04 22:27:10 +00001264#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001265 // now compare against the current rgn
1266 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001267 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001268#endif
reed@google.com819c9212011-02-23 18:56:55 +00001269}
1270#endif
1271
reed@google.com90c07ea2012-04-13 13:50:27 +00001272void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001273 SkClipStack::B2TIter iter(fClipStack);
1274 const SkClipStack::B2TIter::Clip* clip;
reed@google.com90c07ea2012-04-13 13:50:27 +00001275
robertphillips@google.com7460b372012-04-25 16:54:51 +00001276 SkRect empty = { 0, 0, 0, 0 };
reed@google.com90c07ea2012-04-13 13:50:27 +00001277 while ((clip = iter.next()) != NULL) {
1278 if (clip->fPath) {
1279 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1280 } else if (clip->fRect) {
1281 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1282 } else {
1283 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1284 }
1285 }
1286}
1287
reed@google.com5c3d1472011-02-22 19:12:23 +00001288///////////////////////////////////////////////////////////////////////////////
1289
reed@android.comba09de42010-02-05 20:46:05 +00001290void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001292 SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
1293 fLocalBoundsCompareTypeBW;
1294
1295 if (!this->getClipBounds(&r, et)) {
1296 rCompare.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001298 rCompare.set(SkScalarToCompareType(r.fLeft),
1299 SkScalarToCompareType(r.fTop),
1300 SkScalarToCompareType(r.fRight),
1301 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 }
1303}
1304
reed@android.comd252db02009-04-01 18:31:44 +00001305/* current impl ignores edgetype, and relies on
1306 getLocalClipBoundsCompareType(), which always returns a value assuming
1307 antialiasing (worst case)
1308 */
reed@android.comba09de42010-02-05 20:46:05 +00001309bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001310
reed@google.com16078632011-12-06 18:56:37 +00001311 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001312 return true;
1313
reed@google.com00177082011-10-12 14:34:30 +00001314 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 return true;
1316 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317
tomhudson@google.com8d430182011-06-06 19:11:19 +00001318 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001319 SkRect dst;
1320 fMCRec->fMatrix->mapRect(&dst, rect);
1321 SkIRect idst;
1322 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001323 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001324 } else {
reed@android.comba09de42010-02-05 20:46:05 +00001325 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
reed@android.comd252db02009-04-01 18:31:44 +00001326
reed@android.coma380ae42009-07-21 01:17:02 +00001327 // for speed, do the most likely reject compares first
1328 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1329 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1330 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1331 return true;
1332 }
1333 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1334 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1335 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1336 return true;
1337 }
1338 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001339 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001340}
1341
1342bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
reed@android.comd252db02009-04-01 18:31:44 +00001343 return path.isEmpty() || this->quickReject(path.getBounds(), et);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344}
1345
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001346static inline int pinIntForScalar(int x) {
1347#ifdef SK_SCALAR_IS_FIXED
1348 if (x < SK_MinS16) {
1349 x = SK_MinS16;
1350 } else if (x > SK_MaxS16) {
1351 x = SK_MaxS16;
1352 }
1353#endif
1354 return x;
1355}
1356
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001358 SkIRect ibounds;
1359 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001360 return false;
1361 }
1362
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001363 SkMatrix inverse;
1364 // if we can't invert the CTM, we can't return local clip bounds
1365 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001366 if (bounds) {
1367 bounds->setEmpty();
1368 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001369 return false;
1370 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001371
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001372 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001373 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001374 // adjust it outwards if we are antialiasing
1375 int inset = (kAA_EdgeType == et);
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001376
1377 // SkRect::iset() will correctly assert if we pass a value out of range
1378 // (when SkScalar==fixed), so we pin to legal values. This does not
1379 // really returnt the correct answer, but its the best we can do given
1380 // that we've promised to return SkRect (even though we support devices
1381 // that can be larger than 32K in width or height).
1382 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1383 pinIntForScalar(ibounds.fTop - inset),
1384 pinIntForScalar(ibounds.fRight + inset),
1385 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001386 inverse.mapRect(bounds, r);
1387 }
1388 return true;
1389}
1390
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001391bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001392 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001393 if (clip.isEmpty()) {
1394 if (bounds) {
1395 bounds->setEmpty();
1396 }
1397 return false;
1398 }
1399
1400 if (NULL != bounds) {
1401 *bounds = clip.getBounds();
1402 }
1403 return true;
1404}
1405
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406const SkMatrix& SkCanvas::getTotalMatrix() const {
1407 return *fMCRec->fMatrix;
1408}
1409
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001410SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001411 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1412 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001413 return kComplex_ClipType;
1414}
1415
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001417 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001418}
1419
reed@android.comf2b98d62010-12-20 18:26:13 +00001420void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1421 if (NULL == matrix || matrix->isIdentity()) {
1422 if (fUseExternalMatrix) {
1423 fDeviceCMDirty = true;
1424 }
1425 fUseExternalMatrix = false;
1426 } else {
mike@reedtribe.org90bf4272012-04-14 19:06:16 +00001427 if (matrix->invert(&fExternalInverse)) {
1428 fExternalMatrix = *matrix;
1429 fUseExternalMatrix = true;
1430 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.comfc9a3be2012-04-12 13:52:14 +00001431 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001432 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001433}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434
bsalomon@google.come97f0852011-06-17 13:10:25 +00001435SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1436 int width, int height,
1437 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001438 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001439 if (device) {
1440 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1441 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001442 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001443 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001444 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445}
1446
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001447SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001448 int width, int height,
1449 bool isOpaque) {
1450 SkDevice* device = this->getDevice();
1451 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001452 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001453 } else {
1454 return NULL;
1455 }
1456}
1457
1458
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459//////////////////////////////////////////////////////////////////////////////
1460// These are the virtual drawing methods
1461//////////////////////////////////////////////////////////////////////////////
1462
reed@google.com2a981812011-04-14 18:59:28 +00001463void SkCanvas::clear(SkColor color) {
1464 SkDrawIter iter(this);
1465
1466 while (iter.next()) {
1467 iter.fDevice->clear(color);
1468 }
1469}
1470
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001472 this->internalDrawPaint(paint);
1473}
1474
1475void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001476 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_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->drawPaint(iter, 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::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1486 const SkPaint& paint) {
1487 if ((long)count <= 0) {
1488 return;
1489 }
1490
reed@google.coma584aed2012-05-16 14:06:02 +00001491 if (paint.canComputeFastBounds()) {
1492 SkRect r;
1493 // special-case 2 points (common for drawing a single line)
1494 if (2 == count) {
1495 r.set(pts[0], pts[1]);
1496 } else {
1497 r.set(pts, count);
1498 }
1499 SkRect storage;
1500 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage),
1501 paint2EdgeType(&paint))) {
1502 return;
1503 }
1504 }
1505
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 SkASSERT(pts != NULL);
1507
reed@google.com4e2b3d32011-04-07 14:18:59 +00001508 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001509
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001511 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512 }
reed@google.com4b226022011-01-11 18:32:13 +00001513
reed@google.com4e2b3d32011-04-07 14:18:59 +00001514 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515}
1516
1517void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1518 if (paint.canComputeFastBounds()) {
1519 SkRect storage;
1520 if (this->quickReject(paint.computeFastBounds(r, &storage),
1521 paint2EdgeType(&paint))) {
1522 return;
1523 }
1524 }
reed@google.com4b226022011-01-11 18:32:13 +00001525
reed@google.com4e2b3d32011-04-07 14:18:59 +00001526 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527
1528 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001529 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001530 }
1531
reed@google.com4e2b3d32011-04-07 14:18:59 +00001532 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533}
1534
1535void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001536 if (!path.isFinite()) {
1537 return;
1538 }
1539
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001540 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001541 SkRect storage;
1542 const SkRect& bounds = path.getBounds();
1543 if (this->quickReject(paint.computeFastBounds(bounds, &storage),
reed@android.com8a1c16f2008-12-17 15:59:43 +00001544 paint2EdgeType(&paint))) {
1545 return;
1546 }
1547 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001548 if (path.isEmpty()) {
1549 if (path.isInverseFillType()) {
1550 this->internalDrawPaint(paint);
1551 }
1552 return;
1553 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001554
reed@google.com4e2b3d32011-04-07 14:18:59 +00001555 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001556
1557 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001558 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559 }
1560
reed@google.com4e2b3d32011-04-07 14:18:59 +00001561 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562}
1563
1564void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1565 const SkPaint* paint) {
1566 SkDEBUGCODE(bitmap.validate();)
1567
reed@google.com3d608122011-11-21 15:16:16 +00001568 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001569 SkRect bounds = {
1570 x, y,
1571 x + SkIntToScalar(bitmap.width()),
1572 y + SkIntToScalar(bitmap.height())
1573 };
1574 if (paint) {
1575 (void)paint->computeFastBounds(bounds, &bounds);
1576 }
1577 if (this->quickReject(bounds, paint2EdgeType(paint))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578 return;
1579 }
1580 }
reed@google.com4b226022011-01-11 18:32:13 +00001581
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582 SkMatrix matrix;
1583 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001584 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585}
1586
reed@google.com9987ec32011-09-07 11:57:52 +00001587// this one is non-virtual, so it can be called safely by other canvas apis
1588void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1589 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1591 return;
1592 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001593
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001595 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001596 SkRect storage;
1597 const SkRect* bounds = &dst;
1598 if (paint) {
1599 bounds = &paint->computeFastBounds(dst, &storage);
1600 }
1601 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001602 return;
1603 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 }
reed@google.com3d608122011-11-21 15:16:16 +00001605
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001607
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001609 SkRect tmpSrc;
1610 if (src) {
1611 tmpSrc.set(*src);
1612 // if the extract process clipped off the top or left of the
1613 // original, we adjust for that here to get the position right.
1614 if (tmpSrc.fLeft > 0) {
1615 tmpSrc.fRight -= tmpSrc.fLeft;
1616 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001617 }
reed@android.com87899992009-10-16 14:48:38 +00001618 if (tmpSrc.fTop > 0) {
1619 tmpSrc.fBottom -= tmpSrc.fTop;
1620 tmpSrc.fTop = 0;
1621 }
1622 } else {
1623 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1624 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625 }
reed@android.com87899992009-10-16 14:48:38 +00001626 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001627
reed@android.comf2b98d62010-12-20 18:26:13 +00001628 // ensure that src is "valid" before we pass it to our internal routines
1629 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1630 SkIRect tmpISrc;
1631 if (src) {
1632 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001633 if (!tmpISrc.intersect(*src)) {
1634 return;
1635 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001636 src = &tmpISrc;
1637 }
1638 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639}
1640
reed@google.com9987ec32011-09-07 11:57:52 +00001641void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1642 const SkRect& dst, const SkPaint* paint) {
1643 SkDEBUGCODE(bitmap.validate();)
1644 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1645}
1646
reed@android.com8a1c16f2008-12-17 15:59:43 +00001647void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1648 const SkPaint* paint) {
1649 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001650 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001651}
1652
reed@android.comf2b98d62010-12-20 18:26:13 +00001653void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1654 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001656
reed@google.com4e2b3d32011-04-07 14:18:59 +00001657 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001658
reed@android.com8a1c16f2008-12-17 15:59:43 +00001659 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001660 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001661 }
reed@android.com9b039062009-02-11 15:09:58 +00001662
reed@google.com4e2b3d32011-04-07 14:18:59 +00001663 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001664}
1665
reed@google.com9987ec32011-09-07 11:57:52 +00001666void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1667 const SkIRect& center, const SkRect& dst,
1668 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001669 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001670 SkRect storage;
1671 const SkRect* bounds = &dst;
1672 if (paint) {
1673 bounds = &paint->computeFastBounds(dst, &storage);
1674 }
1675 if (this->quickReject(*bounds, paint2EdgeType(paint))) {
reed@google.com3d608122011-11-21 15:16:16 +00001676 return;
1677 }
1678 }
1679
reed@google.com9987ec32011-09-07 11:57:52 +00001680 const int32_t w = bitmap.width();
1681 const int32_t h = bitmap.height();
1682
1683 SkIRect c = center;
1684 // pin center to the bounds of the bitmap
1685 c.fLeft = SkMax32(0, center.fLeft);
1686 c.fTop = SkMax32(0, center.fTop);
1687 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1688 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1689
1690 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1691 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1692 SkScalar dstX[4] = {
1693 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1694 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1695 };
1696 SkScalar dstY[4] = {
1697 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1698 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1699 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001700
reed@google.com9987ec32011-09-07 11:57:52 +00001701 if (dstX[1] > dstX[2]) {
1702 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1703 dstX[2] = dstX[1];
1704 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001705
reed@google.com9987ec32011-09-07 11:57:52 +00001706 if (dstY[1] > dstY[2]) {
1707 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1708 dstY[2] = dstY[1];
1709 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001710
reed@google.com9987ec32011-09-07 11:57:52 +00001711 SkIRect s;
1712 SkRect d;
1713 for (int y = 0; y < 3; y++) {
1714 s.fTop = srcY[y];
1715 s.fBottom = srcY[y+1];
1716 d.fTop = dstY[y];
1717 d.fBottom = dstY[y+1];
1718 for (int x = 0; x < 3; x++) {
1719 s.fLeft = srcX[x];
1720 s.fRight = srcX[x+1];
1721 d.fLeft = dstX[x];
1722 d.fRight = dstX[x+1];
1723 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1724 }
1725 }
1726}
1727
1728void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1729 const SkRect& dst, const SkPaint* paint) {
1730 SkDEBUGCODE(bitmap.validate();)
1731
1732 // Need a device entry-point, so gpu can use a mesh
1733 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1734}
1735
reed@google.comf67e4cf2011-03-15 20:56:58 +00001736class SkDeviceFilteredPaint {
1737public:
1738 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1739 SkDevice::TextFlags flags;
1740 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001741 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001742 newPaint->setFlags(flags.fFlags);
1743 newPaint->setHinting(flags.fHinting);
1744 fPaint = newPaint;
1745 } else {
1746 fPaint = &paint;
1747 }
1748 }
1749
reed@google.comf67e4cf2011-03-15 20:56:58 +00001750 const SkPaint& paint() const { return *fPaint; }
1751
1752private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001753 const SkPaint* fPaint;
1754 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001755};
1756
bungeman@google.com52c748b2011-08-22 21:30:43 +00001757void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1758 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001759 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001760 draw.fDevice->drawRect(draw, r, paint);
1761 } else {
1762 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001763 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001764 draw.fDevice->drawRect(draw, r, p);
1765 }
1766}
1767
1768void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1769 const char text[], size_t byteLength,
1770 SkScalar x, SkScalar y) {
1771 SkASSERT(byteLength == 0 || text != NULL);
1772
1773 // nothing to draw
1774 if (text == NULL || byteLength == 0 ||
1775 draw.fClip->isEmpty() ||
1776 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1777 return;
1778 }
1779
1780 SkScalar width = 0;
1781 SkPoint start;
1782
1783 start.set(0, 0); // to avoid warning
1784 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1785 SkPaint::kStrikeThruText_Flag)) {
1786 width = paint.measureText(text, byteLength);
1787
1788 SkScalar offsetX = 0;
1789 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1790 offsetX = SkScalarHalf(width);
1791 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1792 offsetX = width;
1793 }
1794 start.set(x - offsetX, y);
1795 }
1796
1797 if (0 == width) {
1798 return;
1799 }
1800
1801 uint32_t flags = paint.getFlags();
1802
1803 if (flags & (SkPaint::kUnderlineText_Flag |
1804 SkPaint::kStrikeThruText_Flag)) {
1805 SkScalar textSize = paint.getTextSize();
1806 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1807 SkRect r;
1808
1809 r.fLeft = start.fX;
1810 r.fRight = start.fX + width;
1811
1812 if (flags & SkPaint::kUnderlineText_Flag) {
1813 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1814 start.fY);
1815 r.fTop = offset;
1816 r.fBottom = offset + height;
1817 DrawRect(draw, paint, r, textSize);
1818 }
1819 if (flags & SkPaint::kStrikeThruText_Flag) {
1820 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1821 start.fY);
1822 r.fTop = offset;
1823 r.fBottom = offset + height;
1824 DrawRect(draw, paint, r, textSize);
1825 }
1826 }
1827}
1828
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829void SkCanvas::drawText(const void* text, size_t byteLength,
1830 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001831 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832
1833 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001834 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001835 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001836 DrawTextDecorations(iter, dfp.paint(),
1837 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001838 }
1839
reed@google.com4e2b3d32011-04-07 14:18:59 +00001840 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841}
1842
1843void SkCanvas::drawPosText(const void* text, size_t byteLength,
1844 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001845 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001846
reed@android.com8a1c16f2008-12-17 15:59:43 +00001847 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001848 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001849 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001850 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001851 }
reed@google.com4b226022011-01-11 18:32:13 +00001852
reed@google.com4e2b3d32011-04-07 14:18:59 +00001853 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854}
1855
1856void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1857 const SkScalar xpos[], SkScalar constY,
1858 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001859 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001860
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001862 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001864 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001865 }
reed@google.com4b226022011-01-11 18:32:13 +00001866
reed@google.com4e2b3d32011-04-07 14:18:59 +00001867 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001868}
1869
1870void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1871 const SkPath& path, const SkMatrix* matrix,
1872 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001873 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001874
1875 while (iter.next()) {
1876 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001877 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878 }
1879
reed@google.com4e2b3d32011-04-07 14:18:59 +00001880 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001881}
1882
djsollen@google.com56c69772011-11-08 19:00:26 +00001883#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001884void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1885 const SkPoint pos[], const SkPaint& paint,
1886 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001887 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001888
1889 while (iter.next()) {
1890 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001891 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001892 }
1893
reed@google.com4e2b3d32011-04-07 14:18:59 +00001894 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001895}
1896#endif
1897
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1899 const SkPoint verts[], const SkPoint texs[],
1900 const SkColor colors[], SkXfermode* xmode,
1901 const uint16_t indices[], int indexCount,
1902 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001903 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001904
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905 while (iter.next()) {
1906 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001907 colors, xmode, indices, indexCount,
1908 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 }
reed@google.com4b226022011-01-11 18:32:13 +00001910
reed@google.com4e2b3d32011-04-07 14:18:59 +00001911 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912}
1913
reed@android.comcb608442009-12-04 21:32:27 +00001914void SkCanvas::drawData(const void* data, size_t length) {
1915 // do nothing. Subclasses may do something with the data
1916}
1917
reed@android.com8a1c16f2008-12-17 15:59:43 +00001918//////////////////////////////////////////////////////////////////////////////
1919// These methods are NOT virtual, and therefore must call back into virtual
1920// methods, rather than actually drawing themselves.
1921//////////////////////////////////////////////////////////////////////////////
1922
1923void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001924 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925 SkPaint paint;
1926
1927 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001928 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001929 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001930 }
1931 this->drawPaint(paint);
1932}
1933
reed@android.com845fdac2009-06-23 03:01:32 +00001934void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001935 SkPaint paint;
1936
1937 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001938 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001939 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940 }
1941 this->drawPaint(paint);
1942}
1943
1944void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1945 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001946
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947 pt.set(x, y);
1948 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1949}
1950
1951void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1952 SkPoint pt;
1953 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001954
reed@android.com8a1c16f2008-12-17 15:59:43 +00001955 pt.set(x, y);
1956 paint.setColor(color);
1957 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1958}
1959
1960void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1961 const SkPaint& paint) {
1962 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001963
reed@android.com8a1c16f2008-12-17 15:59:43 +00001964 pts[0].set(x0, y0);
1965 pts[1].set(x1, y1);
1966 this->drawPoints(kLines_PointMode, 2, pts, paint);
1967}
1968
1969void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1970 SkScalar right, SkScalar bottom,
1971 const SkPaint& paint) {
1972 SkRect r;
1973
1974 r.set(left, top, right, bottom);
1975 this->drawRect(r, paint);
1976}
1977
1978void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1979 const SkPaint& paint) {
1980 if (radius < 0) {
1981 radius = 0;
1982 }
1983
1984 SkRect r;
1985 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001986
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987 if (paint.canComputeFastBounds()) {
1988 SkRect storage;
1989 if (this->quickReject(paint.computeFastBounds(r, &storage),
1990 paint2EdgeType(&paint))) {
1991 return;
1992 }
1993 }
reed@google.com4b226022011-01-11 18:32:13 +00001994
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995 SkPath path;
1996 path.addOval(r);
1997 this->drawPath(path, paint);
1998}
1999
2000void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2001 const SkPaint& paint) {
2002 if (rx > 0 && ry > 0) {
2003 if (paint.canComputeFastBounds()) {
2004 SkRect storage;
2005 if (this->quickReject(paint.computeFastBounds(r, &storage),
2006 paint2EdgeType(&paint))) {
2007 return;
2008 }
2009 }
2010
2011 SkPath path;
2012 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
2013 this->drawPath(path, paint);
2014 } else {
2015 this->drawRect(r, paint);
2016 }
2017}
2018
2019void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
2020 if (paint.canComputeFastBounds()) {
2021 SkRect storage;
2022 if (this->quickReject(paint.computeFastBounds(oval, &storage),
2023 paint2EdgeType(&paint))) {
2024 return;
2025 }
2026 }
2027
2028 SkPath path;
2029 path.addOval(oval);
2030 this->drawPath(path, paint);
2031}
2032
2033void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2034 SkScalar sweepAngle, bool useCenter,
2035 const SkPaint& paint) {
2036 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2037 this->drawOval(oval, paint);
2038 } else {
2039 SkPath path;
2040 if (useCenter) {
2041 path.moveTo(oval.centerX(), oval.centerY());
2042 }
2043 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2044 if (useCenter) {
2045 path.close();
2046 }
2047 this->drawPath(path, paint);
2048 }
2049}
2050
2051void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2052 const SkPath& path, SkScalar hOffset,
2053 SkScalar vOffset, const SkPaint& paint) {
2054 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002055
reed@android.com8a1c16f2008-12-17 15:59:43 +00002056 matrix.setTranslate(hOffset, vOffset);
2057 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2058}
2059
reed@android.comf76bacf2009-05-13 14:00:33 +00002060///////////////////////////////////////////////////////////////////////////////
2061
reed@android.com8a1c16f2008-12-17 15:59:43 +00002062void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064}
2065
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066///////////////////////////////////////////////////////////////////////////////
2067///////////////////////////////////////////////////////////////////////////////
2068
2069SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002070 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071
2072 SkASSERT(canvas);
2073
2074 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2075 fDone = !fImpl->next();
2076}
2077
2078SkCanvas::LayerIter::~LayerIter() {
2079 fImpl->~SkDrawIter();
2080}
2081
2082void SkCanvas::LayerIter::next() {
2083 fDone = !fImpl->next();
2084}
2085
2086SkDevice* SkCanvas::LayerIter::device() const {
2087 return fImpl->getDevice();
2088}
2089
2090const SkMatrix& SkCanvas::LayerIter::matrix() const {
2091 return fImpl->getMatrix();
2092}
2093
2094const SkPaint& SkCanvas::LayerIter::paint() const {
2095 const SkPaint* paint = fImpl->getPaint();
2096 if (NULL == paint) {
2097 paint = &fDefaultPaint;
2098 }
2099 return *paint;
2100}
2101
2102const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2103int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2104int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002105
2106///////////////////////////////////////////////////////////////////////////////
2107
2108SkCanvas::ClipVisitor::~ClipVisitor() { }