blob: 28a2bc4363e87d2e2629328eb7c1f083f0f931e6 [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"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000016#include "SkMetaData.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000018#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkScalarCompare.h"
reed@google.com97af1a62012-08-28 12:19:02 +000020#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000021#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000022#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000023#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025
reed@google.com82ce2b82012-06-26 17:43:26 +000026SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000027SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000028SK_DEFINE_INST_COUNT(SkDrawFilter)
29
reed@google.comda17f752012-08-16 18:27:05 +000030// experimental for faster tiled drawing...
31//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000032
reed@android.com8a1c16f2008-12-17 15:59:43 +000033//#define SK_TRACE_SAVERESTORE
34
35#ifdef SK_TRACE_SAVERESTORE
36 static int gLayerCounter;
37 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
38 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
39
40 static int gRecCounter;
41 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
42 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
43
44 static int gCanvasCounter;
45 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
46 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
47#else
48 #define inc_layer()
49 #define dec_layer()
50 #define inc_rec()
51 #define dec_rec()
52 #define inc_canvas()
53 #define dec_canvas()
54#endif
55
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000056typedef SkTLazy<SkPaint> SkLazyPaint;
57
reed@google.com97af1a62012-08-28 12:19:02 +000058void SkCanvas::predrawNotify() {
59 if (fSurfaceBase) {
60 fSurfaceBase->aboutToDraw(this);
61 }
62}
63
reed@android.com8a1c16f2008-12-17 15:59:43 +000064///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000065
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 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000339 }
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); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000436 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000437 AutoDrawLooper looper(this, paint, true); \
438 while (looper.next(type)) { \
439 SkAutoBounderCommit ac(fBounder); \
440 SkDrawIter iter(this);
441
reed@google.com4e2b3d32011-04-07 14:18:59 +0000442#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000444 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000445 AutoDrawLooper looper(this, paint); \
446 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 SkAutoBounderCommit ac(fBounder); \
448 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000449
reed@google.com4e2b3d32011-04-07 14:18:59 +0000450#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451
452////////////////////////////////////////////////////////////////////////////
453
454SkDevice* SkCanvas::init(SkDevice* device) {
455 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000456 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000457 fLocalBoundsCompareTypeDirty = 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;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000461 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000462
463 fMCRec = (MCRec*)fMCStack.push_back();
464 new (fMCRec) MCRec(NULL, 0);
465
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000466 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 fMCRec->fTopLayer = fMCRec->fLayer;
468 fMCRec->fNext = NULL;
469
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000470 fExternalMatrix.reset();
471 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000472 fUseExternalMatrix = false;
skia.committer@gmail.coma27096b2012-08-30 14:38:00 +0000473
reed@google.com97af1a62012-08-28 12:19:02 +0000474 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000475
reed@android.com8a1c16f2008-12-17 15:59:43 +0000476 return this->setDevice(device);
477}
478
reed@google.comcde92112011-07-06 20:00:52 +0000479SkCanvas::SkCanvas()
480: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000481 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000482
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000483 this->init(NULL);
484}
485
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000487 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 inc_canvas();
489
490 this->init(device);
491}
492
493SkCanvas::SkCanvas(const SkBitmap& bitmap)
494 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
495 inc_canvas();
496
reed@google.comcde92112011-07-06 20:00:52 +0000497 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498}
499
500SkCanvas::~SkCanvas() {
501 // free up the contents of our deque
502 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000503 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000504
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 this->internalRestore(); // restore the last, since we're going away
506
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000507 SkSafeUnref(fBounder);
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000508 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000509
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 dec_canvas();
511}
512
513SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
514 SkRefCnt_SafeAssign(fBounder, bounder);
515 return bounder;
516}
517
518SkDrawFilter* SkCanvas::getDrawFilter() const {
519 return fMCRec->fFilter;
520}
521
522SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
523 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
524 return filter;
525}
526
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000527SkMetaData& SkCanvas::getMetaData() {
528 // metadata users are rare, so we lazily allocate it. If that changes we
529 // can decide to just make it a field in the device (rather than a ptr)
530 if (NULL == fMetaData) {
531 fMetaData = new SkMetaData;
532 }
533 return *fMetaData;
534}
535
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536///////////////////////////////////////////////////////////////////////////////
537
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000538void SkCanvas::flush() {
539 SkDevice* device = this->getDevice();
540 if (device) {
541 device->flush();
542 }
543}
544
reed@google.com210ce002011-11-01 14:24:23 +0000545SkISize SkCanvas::getDeviceSize() const {
546 SkDevice* d = this->getDevice();
547 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
548}
549
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550SkDevice* SkCanvas::getDevice() const {
551 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000552 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 SkASSERT(rec && rec->fLayer);
554 return rec->fLayer->fDevice;
555}
556
reed@google.com0b53d592012-03-19 18:26:34 +0000557SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
558 if (updateMatrixClip) {
559 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
560 }
reed@google.com9266fed2011-03-30 00:18:03 +0000561 return fMCRec->fTopLayer->fDevice;
562}
563
reed@android.com8a1c16f2008-12-17 15:59:43 +0000564SkDevice* SkCanvas::setDevice(SkDevice* device) {
565 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000566 SkDeque::F2BIter iter(fMCStack);
567 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 SkASSERT(rec && rec->fLayer);
569 SkDevice* rootDevice = rec->fLayer->fDevice;
570
571 if (rootDevice == device) {
572 return device;
573 }
reed@google.com4b226022011-01-11 18:32:13 +0000574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000576 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 }
578 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000579 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 }
581
582 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
583 rootDevice = device;
584
585 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000586
reed@android.com8a1c16f2008-12-17 15:59:43 +0000587 /* Now we update our initial region to have the bounds of the new device,
588 and then intersect all of the clips in our stack with these bounds,
589 to ensure that we can't draw outside of the device's bounds (and trash
590 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000591
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 NOTE: this is only a partial-fix, since if the new device is larger than
593 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000594 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
596 reconstruct the correct clips, so this approximation will have to do.
597 The caller really needs to restore() back to the base if they want to
598 accurately take advantage of the new device bounds.
599 */
600
reed@google.com42aea282012-03-28 16:19:15 +0000601 SkIRect bounds;
602 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000603 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000604 } else {
605 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 }
reed@google.com42aea282012-03-28 16:19:15 +0000607 // now jam our 1st clip to be bounds, and intersect the rest with that
608 rec->fRasterClip->setRect(bounds);
609 while ((rec = (MCRec*)iter.next()) != NULL) {
610 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
611 }
612
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 return device;
614}
615
reed@google.comaf951c92011-06-16 19:10:39 +0000616SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
617 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 device->unref();
619 return device;
620}
621
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000622bool SkCanvas::readPixels(SkBitmap* bitmap,
623 int x, int y,
624 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000625 SkDevice* device = this->getDevice();
626 if (!device) {
627 return false;
628 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000629 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000630}
631
bsalomon@google.comc6980972011-11-02 19:57:21 +0000632bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000633 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000634 if (!device) {
635 return false;
636 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000637
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000638 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000639 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000640 if (!bounds.intersect(srcRect)) {
641 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000642 }
643
644 SkBitmap tmp;
645 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
646 bounds.height());
647 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
648 bitmap->swap(tmp);
649 return true;
650 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000651 return false;
652 }
reed@google.com51df9e32010-12-23 19:29:18 +0000653}
654
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000655void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
656 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000657 SkDevice* device = this->getDevice();
658 if (device) {
bsalomon@google.com405d0f42012-08-29 21:26:13 +0000659 if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
660 SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
661 device->accessBitmap(true);
662 device->writePixels(bitmap, x, y, config8888);
663 }
reed@google.com51df9e32010-12-23 19:29:18 +0000664 }
665}
666
junov@google.com4370aed2012-01-18 16:21:08 +0000667SkCanvas* SkCanvas::canvasForDrawIter() {
668 return this;
669}
670
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671//////////////////////////////////////////////////////////////////////////////
672
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673void SkCanvas::updateDeviceCMCache() {
674 if (fDeviceCMDirty) {
675 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000676 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000680 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000681 if (fUseExternalMatrix) {
682 layer->updateExternalMatrix(fExternalMatrix,
683 fExternalInverse);
684 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000686 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000688 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000689 if (fUseExternalMatrix) {
690 layer->updateExternalMatrix(fExternalMatrix,
691 fExternalInverse);
692 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000693 } while ((layer = layer->fNext) != NULL);
694 }
695 fDeviceCMDirty = false;
696 }
697}
698
reed@android.comf2b98d62010-12-20 18:26:13 +0000699void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000700 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000702 if (fLastDeviceToGainFocus != device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000703 device->gainFocus(matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000704 fLastDeviceToGainFocus = device;
705 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706}
707
708///////////////////////////////////////////////////////////////////////////////
709
710int SkCanvas::internalSave(SaveFlags flags) {
711 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000712
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 MCRec* newTop = (MCRec*)fMCStack.push_back();
714 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 newTop->fNext = fMCRec;
717 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000718
reed@google.com5c3d1472011-02-22 19:12:23 +0000719 fClipStack.save();
720 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 return saveCount;
723}
724
725int SkCanvas::save(SaveFlags flags) {
726 // call shared impl
727 return this->internalSave(flags);
728}
729
730#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
731#define C16MASK (1 << SkBitmap::kRGB_565_Config)
732#define C8MASK (1 << SkBitmap::kA8_Config)
733
734static SkBitmap::Config resolve_config(SkCanvas* canvas,
735 const SkIRect& bounds,
736 SkCanvas::SaveFlags flags,
737 bool* isOpaque) {
738 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
739
740#if 0
741 // loop through and union all the configs we may draw into
742 uint32_t configMask = 0;
743 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
744 {
745 SkDevice* device = canvas->getLayerDevice(i);
746 if (device->intersects(bounds))
747 configMask |= 1 << device->config();
748 }
749
750 // if the caller wants alpha or fullcolor, we can't return 565
751 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
752 SkCanvas::kHasAlphaLayer_SaveFlag))
753 configMask &= ~C16MASK;
754
755 switch (configMask) {
756 case C8MASK: // if we only have A8, return that
757 return SkBitmap::kA8_Config;
758
759 case C16MASK: // if we only have 565, return that
760 return SkBitmap::kRGB_565_Config;
761
762 default:
763 return SkBitmap::kARGB_8888_Config; // default answer
764 }
765#else
766 return SkBitmap::kARGB_8888_Config; // default answer
767#endif
768}
769
770static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
771 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
772}
773
junov@chromium.orga907ac32012-02-24 21:54:07 +0000774bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
775 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000776 SkIRect clipBounds;
777 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000778 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000779 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000780 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 if (NULL != bounds) {
782 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 this->getTotalMatrix().mapRect(&r, *bounds);
785 r.roundOut(&ir);
786 // early exit if the layer's bounds are clipped out
787 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000788 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000789 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000790 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000791 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792 }
793 } else { // no user bounds, so just use the clip
794 ir = clipBounds;
795 }
796
reed@google.com5c3d1472011-02-22 19:12:23 +0000797 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000798
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 // early exit if the clip is now empty
800 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000801 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000802 return false;
803 }
804
805 if (intersection) {
806 *intersection = ir;
807 }
808 return true;
809}
810
811int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
812 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000813 return this->internalSaveLayer(bounds, paint, flags, false);
814}
815
816int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
817 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000818 // do this before we create the layer. We don't call the public save() since
819 // that would invoke a possibly overridden virtual
820 int count = this->internalSave(flags);
821
822 fDeviceCMDirty = true;
823
824 SkIRect ir;
825 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 return count;
827 }
828
reed@google.comb55deeb2012-01-06 14:43:09 +0000829 // Kill the imagefilter if our device doesn't allow it
830 SkLazyPaint lazyP;
831 if (paint && paint->getImageFilter()) {
832 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000833 if (justForImageFilter) {
834 // early exit if the layer was just for the imageFilter
835 return count;
836 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000837 SkPaint* p = lazyP.set(*paint);
838 p->setImageFilter(NULL);
839 paint = p;
840 }
841 }
842
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 bool isOpaque;
844 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
845
reed@google.com76dd2772012-01-05 21:15:07 +0000846 SkDevice* device;
847 if (paint && paint->getImageFilter()) {
848 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
849 isOpaque);
850 } else {
851 device = this->createLayerDevice(config, ir.width(), ir.height(),
852 isOpaque);
853 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000854 if (NULL == device) {
855 SkDebugf("Unable to create device for layer.");
856 return count;
857 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000858
reed@google.com6f8f2922011-03-04 22:27:10 +0000859 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000860 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 device->unref();
862
863 layer->fNext = fMCRec->fTopLayer;
864 fMCRec->fLayer = layer;
865 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
866
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000867 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 return count;
869}
870
871int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
872 SaveFlags flags) {
873 if (0xFF == alpha) {
874 return this->saveLayer(bounds, NULL, flags);
875 } else {
876 SkPaint tmpPaint;
877 tmpPaint.setAlpha(alpha);
878 return this->saveLayer(bounds, &tmpPaint, flags);
879 }
880}
881
882void SkCanvas::restore() {
883 // check for underflow
884 if (fMCStack.count() > 1) {
885 this->internalRestore();
886 }
887}
888
889void SkCanvas::internalRestore() {
890 SkASSERT(fMCStack.count() != 0);
891
892 fDeviceCMDirty = true;
893 fLocalBoundsCompareTypeDirty = true;
bsalomon@google.comc2a24832012-08-29 19:40:59 +0000894 // Dirty this pointer to handle the case of a new device created at the same address as the
895 // device we are restoring from. E.g.:
896 // saveLayer (creates a device)
897 // drawSomething
898 // restore (deletes the device)
899 // saveLayer (oops new device at the same address)
900 fLastDeviceToGainFocus = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901
reed@google.com5c3d1472011-02-22 19:12:23 +0000902 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000903 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 DeviceCM* layer = fMCRec->fLayer; // may be null
905 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
906 fMCRec->fLayer = NULL;
907
908 // now do the normal restore()
909 fMCRec->~MCRec(); // balanced in save()
910 fMCStack.pop_back();
911 fMCRec = (MCRec*)fMCStack.back();
912
913 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
914 since if we're being recorded, we don't want to record this (the
915 recorder will have already recorded the restore).
916 */
917 if (NULL != layer) {
918 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000919 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000920 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
921 layer->fPaint);
922 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000924
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000925 SkASSERT(fSaveLayerCount > 0);
926 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 }
928 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000929 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000930
931 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932}
933
934int SkCanvas::getSaveCount() const {
935 return fMCStack.count();
936}
937
938void SkCanvas::restoreToCount(int count) {
939 // sanity check
940 if (count < 1) {
941 count = 1;
942 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000943
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000944 int n = this->getSaveCount() - count;
945 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000946 this->restore();
947 }
948}
949
reed@google.com7c202932011-12-14 18:48:05 +0000950bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000951 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000952}
953
reed@android.com8a1c16f2008-12-17 15:59:43 +0000954/////////////////////////////////////////////////////////////////////////////
955
956// can't draw it if its empty, or its too big for a fixed-point width or height
957static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000958 return bitmap.width() <= 0 || bitmap.height() <= 0
959#ifndef SK_ALLOW_OVER_32K_BITMAPS
960 || bitmap.width() > 32767 || bitmap.height() > 32767
961#endif
962 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963}
964
reed@android.comf2b98d62010-12-20 18:26:13 +0000965void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000966 const SkMatrix& matrix, const SkPaint* paint) {
967 if (reject_bitmap(bitmap)) {
968 return;
969 }
970
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000971 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000973 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000975 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976}
977
reed@google.com76dd2772012-01-05 21:15:07 +0000978#include "SkImageFilter.h"
979
980class DeviceImageFilterProxy : public SkImageFilter::Proxy {
981public:
982 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000983
reed@google.com8926b162012-03-23 15:36:36 +0000984 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
985 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
986 w, h, false);
987 }
988 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
989 return fDevice->canHandleImageFilter(filter);
990 }
991 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000992 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000993 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
994 return fDevice->filterImage(filter, src, ctm, result, offset);
995 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000996
reed@google.com76dd2772012-01-05 21:15:07 +0000997private:
998 SkDevice* fDevice;
999};
1000
reed@google.com8926b162012-03-23 15:36:36 +00001001void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
1002 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003 SkPaint tmp;
1004 if (NULL == paint) {
1005 tmp.setDither(true);
1006 paint = &tmp;
1007 }
reed@google.com4b226022011-01-11 18:32:13 +00001008
reed@google.com8926b162012-03-23 15:36:36 +00001009 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001010 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +00001011 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001012 paint = &looper.paint();
1013 SkImageFilter* filter = paint->getImageFilter();
1014 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001015 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +00001016 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001017 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +00001018 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +00001019 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001020 SkPaint tmpUnfiltered(*paint);
1021 tmpUnfiltered.setImageFilter(NULL);
1022 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001023 }
1024 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001025 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001026 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001028 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029}
1030
reed@google.com8926b162012-03-23 15:36:36 +00001031void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1032 const SkPaint* paint) {
1033 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001034
reed@google.com8926b162012-03-23 15:36:36 +00001035 if (reject_bitmap(bitmap)) {
1036 return;
1037 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001038
reed@google.com8926b162012-03-23 15:36:36 +00001039 SkPaint tmp;
1040 if (NULL == paint) {
1041 paint = &tmp;
1042 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001043
reed@google.com8926b162012-03-23 15:36:36 +00001044 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001045
reed@google.com8926b162012-03-23 15:36:36 +00001046 while (iter.next()) {
1047 paint = &looper.paint();
1048 SkImageFilter* filter = paint->getImageFilter();
1049 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1050 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1051 DeviceImageFilterProxy proxy(iter.fDevice);
1052 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001053 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1054 &dst, &pos)) {
1055 SkPaint tmpUnfiltered(*paint);
1056 tmpUnfiltered.setImageFilter(NULL);
1057 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1058 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001059 }
1060 } else {
1061 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1062 }
1063 }
1064 LOOPER_END
1065}
1066
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067/////////////////////////////////////////////////////////////////////////////
1068
1069bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1070 fDeviceCMDirty = true;
1071 fLocalBoundsCompareTypeDirty = true;
1072 return fMCRec->fMatrix->preTranslate(dx, dy);
1073}
1074
1075bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1076 fDeviceCMDirty = true;
1077 fLocalBoundsCompareTypeDirty = true;
1078 return fMCRec->fMatrix->preScale(sx, sy);
1079}
1080
1081bool SkCanvas::rotate(SkScalar degrees) {
1082 fDeviceCMDirty = true;
1083 fLocalBoundsCompareTypeDirty = true;
1084 return fMCRec->fMatrix->preRotate(degrees);
1085}
1086
1087bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1088 fDeviceCMDirty = true;
1089 fLocalBoundsCompareTypeDirty = true;
1090 return fMCRec->fMatrix->preSkew(sx, sy);
1091}
1092
1093bool SkCanvas::concat(const SkMatrix& matrix) {
1094 fDeviceCMDirty = true;
1095 fLocalBoundsCompareTypeDirty = true;
1096 return fMCRec->fMatrix->preConcat(matrix);
1097}
1098
1099void SkCanvas::setMatrix(const SkMatrix& matrix) {
1100 fDeviceCMDirty = true;
1101 fLocalBoundsCompareTypeDirty = true;
1102 *fMCRec->fMatrix = matrix;
1103}
1104
1105// this is not virtual, so it must call a virtual method so that subclasses
1106// will see its action
1107void SkCanvas::resetMatrix() {
1108 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001109
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 matrix.reset();
1111 this->setMatrix(matrix);
1112}
1113
1114//////////////////////////////////////////////////////////////////////////////
1115
reed@google.comc42d35d2011-10-12 11:57:42 +00001116bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001117#ifdef SK_ENABLE_CLIP_QUICKREJECT
1118 if (SkRegion::kIntersect_Op == op) {
1119 if (fMCRec->fRasterClip->isEmpty()) {
1120 return false;
1121 }
1122
reed@google.com3b3e8952012-08-16 20:53:31 +00001123 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001124 fDeviceCMDirty = true;
1125 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001126
1127 fClipStack.clipEmpty();
1128 return fMCRec->fRasterClip->setEmpty();
1129 }
1130 }
1131#endif
1132
reed@google.com5c3d1472011-02-22 19:12:23 +00001133 AutoValidateClip avc(this);
1134
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 fDeviceCMDirty = true;
1136 fLocalBoundsCompareTypeDirty = true;
1137
1138 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001139 // for these simpler matrices, we can stay a rect ever after applying
1140 // the matrix. This means we don't have to a) make a path, and b) tell
1141 // the region code to scan-convert the path, only to discover that it
1142 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001143 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001144
1145 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001146 fClipStack.clipDevRect(r, op, doAA);
1147 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001149 // since we're rotate or some such thing, we convert the rect to a path
1150 // and clip against that, since it can handle any matrix. However, to
1151 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1152 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 SkPath path;
1154
1155 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001156 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 }
1158}
1159
reed@google.com00177082011-10-12 14:34:30 +00001160static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001161 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001162 // base is used to limit the size (and therefore memory allocation) of the
1163 // region that results from scan converting devPath.
1164 SkRegion base;
1165
reed@google.com819c9212011-02-23 18:56:55 +00001166 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001167 // since we are intersect, we can do better (tighter) with currRgn's
1168 // bounds, than just using the device. However, if currRgn is complex,
1169 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001170 if (currClip->isRect()) {
1171 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001172 } else {
reed@google.com00177082011-10-12 14:34:30 +00001173 base.setRect(currClip->getBounds());
1174 SkRasterClip clip;
1175 clip.setPath(devPath, base, doAA);
1176 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001177 }
reed@google.com819c9212011-02-23 18:56:55 +00001178 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001179 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001180 if (!device) {
1181 return currClip->setEmpty();
1182 }
1183
junov@chromium.orga907ac32012-02-24 21:54:07 +00001184 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001185
1186 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001187 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001188 } else {
reed@google.com00177082011-10-12 14:34:30 +00001189 SkRasterClip clip;
1190 clip.setPath(devPath, base, doAA);
1191 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001192 }
1193 }
1194}
1195
reed@google.comc42d35d2011-10-12 11:57:42 +00001196bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001197#ifdef SK_ENABLE_CLIP_QUICKREJECT
1198 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1199 if (fMCRec->fRasterClip->isEmpty()) {
1200 return false;
1201 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001202
reed@google.com3b3e8952012-08-16 20:53:31 +00001203 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001204 fDeviceCMDirty = true;
1205 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001206
reed@google.comda17f752012-08-16 18:27:05 +00001207 fClipStack.clipEmpty();
1208 return fMCRec->fRasterClip->setEmpty();
1209 }
1210 }
1211#endif
1212
reed@google.com5c3d1472011-02-22 19:12:23 +00001213 AutoValidateClip avc(this);
1214
reed@android.com8a1c16f2008-12-17 15:59:43 +00001215 fDeviceCMDirty = true;
1216 fLocalBoundsCompareTypeDirty = true;
1217
1218 SkPath devPath;
1219 path.transform(*fMCRec->fMatrix, &devPath);
1220
reed@google.comfe701122011-11-08 19:41:23 +00001221 // Check if the transfomation, or the original path itself
1222 // made us empty. Note this can also happen if we contained NaN
1223 // values. computing the bounds detects this, and will set our
1224 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1225 if (devPath.getBounds().isEmpty()) {
1226 // resetting the path will remove any NaN or other wanky values
1227 // that might upset our scan converter.
1228 devPath.reset();
1229 }
1230
reed@google.com5c3d1472011-02-22 19:12:23 +00001231 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001232 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001233
reed@google.com00177082011-10-12 14:34:30 +00001234 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235}
1236
1237bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001238 AutoValidateClip avc(this);
1239
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 fDeviceCMDirty = true;
1241 fLocalBoundsCompareTypeDirty = true;
1242
reed@google.com5c3d1472011-02-22 19:12:23 +00001243 // todo: signal fClipStack that we have a region, and therefore (I guess)
1244 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001245 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001246
reed@google.com00177082011-10-12 14:34:30 +00001247 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248}
1249
reed@google.com819c9212011-02-23 18:56:55 +00001250#ifdef SK_DEBUG
1251void SkCanvas::validateClip() const {
1252 // construct clipRgn from the clipstack
1253 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001254 if (!device) {
1255 SkASSERT(this->getTotalClip().isEmpty());
1256 return;
1257 }
1258
reed@google.com819c9212011-02-23 18:56:55 +00001259 SkIRect ir;
1260 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001261 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001262
robertphillips@google.com80214e22012-07-20 15:33:18 +00001263 SkClipStack::B2TIter iter(fClipStack);
1264 const SkClipStack::B2TIter::Clip* clip;
reed@google.com819c9212011-02-23 18:56:55 +00001265 while ((clip = iter.next()) != NULL) {
1266 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001267 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001268 } else if (clip->fRect) {
1269 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001270 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001271 } else {
reed@google.com00177082011-10-12 14:34:30 +00001272 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001273 }
1274 }
1275
reed@google.com6f8f2922011-03-04 22:27:10 +00001276#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001277 // now compare against the current rgn
1278 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001279 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001280#endif
reed@google.com819c9212011-02-23 18:56:55 +00001281}
1282#endif
1283
reed@google.com90c07ea2012-04-13 13:50:27 +00001284void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001285 SkClipStack::B2TIter iter(fClipStack);
1286 const SkClipStack::B2TIter::Clip* clip;
reed@google.com90c07ea2012-04-13 13:50:27 +00001287
robertphillips@google.com7460b372012-04-25 16:54:51 +00001288 SkRect empty = { 0, 0, 0, 0 };
reed@google.com90c07ea2012-04-13 13:50:27 +00001289 while ((clip = iter.next()) != NULL) {
1290 if (clip->fPath) {
1291 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1292 } else if (clip->fRect) {
1293 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1294 } else {
1295 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1296 }
1297 }
1298}
1299
reed@google.com5c3d1472011-02-22 19:12:23 +00001300///////////////////////////////////////////////////////////////////////////////
1301
reed@google.com3b3e8952012-08-16 20:53:31 +00001302void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001304
reed@google.com3b3e8952012-08-16 20:53:31 +00001305 if (!this->getClipBounds(&r)) {
1306 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001308 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1309 SkScalarToCompareType(r.fTop),
1310 SkScalarToCompareType(r.fRight),
1311 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001312 }
1313}
1314
reed@google.com3b3e8952012-08-16 20:53:31 +00001315bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001316
reed@google.com16078632011-12-06 18:56:37 +00001317 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001318 return true;
1319
reed@google.com00177082011-10-12 14:34:30 +00001320 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 return true;
1322 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323
tomhudson@google.com8d430182011-06-06 19:11:19 +00001324 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001325 SkRect dst;
1326 fMCRec->fMatrix->mapRect(&dst, rect);
1327 SkIRect idst;
1328 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001329 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001330 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001331 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001332
reed@android.coma380ae42009-07-21 01:17:02 +00001333 // for speed, do the most likely reject compares first
1334 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1335 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1336 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1337 return true;
1338 }
1339 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1340 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1341 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1342 return true;
1343 }
1344 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001345 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001346}
1347
reed@google.com3b3e8952012-08-16 20:53:31 +00001348bool SkCanvas::quickReject(const SkPath& path) const {
1349 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001350}
1351
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001352static inline int pinIntForScalar(int x) {
1353#ifdef SK_SCALAR_IS_FIXED
1354 if (x < SK_MinS16) {
1355 x = SK_MinS16;
1356 } else if (x > SK_MaxS16) {
1357 x = SK_MaxS16;
1358 }
1359#endif
1360 return x;
1361}
1362
reed@google.com3b3e8952012-08-16 20:53:31 +00001363bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001364 SkIRect ibounds;
1365 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001366 return false;
1367 }
1368
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001369 SkMatrix inverse;
1370 // if we can't invert the CTM, we can't return local clip bounds
1371 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001372 if (bounds) {
1373 bounds->setEmpty();
1374 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001375 return false;
1376 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001378 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001379 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001380 // adjust it outwards in case we are antialiasing
1381 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001382
1383 // SkRect::iset() will correctly assert if we pass a value out of range
1384 // (when SkScalar==fixed), so we pin to legal values. This does not
1385 // really returnt the correct answer, but its the best we can do given
1386 // that we've promised to return SkRect (even though we support devices
1387 // that can be larger than 32K in width or height).
1388 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1389 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001390 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001391 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001392 inverse.mapRect(bounds, r);
1393 }
1394 return true;
1395}
1396
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001397bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001398 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001399 if (clip.isEmpty()) {
1400 if (bounds) {
1401 bounds->setEmpty();
1402 }
1403 return false;
1404 }
1405
1406 if (NULL != bounds) {
1407 *bounds = clip.getBounds();
1408 }
1409 return true;
1410}
1411
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412const SkMatrix& SkCanvas::getTotalMatrix() const {
1413 return *fMCRec->fMatrix;
1414}
1415
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001416SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001417 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1418 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001419 return kComplex_ClipType;
1420}
1421
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001423 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424}
1425
reed@android.comf2b98d62010-12-20 18:26:13 +00001426void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1427 if (NULL == matrix || matrix->isIdentity()) {
1428 if (fUseExternalMatrix) {
1429 fDeviceCMDirty = true;
1430 }
1431 fUseExternalMatrix = false;
1432 } else {
mike@reedtribe.org90bf4272012-04-14 19:06:16 +00001433 if (matrix->invert(&fExternalInverse)) {
1434 fExternalMatrix = *matrix;
1435 fUseExternalMatrix = true;
1436 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.comfc9a3be2012-04-12 13:52:14 +00001437 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001438 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001439}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440
bsalomon@google.come97f0852011-06-17 13:10:25 +00001441SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1442 int width, int height,
1443 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001444 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001445 if (device) {
1446 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1447 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001448 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001449 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001450 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451}
1452
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001453SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001454 int width, int height,
1455 bool isOpaque) {
1456 SkDevice* device = this->getDevice();
1457 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001458 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001459 } else {
1460 return NULL;
1461 }
1462}
1463
1464
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465//////////////////////////////////////////////////////////////////////////////
1466// These are the virtual drawing methods
1467//////////////////////////////////////////////////////////////////////////////
1468
reed@google.com2a981812011-04-14 18:59:28 +00001469void SkCanvas::clear(SkColor color) {
1470 SkDrawIter iter(this);
1471
1472 while (iter.next()) {
1473 iter.fDevice->clear(color);
1474 }
1475}
1476
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001478 this->internalDrawPaint(paint);
1479}
1480
1481void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001482 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483
1484 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001485 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001486 }
1487
reed@google.com4e2b3d32011-04-07 14:18:59 +00001488 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489}
1490
1491void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1492 const SkPaint& paint) {
1493 if ((long)count <= 0) {
1494 return;
1495 }
1496
reed@google.coma584aed2012-05-16 14:06:02 +00001497 if (paint.canComputeFastBounds()) {
1498 SkRect r;
1499 // special-case 2 points (common for drawing a single line)
1500 if (2 == count) {
1501 r.set(pts[0], pts[1]);
1502 } else {
1503 r.set(pts, count);
1504 }
1505 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001506 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001507 return;
1508 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001509 }
reed@google.coma584aed2012-05-16 14:06:02 +00001510
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511 SkASSERT(pts != NULL);
1512
reed@google.com4e2b3d32011-04-07 14:18:59 +00001513 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001514
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001516 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517 }
reed@google.com4b226022011-01-11 18:32:13 +00001518
reed@google.com4e2b3d32011-04-07 14:18:59 +00001519 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520}
1521
1522void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1523 if (paint.canComputeFastBounds()) {
1524 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001525 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001526 return;
1527 }
1528 }
reed@google.com4b226022011-01-11 18:32:13 +00001529
reed@google.com4e2b3d32011-04-07 14:18:59 +00001530 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531
1532 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001533 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534 }
1535
reed@google.com4e2b3d32011-04-07 14:18:59 +00001536 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537}
1538
1539void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001540 if (!path.isFinite()) {
1541 return;
1542 }
1543
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001544 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001545 SkRect storage;
1546 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001547 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548 return;
1549 }
1550 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001551 if (path.isEmpty()) {
1552 if (path.isInverseFillType()) {
1553 this->internalDrawPaint(paint);
1554 }
1555 return;
1556 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557
reed@google.com4e2b3d32011-04-07 14:18:59 +00001558 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001559
1560 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001561 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562 }
1563
reed@google.com4e2b3d32011-04-07 14:18:59 +00001564 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565}
1566
1567void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1568 const SkPaint* paint) {
1569 SkDEBUGCODE(bitmap.validate();)
1570
reed@google.com3d608122011-11-21 15:16:16 +00001571 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001572 SkRect bounds = {
1573 x, y,
1574 x + SkIntToScalar(bitmap.width()),
1575 y + SkIntToScalar(bitmap.height())
1576 };
1577 if (paint) {
1578 (void)paint->computeFastBounds(bounds, &bounds);
1579 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001580 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001581 return;
1582 }
1583 }
reed@google.com4b226022011-01-11 18:32:13 +00001584
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585 SkMatrix matrix;
1586 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001587 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588}
1589
reed@google.com9987ec32011-09-07 11:57:52 +00001590// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001591void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
reed@google.com9987ec32011-09-07 11:57:52 +00001592 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1594 return;
1595 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001596
reed@google.com3d608122011-11-21 15:16:16 +00001597 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001598 SkRect storage;
1599 const SkRect* bounds = &dst;
1600 if (paint) {
1601 bounds = &paint->computeFastBounds(dst, &storage);
1602 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001603 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001604 return;
1605 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606 }
reed@google.com3d608122011-11-21 15:16:16 +00001607
reed@google.com33535f32012-09-25 15:37:50 +00001608 SkLazyPaint lazy;
1609 if (NULL == paint) {
1610 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001612
reed@google.com33535f32012-09-25 15:37:50 +00001613 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001614
reed@google.com33535f32012-09-25 15:37:50 +00001615 while (iter.next()) {
1616 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
reed@android.comf2b98d62010-12-20 18:26:13 +00001617 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001618
reed@google.com33535f32012-09-25 15:37:50 +00001619 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620}
1621
reed@google.com71121732012-09-18 15:14:33 +00001622void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
1623 const SkRect& dst, const SkPaint* paint) {
reed@google.com9987ec32011-09-07 11:57:52 +00001624 SkDEBUGCODE(bitmap.validate();)
1625 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1626}
1627
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1629 const SkPaint* paint) {
1630 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001631 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632}
1633
reed@android.comf2b98d62010-12-20 18:26:13 +00001634void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1635 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001637
reed@google.com4e2b3d32011-04-07 14:18:59 +00001638 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001639
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001641 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001642 }
reed@android.com9b039062009-02-11 15:09:58 +00001643
reed@google.com4e2b3d32011-04-07 14:18:59 +00001644 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645}
1646
reed@google.com9987ec32011-09-07 11:57:52 +00001647void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1648 const SkIRect& center, const SkRect& dst,
1649 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001650 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001651 SkRect storage;
1652 const SkRect* bounds = &dst;
1653 if (paint) {
1654 bounds = &paint->computeFastBounds(dst, &storage);
1655 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001656 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001657 return;
1658 }
1659 }
1660
reed@google.com9987ec32011-09-07 11:57:52 +00001661 const int32_t w = bitmap.width();
1662 const int32_t h = bitmap.height();
1663
1664 SkIRect c = center;
1665 // pin center to the bounds of the bitmap
1666 c.fLeft = SkMax32(0, center.fLeft);
1667 c.fTop = SkMax32(0, center.fTop);
1668 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1669 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1670
reed@google.com71121732012-09-18 15:14:33 +00001671 const SkScalar srcX[4] = {
1672 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), w
1673 };
1674 const SkScalar srcY[4] = {
1675 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), h
1676 };
reed@google.com9987ec32011-09-07 11:57:52 +00001677 SkScalar dstX[4] = {
1678 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1679 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1680 };
1681 SkScalar dstY[4] = {
1682 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1683 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1684 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001685
reed@google.com9987ec32011-09-07 11:57:52 +00001686 if (dstX[1] > dstX[2]) {
1687 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1688 dstX[2] = dstX[1];
1689 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001690
reed@google.com9987ec32011-09-07 11:57:52 +00001691 if (dstY[1] > dstY[2]) {
1692 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1693 dstY[2] = dstY[1];
1694 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001695
reed@google.com9987ec32011-09-07 11:57:52 +00001696 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001697 SkRect s, d;
1698
reed@google.com9987ec32011-09-07 11:57:52 +00001699 s.fTop = srcY[y];
1700 s.fBottom = srcY[y+1];
1701 d.fTop = dstY[y];
1702 d.fBottom = dstY[y+1];
1703 for (int x = 0; x < 3; x++) {
1704 s.fLeft = srcX[x];
1705 s.fRight = srcX[x+1];
1706 d.fLeft = dstX[x];
1707 d.fRight = dstX[x+1];
1708 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1709 }
1710 }
1711}
1712
1713void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1714 const SkRect& dst, const SkPaint* paint) {
1715 SkDEBUGCODE(bitmap.validate();)
1716
1717 // Need a device entry-point, so gpu can use a mesh
1718 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1719}
1720
reed@google.comf67e4cf2011-03-15 20:56:58 +00001721class SkDeviceFilteredPaint {
1722public:
1723 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1724 SkDevice::TextFlags flags;
1725 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001726 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001727 newPaint->setFlags(flags.fFlags);
1728 newPaint->setHinting(flags.fHinting);
1729 fPaint = newPaint;
1730 } else {
1731 fPaint = &paint;
1732 }
1733 }
1734
reed@google.comf67e4cf2011-03-15 20:56:58 +00001735 const SkPaint& paint() const { return *fPaint; }
1736
1737private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001738 const SkPaint* fPaint;
1739 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001740};
1741
bungeman@google.com52c748b2011-08-22 21:30:43 +00001742void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1743 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001744 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001745 draw.fDevice->drawRect(draw, r, paint);
1746 } else {
1747 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001748 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001749 draw.fDevice->drawRect(draw, r, p);
1750 }
1751}
1752
1753void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1754 const char text[], size_t byteLength,
1755 SkScalar x, SkScalar y) {
1756 SkASSERT(byteLength == 0 || text != NULL);
1757
1758 // nothing to draw
1759 if (text == NULL || byteLength == 0 ||
1760 draw.fClip->isEmpty() ||
1761 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1762 return;
1763 }
1764
1765 SkScalar width = 0;
1766 SkPoint start;
1767
1768 start.set(0, 0); // to avoid warning
1769 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1770 SkPaint::kStrikeThruText_Flag)) {
1771 width = paint.measureText(text, byteLength);
1772
1773 SkScalar offsetX = 0;
1774 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1775 offsetX = SkScalarHalf(width);
1776 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1777 offsetX = width;
1778 }
1779 start.set(x - offsetX, y);
1780 }
1781
1782 if (0 == width) {
1783 return;
1784 }
1785
1786 uint32_t flags = paint.getFlags();
1787
1788 if (flags & (SkPaint::kUnderlineText_Flag |
1789 SkPaint::kStrikeThruText_Flag)) {
1790 SkScalar textSize = paint.getTextSize();
1791 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1792 SkRect r;
1793
1794 r.fLeft = start.fX;
1795 r.fRight = start.fX + width;
1796
1797 if (flags & SkPaint::kUnderlineText_Flag) {
1798 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1799 start.fY);
1800 r.fTop = offset;
1801 r.fBottom = offset + height;
1802 DrawRect(draw, paint, r, textSize);
1803 }
1804 if (flags & SkPaint::kStrikeThruText_Flag) {
1805 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1806 start.fY);
1807 r.fTop = offset;
1808 r.fBottom = offset + height;
1809 DrawRect(draw, paint, r, textSize);
1810 }
1811 }
1812}
1813
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814void SkCanvas::drawText(const void* text, size_t byteLength,
1815 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001816 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817
1818 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001819 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001820 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001821 DrawTextDecorations(iter, dfp.paint(),
1822 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823 }
1824
reed@google.com4e2b3d32011-04-07 14:18:59 +00001825 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826}
1827
1828void SkCanvas::drawPosText(const void* text, size_t byteLength,
1829 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001830 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001831
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001833 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001835 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836 }
reed@google.com4b226022011-01-11 18:32:13 +00001837
reed@google.com4e2b3d32011-04-07 14:18:59 +00001838 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001839}
1840
1841void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1842 const SkScalar xpos[], SkScalar constY,
1843 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001844 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001845
reed@android.com8a1c16f2008-12-17 15:59:43 +00001846 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001847 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001848 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001849 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001850 }
reed@google.com4b226022011-01-11 18:32:13 +00001851
reed@google.com4e2b3d32011-04-07 14:18:59 +00001852 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001853}
1854
1855void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1856 const SkPath& path, const SkMatrix* matrix,
1857 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001858 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001859
1860 while (iter.next()) {
1861 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001862 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863 }
1864
reed@google.com4e2b3d32011-04-07 14:18:59 +00001865 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001866}
1867
djsollen@google.com56c69772011-11-08 19:00:26 +00001868#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001869void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1870 const SkPoint pos[], const SkPaint& paint,
1871 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001872 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001873
1874 while (iter.next()) {
1875 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001876 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001877 }
1878
reed@google.com4e2b3d32011-04-07 14:18:59 +00001879 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001880}
1881#endif
1882
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1884 const SkPoint verts[], const SkPoint texs[],
1885 const SkColor colors[], SkXfermode* xmode,
1886 const uint16_t indices[], int indexCount,
1887 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001888 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001889
reed@android.com8a1c16f2008-12-17 15:59:43 +00001890 while (iter.next()) {
1891 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001892 colors, xmode, indices, indexCount,
1893 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001894 }
reed@google.com4b226022011-01-11 18:32:13 +00001895
reed@google.com4e2b3d32011-04-07 14:18:59 +00001896 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001897}
1898
reed@android.comcb608442009-12-04 21:32:27 +00001899void SkCanvas::drawData(const void* data, size_t length) {
1900 // do nothing. Subclasses may do something with the data
1901}
1902
reed@android.com8a1c16f2008-12-17 15:59:43 +00001903//////////////////////////////////////////////////////////////////////////////
1904// These methods are NOT virtual, and therefore must call back into virtual
1905// methods, rather than actually drawing themselves.
1906//////////////////////////////////////////////////////////////////////////////
1907
1908void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001909 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001910 SkPaint paint;
1911
1912 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001913 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001914 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915 }
1916 this->drawPaint(paint);
1917}
1918
reed@android.com845fdac2009-06-23 03:01:32 +00001919void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920 SkPaint paint;
1921
1922 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001923 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001924 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925 }
1926 this->drawPaint(paint);
1927}
1928
1929void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1930 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001931
reed@android.com8a1c16f2008-12-17 15:59:43 +00001932 pt.set(x, y);
1933 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1934}
1935
1936void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1937 SkPoint pt;
1938 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001939
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940 pt.set(x, y);
1941 paint.setColor(color);
1942 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1943}
1944
1945void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1946 const SkPaint& paint) {
1947 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001948
reed@android.com8a1c16f2008-12-17 15:59:43 +00001949 pts[0].set(x0, y0);
1950 pts[1].set(x1, y1);
1951 this->drawPoints(kLines_PointMode, 2, pts, paint);
1952}
1953
1954void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1955 SkScalar right, SkScalar bottom,
1956 const SkPaint& paint) {
1957 SkRect r;
1958
1959 r.set(left, top, right, bottom);
1960 this->drawRect(r, paint);
1961}
1962
1963void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1964 const SkPaint& paint) {
1965 if (radius < 0) {
1966 radius = 0;
1967 }
1968
1969 SkRect r;
1970 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001971
reed@android.com8a1c16f2008-12-17 15:59:43 +00001972 if (paint.canComputeFastBounds()) {
1973 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001974 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975 return;
1976 }
1977 }
reed@google.com4b226022011-01-11 18:32:13 +00001978
reed@android.com8a1c16f2008-12-17 15:59:43 +00001979 SkPath path;
1980 path.addOval(r);
1981 this->drawPath(path, paint);
1982}
1983
1984void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1985 const SkPaint& paint) {
1986 if (rx > 0 && ry > 0) {
1987 if (paint.canComputeFastBounds()) {
1988 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001989 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990 return;
1991 }
1992 }
1993
1994 SkPath path;
1995 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1996 this->drawPath(path, paint);
1997 } else {
1998 this->drawRect(r, paint);
1999 }
2000}
2001
2002void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
2003 if (paint.canComputeFastBounds()) {
2004 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002005 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002006 return;
2007 }
2008 }
2009
2010 SkPath path;
2011 path.addOval(oval);
2012 this->drawPath(path, paint);
2013}
2014
2015void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2016 SkScalar sweepAngle, bool useCenter,
2017 const SkPaint& paint) {
2018 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2019 this->drawOval(oval, paint);
2020 } else {
2021 SkPath path;
2022 if (useCenter) {
2023 path.moveTo(oval.centerX(), oval.centerY());
2024 }
2025 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2026 if (useCenter) {
2027 path.close();
2028 }
2029 this->drawPath(path, paint);
2030 }
2031}
2032
2033void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2034 const SkPath& path, SkScalar hOffset,
2035 SkScalar vOffset, const SkPaint& paint) {
2036 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002037
reed@android.com8a1c16f2008-12-17 15:59:43 +00002038 matrix.setTranslate(hOffset, vOffset);
2039 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2040}
2041
reed@android.comf76bacf2009-05-13 14:00:33 +00002042///////////////////////////////////////////////////////////////////////////////
2043
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002045 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002046}
2047
reed@android.com8a1c16f2008-12-17 15:59:43 +00002048///////////////////////////////////////////////////////////////////////////////
2049///////////////////////////////////////////////////////////////////////////////
2050
2051SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002052 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002053
2054 SkASSERT(canvas);
2055
2056 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2057 fDone = !fImpl->next();
2058}
2059
2060SkCanvas::LayerIter::~LayerIter() {
2061 fImpl->~SkDrawIter();
2062}
2063
2064void SkCanvas::LayerIter::next() {
2065 fDone = !fImpl->next();
2066}
2067
2068SkDevice* SkCanvas::LayerIter::device() const {
2069 return fImpl->getDevice();
2070}
2071
2072const SkMatrix& SkCanvas::LayerIter::matrix() const {
2073 return fImpl->getMatrix();
2074}
2075
2076const SkPaint& SkCanvas::LayerIter::paint() const {
2077 const SkPaint* paint = fImpl->getPaint();
2078 if (NULL == paint) {
2079 paint = &fDefaultPaint;
2080 }
2081 return *paint;
2082}
2083
2084const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2085int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2086int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002087
2088///////////////////////////////////////////////////////////////////////////////
2089
2090SkCanvas::ClipVisitor::~ClipVisitor() { }
reed@google.com97af1a62012-08-28 12:19:02 +00002091
2092