blob: f5c6bc2a655572a6093f66ba6ce5800c73e2fbfa [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
reed@android.com8a1c16f2008-12-17 15:59:43 +000010#include "SkCanvas.h"
11#include "SkBounder.h"
12#include "SkDevice.h"
13#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
16#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000017#include "SkRasterClip.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkScalarCompare.h"
reed@google.com97af1a62012-08-28 12:19:02 +000019#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000021#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000022#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024
reed@google.com82ce2b82012-06-26 17:43:26 +000025SK_DEFINE_INST_COUNT(SkBounder)
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000026SK_DEFINE_INST_COUNT(SkCanvas)
reed@google.com82ce2b82012-06-26 17:43:26 +000027SK_DEFINE_INST_COUNT(SkDrawFilter)
28
reed@google.comda17f752012-08-16 18:27:05 +000029// experimental for faster tiled drawing...
30//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000031
reed@android.com8a1c16f2008-12-17 15:59:43 +000032//#define SK_TRACE_SAVERESTORE
33
34#ifdef SK_TRACE_SAVERESTORE
35 static int gLayerCounter;
36 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
37 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
38
39 static int gRecCounter;
40 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
41 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
42
43 static int gCanvasCounter;
44 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
45 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
46#else
47 #define inc_layer()
48 #define dec_layer()
49 #define inc_rec()
50 #define dec_rec()
51 #define inc_canvas()
52 #define dec_canvas()
53#endif
54
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000055typedef SkTLazy<SkPaint> SkLazyPaint;
56
reed@google.com97af1a62012-08-28 12:19:02 +000057void SkCanvas::predrawNotify() {
58 if (fSurfaceBase) {
59 fSurfaceBase->aboutToDraw(this);
60 }
61}
62
reed@android.com8a1c16f2008-12-17 15:59:43 +000063///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000064
65/* This is the record we keep for each SkDevice that the user installs.
66 The clip/matrix/proc are fields that reflect the top of the save/restore
67 stack. Whenever the canvas changes, it marks a dirty flag, and then before
68 these are used (assuming we're not on a layer) we rebuild these cache
69 values: they reflect the top of the save stack, but translated and clipped
70 by the device's XY offset and bitmap-bounds.
71*/
72struct DeviceCM {
73 DeviceCM* fNext;
74 SkDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000075 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000076 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000077 SkPaint* fPaint; // may be null (in the future)
reed@android.comf2b98d62010-12-20 18:26:13 +000078 // optional, related to canvas' external matrix
79 const SkMatrix* fMVMatrix;
80 const SkMatrix* fExtMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000082 DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 : fNext(NULL) {
84 if (NULL != device) {
85 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000086 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 }
reed@google.com4b226022011-01-11 18:32:13 +000088 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000090 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000091
bungeman@google.com88edf1e2011-08-08 19:41:56 +000092 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000094 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 fDevice->unref();
96 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000097 SkDELETE(fPaint);
98 }
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@google.com045e62d2011-10-24 12:19:46 +0000100 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
101 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000102 int x = fDevice->getOrigin().x();
103 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 int width = fDevice->width();
105 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 if ((x | y) == 0) {
108 fMatrix = &totalMatrix;
109 fClip = totalClip;
110 } else {
111 fMatrixStorage = totalMatrix;
112 fMatrixStorage.postTranslate(SkIntToScalar(-x),
113 SkIntToScalar(-y));
114 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 totalClip.translate(-x, -y, &fClip);
117 }
118
reed@google.com045e62d2011-10-24 12:19:46 +0000119 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120
121 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000122
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000124 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 SkRegion::kDifference_Op);
126 }
reed@google.com4b226022011-01-11 18:32:13 +0000127
reed@google.com045e62d2011-10-24 12:19:46 +0000128 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129
130#ifdef SK_DEBUG
131 if (!fClip.isEmpty()) {
132 SkIRect deviceR;
133 deviceR.set(0, 0, width, height);
134 SkASSERT(deviceR.contains(fClip.getBounds()));
135 }
136#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000137 // default is to assume no external matrix
138 fMVMatrix = NULL;
139 fExtMatrix = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 }
reed@android.comf2b98d62010-12-20 18:26:13 +0000141
142 // can only be called after calling updateMC()
143 void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
144 fMVMatrixStorage.setConcat(extI, *fMatrix);
145 fMVMatrix = &fMVMatrixStorage;
146 fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
147 }
148
reed@android.com8a1c16f2008-12-17 15:59:43 +0000149private:
reed@android.comf2b98d62010-12-20 18:26:13 +0000150 SkMatrix fMatrixStorage, fMVMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000151};
152
153/* This is the record we keep for each save/restore level in the stack.
154 Since a level optionally copies the matrix and/or stack, we have pointers
155 for these fields. If the value is copied for this level, the copy is
156 stored in the ...Storage field, and the pointer points to that. If the
157 value is not copied for this level, we ignore ...Storage, and just point
158 at the corresponding value in the previous level in the stack.
159*/
160class SkCanvas::MCRec {
161public:
162 MCRec* fNext;
reed@google.com00177082011-10-12 14:34:30 +0000163 SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
164 SkRasterClip* fRasterClip; // points to either fRegionStorage or prev MCRec
165 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000166
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 DeviceCM* fLayer;
168 /* If there are any layers in the stack, this points to the top-most
169 one that is at or below this level in the stack (so we know what
170 bitmap/device to draw into from this level. This value is NOT
171 reference counted, since the real owner is either our fLayer field,
172 or a previous one in a lower level.)
173 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000174 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175
176 MCRec(const MCRec* prev, int flags) {
177 if (NULL != prev) {
178 if (flags & SkCanvas::kMatrix_SaveFlag) {
179 fMatrixStorage = *prev->fMatrix;
180 fMatrix = &fMatrixStorage;
181 } else {
182 fMatrix = prev->fMatrix;
183 }
reed@google.com4b226022011-01-11 18:32:13 +0000184
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 if (flags & SkCanvas::kClip_SaveFlag) {
reed@google.com00177082011-10-12 14:34:30 +0000186 fRasterClipStorage = *prev->fRasterClip;
187 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 } else {
reed@google.com00177082011-10-12 14:34:30 +0000189 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190 }
191
192 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000193 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
195 fTopLayer = prev->fTopLayer;
196 } else { // no prev
197 fMatrixStorage.reset();
reed@google.com4b226022011-01-11 18:32:13 +0000198
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 fMatrix = &fMatrixStorage;
reed@google.com00177082011-10-12 14:34:30 +0000200 fRasterClip = &fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 fFilter = NULL;
202 fTopLayer = NULL;
203 }
204 fLayer = NULL;
205
206 // don't bother initializing fNext
207 inc_rec();
208 }
209 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000210 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 SkDELETE(fLayer);
212 dec_rec();
213 }
reed@google.com4b226022011-01-11 18:32:13 +0000214
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215private:
reed@google.com00177082011-10-12 14:34:30 +0000216 SkMatrix fMatrixStorage;
217 SkRasterClip fRasterClipStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218};
219
220class SkDrawIter : public SkDraw {
221public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000222 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000223 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000224 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 canvas->updateDeviceCMCache();
226
reed@google.com90c07ea2012-04-13 13:50:27 +0000227 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 fBounder = canvas->getBounder();
229 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000230 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 }
reed@google.com4b226022011-01-11 18:32:13 +0000232
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 bool next() {
234 // skip over recs with empty clips
235 if (fSkipEmptyClips) {
236 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
237 fCurrLayer = fCurrLayer->fNext;
238 }
239 }
240
reed@google.comf68c5e22012-02-24 16:38:58 +0000241 const DeviceCM* rec = fCurrLayer;
242 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243
244 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000245 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
246 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 fDevice = rec->fDevice;
248 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000250 fMVMatrix = rec->fMVMatrix;
251 fExtMatrix = rec->fExtMatrix;
252 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253
254 fCurrLayer = rec->fNext;
255 if (fBounder) {
256 fBounder->setClip(fClip);
257 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000259
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000260 fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 return true;
262 }
263 return false;
264 }
reed@google.com4b226022011-01-11 18:32:13 +0000265
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 SkDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000267 int getX() const { return fDevice->getOrigin().x(); }
268 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 const SkMatrix& getMatrix() const { return *fMatrix; }
270 const SkRegion& getClip() const { return *fClip; }
271 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000272
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273private:
274 SkCanvas* fCanvas;
275 const DeviceCM* fCurrLayer;
276 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277 SkBool8 fSkipEmptyClips;
278
279 typedef SkDraw INHERITED;
280};
281
282/////////////////////////////////////////////////////////////////////////////
283
284class AutoDrawLooper {
285public:
reed@google.com8926b162012-03-23 15:36:36 +0000286 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
287 bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 fCanvas = canvas;
289 fLooper = paint.getLooper();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000291 fPaint = NULL;
292 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000293 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000294 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295
reed@google.com8926b162012-03-23 15:36:36 +0000296 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
297 SkPaint tmp;
298 tmp.setImageFilter(fOrigPaint.getImageFilter());
299 // it would be nice if we had a guess at the bounds, instead of null
300 (void)canvas->internalSaveLayer(NULL, &tmp,
301 SkCanvas::kARGB_ClipLayer_SaveFlag, true);
302 // we'll clear the imageFilter for the actual draws in next(), so
303 // it will only be applied during the restore().
304 fDoClearImageFilter = true;
305 }
306
reed@google.com4e2b3d32011-04-07 14:18:59 +0000307 if (fLooper) {
308 fLooper->init(canvas);
reed@google.com129ec222012-05-15 13:24:09 +0000309 fIsSimple = false;
310 } else {
311 // can we be marked as simple?
312 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313 }
314 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000315
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000317 if (fDoClearImageFilter) {
318 fCanvas->internalRestore();
319 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000320 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000322
reed@google.com4e2b3d32011-04-07 14:18:59 +0000323 const SkPaint& paint() const {
324 SkASSERT(fPaint);
325 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000327
reed@google.com129ec222012-05-15 13:24:09 +0000328 bool next(SkDrawFilter::Type drawType) {
329 if (fDone) {
330 return false;
331 } else if (fIsSimple) {
332 fDone = true;
333 fPaint = &fOrigPaint;
334 return !fPaint->nothingToDraw();
335 } else {
336 return this->doNext(drawType);
337 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000338 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000341 SkLazyPaint fLazyPaint;
342 SkCanvas* fCanvas;
343 const SkPaint& fOrigPaint;
344 SkDrawLooper* fLooper;
345 SkDrawFilter* fFilter;
346 const SkPaint* fPaint;
347 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000348 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000349 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000350 bool fIsSimple;
351
352 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353};
354
reed@google.com129ec222012-05-15 13:24:09 +0000355bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000356 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000357 SkASSERT(!fIsSimple);
358 SkASSERT(fLooper || fFilter || fDoClearImageFilter);
359
360 SkPaint* paint = fLazyPaint.set(fOrigPaint);
361
362 if (fDoClearImageFilter) {
363 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000364 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000365
reed@google.com129ec222012-05-15 13:24:09 +0000366 if (fLooper && !fLooper->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000367 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000368 return false;
369 }
370 if (fFilter) {
371 fFilter->filter(paint, drawType);
372 if (NULL == fLooper) {
373 // no looper means we only draw once
374 fDone = true;
375 }
376 }
377 fPaint = paint;
378
379 // if we only came in here for the imagefilter, mark us as done
380 if (!fLooper && !fFilter) {
381 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000382 }
383
384 // call this after any possible paint modifiers
385 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000386 fPaint = NULL;
387 return false;
388 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000389 return true;
390}
391
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392/* Stack helper for managing a SkBounder. In the destructor, if we were
393 given a bounder, we call its commit() method, signifying that we are
394 done accumulating bounds for that draw.
395*/
396class SkAutoBounderCommit {
397public:
398 SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
399 ~SkAutoBounderCommit() {
400 if (NULL != fBounder) {
401 fBounder->commit();
402 }
403 }
404private:
405 SkBounder* fBounder;
406};
407
408#include "SkColorPriv.h"
409
410class AutoValidator {
411public:
412 AutoValidator(SkDevice* device) : fDevice(device) {}
413 ~AutoValidator() {
414#ifdef SK_DEBUG
415 const SkBitmap& bm = fDevice->accessBitmap(false);
416 if (bm.config() == SkBitmap::kARGB_4444_Config) {
417 for (int y = 0; y < bm.height(); y++) {
418 const SkPMColor16* p = bm.getAddr16(0, y);
419 for (int x = 0; x < bm.width(); x++) {
420 SkPMColor16 c = p[x];
421 SkPMColor16Assert(c);
422 }
423 }
424 }
425#endif
426 }
427private:
428 SkDevice* fDevice;
429};
430
431////////// macros to place around the internal draw calls //////////////////
432
reed@google.com8926b162012-03-23 15:36:36 +0000433#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
434/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000435 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000436 AutoDrawLooper looper(this, paint, true); \
437 while (looper.next(type)) { \
438 SkAutoBounderCommit ac(fBounder); \
439 SkDrawIter iter(this);
440
reed@google.com4e2b3d32011-04-07 14:18:59 +0000441#define LOOPER_BEGIN(paint, type) \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
reed@google.com97af1a62012-08-28 12:19:02 +0000443 this->predrawNotify(); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000444 AutoDrawLooper looper(this, paint); \
445 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 SkAutoBounderCommit ac(fBounder); \
447 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000448
reed@google.com4e2b3d32011-04-07 14:18:59 +0000449#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450
451////////////////////////////////////////////////////////////////////////////
452
453SkDevice* SkCanvas::init(SkDevice* device) {
454 fBounder = NULL;
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000455 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456 fLocalBoundsCompareTypeDirty = true;
reed@android.com199f1082009-06-10 02:12:47 +0000457 fLastDeviceToGainFocus = NULL;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000458 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000459 fSaveLayerCount = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460
461 fMCRec = (MCRec*)fMCStack.push_back();
462 new (fMCRec) MCRec(NULL, 0);
463
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000464 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000465 fMCRec->fTopLayer = fMCRec->fLayer;
466 fMCRec->fNext = NULL;
467
vandebo@chromium.org3c898182011-06-22 05:01:28 +0000468 fExternalMatrix.reset();
469 fExternalInverse.reset();
reed@android.comf2b98d62010-12-20 18:26:13 +0000470 fUseExternalMatrix = false;
reed@google.com97af1a62012-08-28 12:19:02 +0000471
472 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000473
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474 return this->setDevice(device);
475}
476
reed@google.comcde92112011-07-06 20:00:52 +0000477SkCanvas::SkCanvas()
478: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000479 inc_canvas();
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000480
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000481 this->init(NULL);
482}
483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484SkCanvas::SkCanvas(SkDevice* device)
mike@reedtribe.orgea4ac972011-04-26 11:48:33 +0000485 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000486 inc_canvas();
487
488 this->init(device);
489}
490
491SkCanvas::SkCanvas(const SkBitmap& bitmap)
492 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
493 inc_canvas();
494
reed@google.comcde92112011-07-06 20:00:52 +0000495 this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496}
497
498SkCanvas::~SkCanvas() {
499 // free up the contents of our deque
500 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000501 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000502
reed@android.com8a1c16f2008-12-17 15:59:43 +0000503 this->internalRestore(); // restore the last, since we're going away
504
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000505 SkSafeUnref(fBounder);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000506
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 dec_canvas();
508}
509
510SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
511 SkRefCnt_SafeAssign(fBounder, bounder);
512 return bounder;
513}
514
515SkDrawFilter* SkCanvas::getDrawFilter() const {
516 return fMCRec->fFilter;
517}
518
519SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
520 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
521 return filter;
522}
523
524///////////////////////////////////////////////////////////////////////////////
525
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000526void SkCanvas::flush() {
527 SkDevice* device = this->getDevice();
528 if (device) {
529 device->flush();
530 }
531}
532
reed@google.com210ce002011-11-01 14:24:23 +0000533SkISize SkCanvas::getDeviceSize() const {
534 SkDevice* d = this->getDevice();
535 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
536}
537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538SkDevice* SkCanvas::getDevice() const {
539 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000540 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000541 SkASSERT(rec && rec->fLayer);
542 return rec->fLayer->fDevice;
543}
544
reed@google.com0b53d592012-03-19 18:26:34 +0000545SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
546 if (updateMatrixClip) {
547 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
548 }
reed@google.com9266fed2011-03-30 00:18:03 +0000549 return fMCRec->fTopLayer->fDevice;
550}
551
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552SkDevice* SkCanvas::setDevice(SkDevice* device) {
553 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000554 SkDeque::F2BIter iter(fMCStack);
555 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000556 SkASSERT(rec && rec->fLayer);
557 SkDevice* rootDevice = rec->fLayer->fDevice;
558
559 if (rootDevice == device) {
560 return device;
561 }
reed@google.com4b226022011-01-11 18:32:13 +0000562
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000564 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000565 }
566 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000567 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000568 }
569
570 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
571 rootDevice = device;
572
573 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 /* Now we update our initial region to have the bounds of the new device,
576 and then intersect all of the clips in our stack with these bounds,
577 to ensure that we can't draw outside of the device's bounds (and trash
578 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000579
reed@android.com8a1c16f2008-12-17 15:59:43 +0000580 NOTE: this is only a partial-fix, since if the new device is larger than
581 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000582 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000583 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
584 reconstruct the correct clips, so this approximation will have to do.
585 The caller really needs to restore() back to the base if they want to
586 accurately take advantage of the new device bounds.
587 */
588
reed@google.com42aea282012-03-28 16:19:15 +0000589 SkIRect bounds;
590 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000592 } else {
593 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594 }
reed@google.com42aea282012-03-28 16:19:15 +0000595 // now jam our 1st clip to be bounds, and intersect the rest with that
596 rec->fRasterClip->setRect(bounds);
597 while ((rec = (MCRec*)iter.next()) != NULL) {
598 (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
599 }
600
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 return device;
602}
603
reed@google.comaf951c92011-06-16 19:10:39 +0000604SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
605 SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606 device->unref();
607 return device;
608}
609
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000610bool SkCanvas::readPixels(SkBitmap* bitmap,
611 int x, int y,
612 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000613 SkDevice* device = this->getDevice();
614 if (!device) {
615 return false;
616 }
bsalomon@google.com6850eab2011-11-03 20:29:47 +0000617 return device->readPixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000618}
619
bsalomon@google.comc6980972011-11-02 19:57:21 +0000620bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
bsalomon@google.comace7bd52011-11-02 19:39:51 +0000621 SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000622 if (!device) {
623 return false;
624 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000625
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000626 SkIRect bounds;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000627 bounds.set(0, 0, device->width(), device->height());
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000628 if (!bounds.intersect(srcRect)) {
629 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000630 }
631
632 SkBitmap tmp;
633 tmp.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(),
634 bounds.height());
635 if (this->readPixels(&tmp, bounds.fLeft, bounds.fTop)) {
636 bitmap->swap(tmp);
637 return true;
638 } else {
reed@google.com51df9e32010-12-23 19:29:18 +0000639 return false;
640 }
reed@google.com51df9e32010-12-23 19:29:18 +0000641}
642
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000643void SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y,
644 Config8888 config8888) {
reed@google.com51df9e32010-12-23 19:29:18 +0000645 SkDevice* device = this->getDevice();
646 if (device) {
bsalomon@google.comd58a1cd2011-11-10 20:57:43 +0000647 device->writePixels(bitmap, x, y, config8888);
reed@google.com51df9e32010-12-23 19:29:18 +0000648 }
649}
650
junov@google.com4370aed2012-01-18 16:21:08 +0000651SkCanvas* SkCanvas::canvasForDrawIter() {
652 return this;
653}
654
reed@android.com8a1c16f2008-12-17 15:59:43 +0000655//////////////////////////////////////////////////////////////////////////////
656
reed@android.com8a1c16f2008-12-17 15:59:43 +0000657void SkCanvas::updateDeviceCMCache() {
658 if (fDeviceCMDirty) {
659 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed@google.com045e62d2011-10-24 12:19:46 +0000660 const SkRasterClip& totalClip = *fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000662
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000664 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.comf2b98d62010-12-20 18:26:13 +0000665 if (fUseExternalMatrix) {
666 layer->updateExternalMatrix(fExternalMatrix,
667 fExternalInverse);
668 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000670 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000671 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000672 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.comf2b98d62010-12-20 18:26:13 +0000673 if (fUseExternalMatrix) {
674 layer->updateExternalMatrix(fExternalMatrix,
675 fExternalInverse);
676 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677 } while ((layer = layer->fNext) != NULL);
678 }
679 fDeviceCMDirty = false;
680 }
681}
682
reed@android.comf2b98d62010-12-20 18:26:13 +0000683void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000684 const SkRegion& clip) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685 SkASSERT(device);
reed@android.com199f1082009-06-10 02:12:47 +0000686 if (fLastDeviceToGainFocus != device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000687 device->gainFocus(matrix, clip);
reed@android.com199f1082009-06-10 02:12:47 +0000688 fLastDeviceToGainFocus = device;
689 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690}
691
692///////////////////////////////////////////////////////////////////////////////
693
694int SkCanvas::internalSave(SaveFlags flags) {
695 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000696
reed@android.com8a1c16f2008-12-17 15:59:43 +0000697 MCRec* newTop = (MCRec*)fMCStack.push_back();
698 new (newTop) MCRec(fMCRec, flags); // balanced in restore()
reed@google.com4b226022011-01-11 18:32:13 +0000699
reed@android.com8a1c16f2008-12-17 15:59:43 +0000700 newTop->fNext = fMCRec;
701 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000702
reed@google.com5c3d1472011-02-22 19:12:23 +0000703 fClipStack.save();
704 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
705
reed@android.com8a1c16f2008-12-17 15:59:43 +0000706 return saveCount;
707}
708
709int SkCanvas::save(SaveFlags flags) {
710 // call shared impl
711 return this->internalSave(flags);
712}
713
714#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
715#define C16MASK (1 << SkBitmap::kRGB_565_Config)
716#define C8MASK (1 << SkBitmap::kA8_Config)
717
718static SkBitmap::Config resolve_config(SkCanvas* canvas,
719 const SkIRect& bounds,
720 SkCanvas::SaveFlags flags,
721 bool* isOpaque) {
722 *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
723
724#if 0
725 // loop through and union all the configs we may draw into
726 uint32_t configMask = 0;
727 for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
728 {
729 SkDevice* device = canvas->getLayerDevice(i);
730 if (device->intersects(bounds))
731 configMask |= 1 << device->config();
732 }
733
734 // if the caller wants alpha or fullcolor, we can't return 565
735 if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
736 SkCanvas::kHasAlphaLayer_SaveFlag))
737 configMask &= ~C16MASK;
738
739 switch (configMask) {
740 case C8MASK: // if we only have A8, return that
741 return SkBitmap::kA8_Config;
742
743 case C16MASK: // if we only have 565, return that
744 return SkBitmap::kRGB_565_Config;
745
746 default:
747 return SkBitmap::kARGB_8888_Config; // default answer
748 }
749#else
750 return SkBitmap::kARGB_8888_Config; // default answer
751#endif
752}
753
754static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
755 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
756}
757
junov@chromium.orga907ac32012-02-24 21:54:07 +0000758bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
759 SkIRect* intersection) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000760 SkIRect clipBounds;
761 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000762 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000763 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000764 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000765 if (NULL != bounds) {
766 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000767
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768 this->getTotalMatrix().mapRect(&r, *bounds);
769 r.roundOut(&ir);
770 // early exit if the layer's bounds are clipped out
771 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000772 if (bounds_affects_clip(flags)) {
reed@google.com00177082011-10-12 14:34:30 +0000773 fMCRec->fRasterClip->setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000774 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000775 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 }
777 } else { // no user bounds, so just use the clip
778 ir = clipBounds;
779 }
780
reed@google.com5c3d1472011-02-22 19:12:23 +0000781 fClipStack.clipDevRect(ir, SkRegion::kIntersect_Op);
junov@chromium.orga907ac32012-02-24 21:54:07 +0000782
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 // early exit if the clip is now empty
784 if (bounds_affects_clip(flags) &&
reed@google.com00177082011-10-12 14:34:30 +0000785 !fMCRec->fRasterClip->op(ir, SkRegion::kIntersect_Op)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000786 return false;
787 }
788
789 if (intersection) {
790 *intersection = ir;
791 }
792 return true;
793}
794
795int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
796 SaveFlags flags) {
reed@google.com8926b162012-03-23 15:36:36 +0000797 return this->internalSaveLayer(bounds, paint, flags, false);
798}
799
800int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
801 SaveFlags flags, bool justForImageFilter) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000802 // do this before we create the layer. We don't call the public save() since
803 // that would invoke a possibly overridden virtual
804 int count = this->internalSave(flags);
805
806 fDeviceCMDirty = true;
807
808 SkIRect ir;
809 if (!this->clipRectBounds(bounds, flags, &ir)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 return count;
811 }
812
reed@google.comb55deeb2012-01-06 14:43:09 +0000813 // Kill the imagefilter if our device doesn't allow it
814 SkLazyPaint lazyP;
815 if (paint && paint->getImageFilter()) {
816 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000817 if (justForImageFilter) {
818 // early exit if the layer was just for the imageFilter
819 return count;
820 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000821 SkPaint* p = lazyP.set(*paint);
822 p->setImageFilter(NULL);
823 paint = p;
824 }
825 }
826
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 bool isOpaque;
828 SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
829
reed@google.com76dd2772012-01-05 21:15:07 +0000830 SkDevice* device;
831 if (paint && paint->getImageFilter()) {
832 device = this->createCompatibleDevice(config, ir.width(), ir.height(),
833 isOpaque);
834 } else {
835 device = this->createLayerDevice(config, ir.width(), ir.height(),
836 isOpaque);
837 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000838 if (NULL == device) {
839 SkDebugf("Unable to create device for layer.");
840 return count;
841 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000842
reed@google.com6f8f2922011-03-04 22:27:10 +0000843 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000844 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000845 device->unref();
846
847 layer->fNext = fMCRec->fTopLayer;
848 fMCRec->fLayer = layer;
849 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
850
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000851 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 return count;
853}
854
855int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
856 SaveFlags flags) {
857 if (0xFF == alpha) {
858 return this->saveLayer(bounds, NULL, flags);
859 } else {
860 SkPaint tmpPaint;
861 tmpPaint.setAlpha(alpha);
862 return this->saveLayer(bounds, &tmpPaint, flags);
863 }
864}
865
866void SkCanvas::restore() {
867 // check for underflow
868 if (fMCStack.count() > 1) {
869 this->internalRestore();
870 }
871}
872
873void SkCanvas::internalRestore() {
874 SkASSERT(fMCStack.count() != 0);
875
876 fDeviceCMDirty = true;
877 fLocalBoundsCompareTypeDirty = true;
878
reed@google.com5c3d1472011-02-22 19:12:23 +0000879 fClipStack.restore();
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000880 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 DeviceCM* layer = fMCRec->fLayer; // may be null
882 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
883 fMCRec->fLayer = NULL;
884
885 // now do the normal restore()
886 fMCRec->~MCRec(); // balanced in save()
887 fMCStack.pop_back();
888 fMCRec = (MCRec*)fMCStack.back();
889
890 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
891 since if we're being recorded, we don't want to record this (the
892 recorder will have already recorded the restore).
893 */
894 if (NULL != layer) {
895 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000896 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000897 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
898 layer->fPaint);
899 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000900 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000901
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000902 SkASSERT(fSaveLayerCount > 0);
903 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 }
905 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000906 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000907
908 SkASSERT(fClipStack.getSaveCount() == this->getSaveCount() - 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909}
910
911int SkCanvas::getSaveCount() const {
912 return fMCStack.count();
913}
914
915void SkCanvas::restoreToCount(int count) {
916 // sanity check
917 if (count < 1) {
918 count = 1;
919 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000920
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000921 int n = this->getSaveCount() - count;
922 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000923 this->restore();
924 }
925}
926
reed@google.com7c202932011-12-14 18:48:05 +0000927bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000928 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000929}
930
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931/////////////////////////////////////////////////////////////////////////////
932
933// can't draw it if its empty, or its too big for a fixed-point width or height
934static bool reject_bitmap(const SkBitmap& bitmap) {
reed@google.coma76de3d2011-01-13 18:30:42 +0000935 return bitmap.width() <= 0 || bitmap.height() <= 0
936#ifndef SK_ALLOW_OVER_32K_BITMAPS
937 || bitmap.width() > 32767 || bitmap.height() > 32767
938#endif
939 ;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940}
941
reed@android.comf2b98d62010-12-20 18:26:13 +0000942void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943 const SkMatrix& matrix, const SkPaint* paint) {
944 if (reject_bitmap(bitmap)) {
945 return;
946 }
947
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000948 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000950 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 }
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000952 this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953}
954
reed@google.com76dd2772012-01-05 21:15:07 +0000955#include "SkImageFilter.h"
956
957class DeviceImageFilterProxy : public SkImageFilter::Proxy {
958public:
959 DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000960
reed@google.com8926b162012-03-23 15:36:36 +0000961 virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
962 return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
963 w, h, false);
964 }
965 virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
966 return fDevice->canHandleImageFilter(filter);
967 }
968 virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
reed@google.com76dd2772012-01-05 21:15:07 +0000969 const SkMatrix& ctm,
reed@google.com8926b162012-03-23 15:36:36 +0000970 SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
971 return fDevice->filterImage(filter, src, ctm, result, offset);
972 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000973
reed@google.com76dd2772012-01-05 21:15:07 +0000974private:
975 SkDevice* fDevice;
976};
977
reed@google.com8926b162012-03-23 15:36:36 +0000978void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
979 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 SkPaint tmp;
981 if (NULL == paint) {
982 tmp.setDither(true);
983 paint = &tmp;
984 }
reed@google.com4b226022011-01-11 18:32:13 +0000985
reed@google.com8926b162012-03-23 15:36:36 +0000986 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987 while (iter.next()) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000988 SkDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +0000989 paint = &looper.paint();
990 SkImageFilter* filter = paint->getImageFilter();
991 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +0000992 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed@google.comb55deeb2012-01-06 14:43:09 +0000993 DeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +0000994 SkBitmap dst;
reed@google.comb55deeb2012-01-06 14:43:09 +0000995 const SkBitmap& src = srcDev->accessBitmap(false);
reed@google.com76dd2772012-01-05 21:15:07 +0000996 if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +0000997 SkPaint tmpUnfiltered(*paint);
998 tmpUnfiltered.setImageFilter(NULL);
999 dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001000 }
1001 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001002 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001003 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001004 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001005 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006}
1007
reed@google.com8926b162012-03-23 15:36:36 +00001008void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1009 const SkPaint* paint) {
1010 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001011
reed@google.com8926b162012-03-23 15:36:36 +00001012 if (reject_bitmap(bitmap)) {
1013 return;
1014 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001015
reed@google.com8926b162012-03-23 15:36:36 +00001016 SkPaint tmp;
1017 if (NULL == paint) {
1018 paint = &tmp;
1019 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001020
reed@google.com8926b162012-03-23 15:36:36 +00001021 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001022
reed@google.com8926b162012-03-23 15:36:36 +00001023 while (iter.next()) {
1024 paint = &looper.paint();
1025 SkImageFilter* filter = paint->getImageFilter();
1026 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1027 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
1028 DeviceImageFilterProxy proxy(iter.fDevice);
1029 SkBitmap dst;
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001030 if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
1031 &dst, &pos)) {
1032 SkPaint tmpUnfiltered(*paint);
1033 tmpUnfiltered.setImageFilter(NULL);
1034 iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
1035 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001036 }
1037 } else {
1038 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1039 }
1040 }
1041 LOOPER_END
1042}
1043
reed@android.com8a1c16f2008-12-17 15:59:43 +00001044/////////////////////////////////////////////////////////////////////////////
1045
1046bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
1047 fDeviceCMDirty = true;
1048 fLocalBoundsCompareTypeDirty = true;
1049 return fMCRec->fMatrix->preTranslate(dx, dy);
1050}
1051
1052bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
1053 fDeviceCMDirty = true;
1054 fLocalBoundsCompareTypeDirty = true;
1055 return fMCRec->fMatrix->preScale(sx, sy);
1056}
1057
1058bool SkCanvas::rotate(SkScalar degrees) {
1059 fDeviceCMDirty = true;
1060 fLocalBoundsCompareTypeDirty = true;
1061 return fMCRec->fMatrix->preRotate(degrees);
1062}
1063
1064bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
1065 fDeviceCMDirty = true;
1066 fLocalBoundsCompareTypeDirty = true;
1067 return fMCRec->fMatrix->preSkew(sx, sy);
1068}
1069
1070bool SkCanvas::concat(const SkMatrix& matrix) {
1071 fDeviceCMDirty = true;
1072 fLocalBoundsCompareTypeDirty = true;
1073 return fMCRec->fMatrix->preConcat(matrix);
1074}
1075
1076void SkCanvas::setMatrix(const SkMatrix& matrix) {
1077 fDeviceCMDirty = true;
1078 fLocalBoundsCompareTypeDirty = true;
1079 *fMCRec->fMatrix = matrix;
1080}
1081
1082// this is not virtual, so it must call a virtual method so that subclasses
1083// will see its action
1084void SkCanvas::resetMatrix() {
1085 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001086
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 matrix.reset();
1088 this->setMatrix(matrix);
1089}
1090
1091//////////////////////////////////////////////////////////////////////////////
1092
reed@google.comc42d35d2011-10-12 11:57:42 +00001093bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001094#ifdef SK_ENABLE_CLIP_QUICKREJECT
1095 if (SkRegion::kIntersect_Op == op) {
1096 if (fMCRec->fRasterClip->isEmpty()) {
1097 return false;
1098 }
1099
reed@google.com3b3e8952012-08-16 20:53:31 +00001100 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001101 fDeviceCMDirty = true;
1102 fLocalBoundsCompareTypeDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001103
1104 fClipStack.clipEmpty();
1105 return fMCRec->fRasterClip->setEmpty();
1106 }
1107 }
1108#endif
1109
reed@google.com5c3d1472011-02-22 19:12:23 +00001110 AutoValidateClip avc(this);
1111
reed@android.com8a1c16f2008-12-17 15:59:43 +00001112 fDeviceCMDirty = true;
1113 fLocalBoundsCompareTypeDirty = true;
1114
1115 if (fMCRec->fMatrix->rectStaysRect()) {
reed@android.com98de2bd2009-03-02 19:41:36 +00001116 // for these simpler matrices, we can stay a rect ever after applying
1117 // the matrix. This means we don't have to a) make a path, and b) tell
1118 // the region code to scan-convert the path, only to discover that it
1119 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121
1122 fMCRec->fMatrix->mapRect(&r, rect);
reed@google.com00177082011-10-12 14:34:30 +00001123 fClipStack.clipDevRect(r, op, doAA);
1124 return fMCRec->fRasterClip->op(r, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 } else {
reed@android.com98de2bd2009-03-02 19:41:36 +00001126 // since we're rotate or some such thing, we convert the rect to a path
1127 // and clip against that, since it can handle any matrix. However, to
1128 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1129 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001130 SkPath path;
1131
1132 path.addRect(rect);
reed@google.comc42d35d2011-10-12 11:57:42 +00001133 return this->SkCanvas::clipPath(path, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001134 }
1135}
1136
reed@google.com00177082011-10-12 14:34:30 +00001137static bool clipPathHelper(const SkCanvas* canvas, SkRasterClip* currClip,
reed@google.comc42d35d2011-10-12 11:57:42 +00001138 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001139 // base is used to limit the size (and therefore memory allocation) of the
1140 // region that results from scan converting devPath.
1141 SkRegion base;
1142
reed@google.com819c9212011-02-23 18:56:55 +00001143 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001144 // since we are intersect, we can do better (tighter) with currRgn's
1145 // bounds, than just using the device. However, if currRgn is complex,
1146 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001147 if (currClip->isRect()) {
1148 return currClip->setPath(devPath, *currClip, doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001149 } else {
reed@google.com00177082011-10-12 14:34:30 +00001150 base.setRect(currClip->getBounds());
1151 SkRasterClip clip;
1152 clip.setPath(devPath, base, doAA);
1153 return currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001154 }
reed@google.com819c9212011-02-23 18:56:55 +00001155 } else {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001156 const SkDevice* device = canvas->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001157 if (!device) {
1158 return currClip->setEmpty();
1159 }
1160
junov@chromium.orga907ac32012-02-24 21:54:07 +00001161 base.setRect(0, 0, device->width(), device->height());
reed@google.com819c9212011-02-23 18:56:55 +00001162
1163 if (SkRegion::kReplace_Op == op) {
reed@google.com00177082011-10-12 14:34:30 +00001164 return currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001165 } else {
reed@google.com00177082011-10-12 14:34:30 +00001166 SkRasterClip clip;
1167 clip.setPath(devPath, base, doAA);
1168 return currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001169 }
1170 }
1171}
1172
reed@google.comc42d35d2011-10-12 11:57:42 +00001173bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed@google.comda17f752012-08-16 18:27:05 +00001174#ifdef SK_ENABLE_CLIP_QUICKREJECT
1175 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
1176 if (fMCRec->fRasterClip->isEmpty()) {
1177 return false;
1178 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001179
reed@google.com3b3e8952012-08-16 20:53:31 +00001180 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001181 fDeviceCMDirty = true;
1182 fLocalBoundsCompareTypeDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001183
reed@google.comda17f752012-08-16 18:27:05 +00001184 fClipStack.clipEmpty();
1185 return fMCRec->fRasterClip->setEmpty();
1186 }
1187 }
1188#endif
1189
reed@google.com5c3d1472011-02-22 19:12:23 +00001190 AutoValidateClip avc(this);
1191
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192 fDeviceCMDirty = true;
1193 fLocalBoundsCompareTypeDirty = true;
1194
1195 SkPath devPath;
1196 path.transform(*fMCRec->fMatrix, &devPath);
1197
reed@google.comfe701122011-11-08 19:41:23 +00001198 // Check if the transfomation, or the original path itself
1199 // made us empty. Note this can also happen if we contained NaN
1200 // values. computing the bounds detects this, and will set our
1201 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1202 if (devPath.getBounds().isEmpty()) {
1203 // resetting the path will remove any NaN or other wanky values
1204 // that might upset our scan converter.
1205 devPath.reset();
1206 }
1207
reed@google.com5c3d1472011-02-22 19:12:23 +00001208 // if we called path.swap() we could avoid a deep copy of this path
reed@google.com00177082011-10-12 14:34:30 +00001209 fClipStack.clipDevPath(devPath, op, doAA);
reed@google.com5c3d1472011-02-22 19:12:23 +00001210
reed@google.com00177082011-10-12 14:34:30 +00001211 return clipPathHelper(this, fMCRec->fRasterClip, devPath, op, doAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212}
1213
1214bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001215 AutoValidateClip avc(this);
1216
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217 fDeviceCMDirty = true;
1218 fLocalBoundsCompareTypeDirty = true;
1219
reed@google.com5c3d1472011-02-22 19:12:23 +00001220 // todo: signal fClipStack that we have a region, and therefore (I guess)
1221 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001222 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001223
reed@google.com00177082011-10-12 14:34:30 +00001224 return fMCRec->fRasterClip->op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225}
1226
reed@google.com819c9212011-02-23 18:56:55 +00001227#ifdef SK_DEBUG
1228void SkCanvas::validateClip() const {
1229 // construct clipRgn from the clipstack
1230 const SkDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001231 if (!device) {
1232 SkASSERT(this->getTotalClip().isEmpty());
1233 return;
1234 }
1235
reed@google.com819c9212011-02-23 18:56:55 +00001236 SkIRect ir;
1237 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001238 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001239
robertphillips@google.com80214e22012-07-20 15:33:18 +00001240 SkClipStack::B2TIter iter(fClipStack);
1241 const SkClipStack::B2TIter::Clip* clip;
reed@google.com819c9212011-02-23 18:56:55 +00001242 while ((clip = iter.next()) != NULL) {
1243 if (clip->fPath) {
reed@google.com00177082011-10-12 14:34:30 +00001244 clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
reed@google.com819c9212011-02-23 18:56:55 +00001245 } else if (clip->fRect) {
1246 clip->fRect->round(&ir);
reed@google.com00177082011-10-12 14:34:30 +00001247 tmpClip.op(ir, clip->fOp);
reed@google.com819c9212011-02-23 18:56:55 +00001248 } else {
reed@google.com00177082011-10-12 14:34:30 +00001249 tmpClip.setEmpty();
reed@google.com819c9212011-02-23 18:56:55 +00001250 }
1251 }
1252
reed@google.com6f8f2922011-03-04 22:27:10 +00001253#if 0 // enable this locally for testing
reed@google.com819c9212011-02-23 18:56:55 +00001254 // now compare against the current rgn
1255 const SkRegion& rgn = this->getTotalClip();
reed@google.com00177082011-10-12 14:34:30 +00001256 SkASSERT(rgn == tmpClip);
reed@google.com02878b82011-02-24 13:04:42 +00001257#endif
reed@google.com819c9212011-02-23 18:56:55 +00001258}
1259#endif
1260
reed@google.com90c07ea2012-04-13 13:50:27 +00001261void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001262 SkClipStack::B2TIter iter(fClipStack);
1263 const SkClipStack::B2TIter::Clip* clip;
reed@google.com90c07ea2012-04-13 13:50:27 +00001264
robertphillips@google.com7460b372012-04-25 16:54:51 +00001265 SkRect empty = { 0, 0, 0, 0 };
reed@google.com90c07ea2012-04-13 13:50:27 +00001266 while ((clip = iter.next()) != NULL) {
1267 if (clip->fPath) {
1268 visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
1269 } else if (clip->fRect) {
1270 visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
1271 } else {
1272 visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
1273 }
1274 }
1275}
1276
reed@google.com5c3d1472011-02-22 19:12:23 +00001277///////////////////////////////////////////////////////////////////////////////
1278
reed@google.com3b3e8952012-08-16 20:53:31 +00001279void SkCanvas::computeLocalClipBoundsCompareType() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 SkRect r;
reed@android.comba09de42010-02-05 20:46:05 +00001281
reed@google.com3b3e8952012-08-16 20:53:31 +00001282 if (!this->getClipBounds(&r)) {
1283 fLocalBoundsCompareType.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001285 fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
1286 SkScalarToCompareType(r.fTop),
1287 SkScalarToCompareType(r.fRight),
1288 SkScalarToCompareType(r.fBottom));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 }
1290}
1291
reed@google.com3b3e8952012-08-16 20:53:31 +00001292bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001293
reed@google.com16078632011-12-06 18:56:37 +00001294 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001295 return true;
1296
reed@google.com00177082011-10-12 14:34:30 +00001297 if (fMCRec->fRasterClip->isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 return true;
1299 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300
tomhudson@google.com8d430182011-06-06 19:11:19 +00001301 if (fMCRec->fMatrix->hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001302 SkRect dst;
1303 fMCRec->fMatrix->mapRect(&dst, rect);
1304 SkIRect idst;
1305 dst.roundOut(&idst);
reed@google.com00177082011-10-12 14:34:30 +00001306 return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001307 } else {
reed@google.com3b3e8952012-08-16 20:53:31 +00001308 const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
reed@android.comd252db02009-04-01 18:31:44 +00001309
reed@android.coma380ae42009-07-21 01:17:02 +00001310 // for speed, do the most likely reject compares first
1311 SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
1312 SkScalarCompareType userB = SkScalarToCompareType(rect.fBottom);
1313 if (userT >= clipR.fBottom || userB <= clipR.fTop) {
1314 return true;
1315 }
1316 SkScalarCompareType userL = SkScalarToCompareType(rect.fLeft);
1317 SkScalarCompareType userR = SkScalarToCompareType(rect.fRight);
1318 if (userL >= clipR.fRight || userR <= clipR.fLeft) {
1319 return true;
1320 }
1321 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323}
1324
reed@google.com3b3e8952012-08-16 20:53:31 +00001325bool SkCanvas::quickReject(const SkPath& path) const {
1326 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327}
1328
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001329static inline int pinIntForScalar(int x) {
1330#ifdef SK_SCALAR_IS_FIXED
1331 if (x < SK_MinS16) {
1332 x = SK_MinS16;
1333 } else if (x > SK_MaxS16) {
1334 x = SK_MaxS16;
1335 }
1336#endif
1337 return x;
1338}
1339
reed@google.com3b3e8952012-08-16 20:53:31 +00001340bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001341 SkIRect ibounds;
1342 if (!getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001343 return false;
1344 }
1345
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001346 SkMatrix inverse;
1347 // if we can't invert the CTM, we can't return local clip bounds
1348 if (!fMCRec->fMatrix->invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001349 if (bounds) {
1350 bounds->setEmpty();
1351 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001352 return false;
1353 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001354
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001355 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001356 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001357 // adjust it outwards in case we are antialiasing
1358 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001359
1360 // SkRect::iset() will correctly assert if we pass a value out of range
1361 // (when SkScalar==fixed), so we pin to legal values. This does not
1362 // really returnt the correct answer, but its the best we can do given
1363 // that we've promised to return SkRect (even though we support devices
1364 // that can be larger than 32K in width or height).
1365 r.iset(pinIntForScalar(ibounds.fLeft - inset),
1366 pinIntForScalar(ibounds.fTop - inset),
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001367 pinIntForScalar(ibounds.fRight + inset),
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001368 pinIntForScalar(ibounds.fBottom + inset));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369 inverse.mapRect(bounds, r);
1370 }
1371 return true;
1372}
1373
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001374bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed@google.com00177082011-10-12 14:34:30 +00001375 const SkRasterClip& clip = *fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001376 if (clip.isEmpty()) {
1377 if (bounds) {
1378 bounds->setEmpty();
1379 }
1380 return false;
1381 }
1382
1383 if (NULL != bounds) {
1384 *bounds = clip.getBounds();
1385 }
1386 return true;
1387}
1388
reed@android.com8a1c16f2008-12-17 15:59:43 +00001389const SkMatrix& SkCanvas::getTotalMatrix() const {
1390 return *fMCRec->fMatrix;
1391}
1392
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001393SkCanvas::ClipType SkCanvas::getClipType() const {
reed@google.com00177082011-10-12 14:34:30 +00001394 if (fMCRec->fRasterClip->isEmpty()) return kEmpty_ClipType;
1395 if (fMCRec->fRasterClip->isRect()) return kRect_ClipType;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001396 return kComplex_ClipType;
1397}
1398
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399const SkRegion& SkCanvas::getTotalClip() const {
reed@google.com00177082011-10-12 14:34:30 +00001400 return fMCRec->fRasterClip->forceGetBW();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401}
1402
reed@android.comf2b98d62010-12-20 18:26:13 +00001403void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
1404 if (NULL == matrix || matrix->isIdentity()) {
1405 if (fUseExternalMatrix) {
1406 fDeviceCMDirty = true;
1407 }
1408 fUseExternalMatrix = false;
1409 } else {
mike@reedtribe.org90bf4272012-04-14 19:06:16 +00001410 if (matrix->invert(&fExternalInverse)) {
1411 fExternalMatrix = *matrix;
1412 fUseExternalMatrix = true;
1413 fDeviceCMDirty = true; // |= (fExternalMatrix != *matrix)
reed@google.comfc9a3be2012-04-12 13:52:14 +00001414 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001415 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001416}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417
bsalomon@google.come97f0852011-06-17 13:10:25 +00001418SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
1419 int width, int height,
1420 bool isOpaque) {
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001421 SkDevice* device = this->getTopDevice();
reed@google.comcde92112011-07-06 20:00:52 +00001422 if (device) {
1423 return device->createCompatibleDeviceForSaveLayer(config, width, height,
1424 isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001425 } else {
reed@google.comcde92112011-07-06 20:00:52 +00001426 return NULL;
bsalomon@google.come97f0852011-06-17 13:10:25 +00001427 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428}
1429
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001430SkDevice* SkCanvas::createCompatibleDevice(SkBitmap::Config config,
bsalomon@google.come97f0852011-06-17 13:10:25 +00001431 int width, int height,
1432 bool isOpaque) {
1433 SkDevice* device = this->getDevice();
1434 if (device) {
reed@google.comcde92112011-07-06 20:00:52 +00001435 return device->createCompatibleDevice(config, width, height, isOpaque);
bsalomon@google.come97f0852011-06-17 13:10:25 +00001436 } else {
1437 return NULL;
1438 }
1439}
1440
1441
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442//////////////////////////////////////////////////////////////////////////////
1443// These are the virtual drawing methods
1444//////////////////////////////////////////////////////////////////////////////
1445
reed@google.com2a981812011-04-14 18:59:28 +00001446void SkCanvas::clear(SkColor color) {
1447 SkDrawIter iter(this);
1448
1449 while (iter.next()) {
1450 iter.fDevice->clear(color);
1451 }
1452}
1453
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001455 this->internalDrawPaint(paint);
1456}
1457
1458void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001459 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001460
1461 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001462 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463 }
1464
reed@google.com4e2b3d32011-04-07 14:18:59 +00001465 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001466}
1467
1468void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1469 const SkPaint& paint) {
1470 if ((long)count <= 0) {
1471 return;
1472 }
1473
reed@google.coma584aed2012-05-16 14:06:02 +00001474 if (paint.canComputeFastBounds()) {
1475 SkRect r;
1476 // special-case 2 points (common for drawing a single line)
1477 if (2 == count) {
1478 r.set(pts[0], pts[1]);
1479 } else {
1480 r.set(pts, count);
1481 }
1482 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001483 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
reed@google.coma584aed2012-05-16 14:06:02 +00001484 return;
1485 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001486 }
reed@google.coma584aed2012-05-16 14:06:02 +00001487
reed@android.com8a1c16f2008-12-17 15:59:43 +00001488 SkASSERT(pts != NULL);
1489
reed@google.com4e2b3d32011-04-07 14:18:59 +00001490 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001491
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001493 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 }
reed@google.com4b226022011-01-11 18:32:13 +00001495
reed@google.com4e2b3d32011-04-07 14:18:59 +00001496 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497}
1498
1499void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1500 if (paint.canComputeFastBounds()) {
1501 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001502 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001503 return;
1504 }
1505 }
reed@google.com4b226022011-01-11 18:32:13 +00001506
reed@google.com4e2b3d32011-04-07 14:18:59 +00001507 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001508
1509 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001510 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511 }
1512
reed@google.com4e2b3d32011-04-07 14:18:59 +00001513 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
1516void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001517 if (!path.isFinite()) {
1518 return;
1519 }
1520
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001521 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
reed@android.comd252db02009-04-01 18:31:44 +00001522 SkRect storage;
1523 const SkRect& bounds = path.getBounds();
reed@google.com3b3e8952012-08-16 20:53:31 +00001524 if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001525 return;
1526 }
1527 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001528 if (path.isEmpty()) {
1529 if (path.isInverseFillType()) {
1530 this->internalDrawPaint(paint);
1531 }
1532 return;
1533 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534
reed@google.com4e2b3d32011-04-07 14:18:59 +00001535 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536
1537 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001538 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539 }
1540
reed@google.com4e2b3d32011-04-07 14:18:59 +00001541 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542}
1543
1544void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1545 const SkPaint* paint) {
1546 SkDEBUGCODE(bitmap.validate();)
1547
reed@google.com3d608122011-11-21 15:16:16 +00001548 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001549 SkRect bounds = {
1550 x, y,
1551 x + SkIntToScalar(bitmap.width()),
1552 y + SkIntToScalar(bitmap.height())
1553 };
1554 if (paint) {
1555 (void)paint->computeFastBounds(bounds, &bounds);
1556 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001557 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558 return;
1559 }
1560 }
reed@google.com4b226022011-01-11 18:32:13 +00001561
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562 SkMatrix matrix;
1563 matrix.setTranslate(x, y);
reed@android.comf2b98d62010-12-20 18:26:13 +00001564 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001565}
1566
reed@google.com9987ec32011-09-07 11:57:52 +00001567// this one is non-virtual, so it can be called safely by other canvas apis
1568void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1569 const SkRect& dst, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001570 if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
1571 return;
1572 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001573
reed@android.com8a1c16f2008-12-17 15:59:43 +00001574 // do this now, to avoid the cost of calling extract for RLE bitmaps
reed@google.com3d608122011-11-21 15:16:16 +00001575 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001576 SkRect storage;
1577 const SkRect* bounds = &dst;
1578 if (paint) {
1579 bounds = &paint->computeFastBounds(dst, &storage);
1580 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001581 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001582 return;
1583 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 }
reed@google.com3d608122011-11-21 15:16:16 +00001585
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586 const SkBitmap* bitmapPtr = &bitmap;
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001587
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588 SkMatrix matrix;
reed@android.com87899992009-10-16 14:48:38 +00001589 SkRect tmpSrc;
1590 if (src) {
1591 tmpSrc.set(*src);
1592 // if the extract process clipped off the top or left of the
1593 // original, we adjust for that here to get the position right.
1594 if (tmpSrc.fLeft > 0) {
1595 tmpSrc.fRight -= tmpSrc.fLeft;
1596 tmpSrc.fLeft = 0;
reed@android.comfead49e2009-10-15 18:51:46 +00001597 }
reed@android.com87899992009-10-16 14:48:38 +00001598 if (tmpSrc.fTop > 0) {
1599 tmpSrc.fBottom -= tmpSrc.fTop;
1600 tmpSrc.fTop = 0;
1601 }
1602 } else {
1603 tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
1604 SkIntToScalar(bitmap.height()));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605 }
reed@android.com87899992009-10-16 14:48:38 +00001606 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001607
reed@android.comf2b98d62010-12-20 18:26:13 +00001608 // ensure that src is "valid" before we pass it to our internal routines
1609 // and to SkDevice. i.e. sure it is contained inside the original bitmap.
1610 SkIRect tmpISrc;
1611 if (src) {
1612 tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
reed@google.com2ade0862011-03-17 17:48:04 +00001613 if (!tmpISrc.intersect(*src)) {
1614 return;
1615 }
reed@android.comf2b98d62010-12-20 18:26:13 +00001616 src = &tmpISrc;
1617 }
1618 this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619}
1620
reed@google.com9987ec32011-09-07 11:57:52 +00001621void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
1622 const SkRect& dst, const SkPaint* paint) {
1623 SkDEBUGCODE(bitmap.validate();)
1624 this->internalDrawBitmapRect(bitmap, src, dst, paint);
1625}
1626
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1628 const SkPaint* paint) {
1629 SkDEBUGCODE(bitmap.validate();)
reed@android.comf2b98d62010-12-20 18:26:13 +00001630 this->internalDrawBitmap(bitmap, NULL, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631}
1632
reed@android.comf2b98d62010-12-20 18:26:13 +00001633void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
1634 const SkMatrix& matrix, const SkPaint& paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635 SkDEBUGCODE(bitmap.validate();)
reed@android.com9b039062009-02-11 15:09:58 +00001636
reed@google.com4e2b3d32011-04-07 14:18:59 +00001637 LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
reed@android.com9b039062009-02-11 15:09:58 +00001638
reed@android.com8a1c16f2008-12-17 15:59:43 +00001639 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001640 iter.fDevice->drawBitmap(iter, bitmap, srcRect, matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641 }
reed@android.com9b039062009-02-11 15:09:58 +00001642
reed@google.com4e2b3d32011-04-07 14:18:59 +00001643 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644}
1645
reed@google.com9987ec32011-09-07 11:57:52 +00001646void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1647 const SkIRect& center, const SkRect& dst,
1648 const SkPaint* paint) {
reed@google.com3d608122011-11-21 15:16:16 +00001649 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001650 SkRect storage;
1651 const SkRect* bounds = &dst;
1652 if (paint) {
1653 bounds = &paint->computeFastBounds(dst, &storage);
1654 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001655 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001656 return;
1657 }
1658 }
1659
reed@google.com9987ec32011-09-07 11:57:52 +00001660 const int32_t w = bitmap.width();
1661 const int32_t h = bitmap.height();
1662
1663 SkIRect c = center;
1664 // pin center to the bounds of the bitmap
1665 c.fLeft = SkMax32(0, center.fLeft);
1666 c.fTop = SkMax32(0, center.fTop);
1667 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1668 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1669
1670 const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
1671 const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
1672 SkScalar dstX[4] = {
1673 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1674 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1675 };
1676 SkScalar dstY[4] = {
1677 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1678 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1679 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001680
reed@google.com9987ec32011-09-07 11:57:52 +00001681 if (dstX[1] > dstX[2]) {
1682 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1683 dstX[2] = dstX[1];
1684 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001685
reed@google.com9987ec32011-09-07 11:57:52 +00001686 if (dstY[1] > dstY[2]) {
1687 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1688 dstY[2] = dstY[1];
1689 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001690
reed@google.com9987ec32011-09-07 11:57:52 +00001691 SkIRect s;
1692 SkRect d;
1693 for (int y = 0; y < 3; y++) {
1694 s.fTop = srcY[y];
1695 s.fBottom = srcY[y+1];
1696 d.fTop = dstY[y];
1697 d.fBottom = dstY[y+1];
1698 for (int x = 0; x < 3; x++) {
1699 s.fLeft = srcX[x];
1700 s.fRight = srcX[x+1];
1701 d.fLeft = dstX[x];
1702 d.fRight = dstX[x+1];
1703 this->internalDrawBitmapRect(bitmap, &s, d, paint);
1704 }
1705 }
1706}
1707
1708void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1709 const SkRect& dst, const SkPaint* paint) {
1710 SkDEBUGCODE(bitmap.validate();)
1711
1712 // Need a device entry-point, so gpu can use a mesh
1713 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1714}
1715
reed@google.comf67e4cf2011-03-15 20:56:58 +00001716class SkDeviceFilteredPaint {
1717public:
1718 SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
1719 SkDevice::TextFlags flags;
1720 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001721 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001722 newPaint->setFlags(flags.fFlags);
1723 newPaint->setHinting(flags.fHinting);
1724 fPaint = newPaint;
1725 } else {
1726 fPaint = &paint;
1727 }
1728 }
1729
reed@google.comf67e4cf2011-03-15 20:56:58 +00001730 const SkPaint& paint() const { return *fPaint; }
1731
1732private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001733 const SkPaint* fPaint;
1734 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001735};
1736
bungeman@google.com52c748b2011-08-22 21:30:43 +00001737void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
1738 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00001739 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00001740 draw.fDevice->drawRect(draw, r, paint);
1741 } else {
1742 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00001743 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00001744 draw.fDevice->drawRect(draw, r, p);
1745 }
1746}
1747
1748void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
1749 const char text[], size_t byteLength,
1750 SkScalar x, SkScalar y) {
1751 SkASSERT(byteLength == 0 || text != NULL);
1752
1753 // nothing to draw
1754 if (text == NULL || byteLength == 0 ||
1755 draw.fClip->isEmpty() ||
1756 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1757 return;
1758 }
1759
1760 SkScalar width = 0;
1761 SkPoint start;
1762
1763 start.set(0, 0); // to avoid warning
1764 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
1765 SkPaint::kStrikeThruText_Flag)) {
1766 width = paint.measureText(text, byteLength);
1767
1768 SkScalar offsetX = 0;
1769 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
1770 offsetX = SkScalarHalf(width);
1771 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
1772 offsetX = width;
1773 }
1774 start.set(x - offsetX, y);
1775 }
1776
1777 if (0 == width) {
1778 return;
1779 }
1780
1781 uint32_t flags = paint.getFlags();
1782
1783 if (flags & (SkPaint::kUnderlineText_Flag |
1784 SkPaint::kStrikeThruText_Flag)) {
1785 SkScalar textSize = paint.getTextSize();
1786 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
1787 SkRect r;
1788
1789 r.fLeft = start.fX;
1790 r.fRight = start.fX + width;
1791
1792 if (flags & SkPaint::kUnderlineText_Flag) {
1793 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
1794 start.fY);
1795 r.fTop = offset;
1796 r.fBottom = offset + height;
1797 DrawRect(draw, paint, r, textSize);
1798 }
1799 if (flags & SkPaint::kStrikeThruText_Flag) {
1800 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
1801 start.fY);
1802 r.fTop = offset;
1803 r.fBottom = offset + height;
1804 DrawRect(draw, paint, r, textSize);
1805 }
1806 }
1807}
1808
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809void SkCanvas::drawText(const void* text, size_t byteLength,
1810 SkScalar x, SkScalar y, const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001811 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812
1813 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001814 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00001815 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00001816 DrawTextDecorations(iter, dfp.paint(),
1817 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818 }
1819
reed@google.com4e2b3d32011-04-07 14:18:59 +00001820 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821}
1822
1823void SkCanvas::drawPosText(const void* text, size_t byteLength,
1824 const SkPoint pos[], const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001825 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001826
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001828 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001830 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831 }
reed@google.com4b226022011-01-11 18:32:13 +00001832
reed@google.com4e2b3d32011-04-07 14:18:59 +00001833 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834}
1835
1836void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
1837 const SkScalar xpos[], SkScalar constY,
1838 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001839 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001840
reed@android.com8a1c16f2008-12-17 15:59:43 +00001841 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001842 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00001844 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001845 }
reed@google.com4b226022011-01-11 18:32:13 +00001846
reed@google.com4e2b3d32011-04-07 14:18:59 +00001847 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001848}
1849
1850void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
1851 const SkPath& path, const SkMatrix* matrix,
1852 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001853 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001854
1855 while (iter.next()) {
1856 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001857 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001858 }
1859
reed@google.com4e2b3d32011-04-07 14:18:59 +00001860 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001861}
1862
djsollen@google.com56c69772011-11-08 19:00:26 +00001863#ifdef SK_BUILD_FOR_ANDROID
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001864void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
1865 const SkPoint pos[], const SkPaint& paint,
1866 const SkPath& path, const SkMatrix* matrix) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001867 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001868
1869 while (iter.next()) {
1870 iter.fDevice->drawPosTextOnPath(iter, text, byteLength, pos,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001871 looper.paint(), path, matrix);
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001872 }
1873
reed@google.com4e2b3d32011-04-07 14:18:59 +00001874 LOOPER_END
djsollen@google.comcd9d69b2011-03-14 20:30:14 +00001875}
1876#endif
1877
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
1879 const SkPoint verts[], const SkPoint texs[],
1880 const SkColor colors[], SkXfermode* xmode,
1881 const uint16_t indices[], int indexCount,
1882 const SkPaint& paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001883 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
reed@google.com4b226022011-01-11 18:32:13 +00001884
reed@android.com8a1c16f2008-12-17 15:59:43 +00001885 while (iter.next()) {
1886 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00001887 colors, xmode, indices, indexCount,
1888 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001889 }
reed@google.com4b226022011-01-11 18:32:13 +00001890
reed@google.com4e2b3d32011-04-07 14:18:59 +00001891 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001892}
1893
reed@android.comcb608442009-12-04 21:32:27 +00001894void SkCanvas::drawData(const void* data, size_t length) {
1895 // do nothing. Subclasses may do something with the data
1896}
1897
reed@android.com8a1c16f2008-12-17 15:59:43 +00001898//////////////////////////////////////////////////////////////////////////////
1899// These methods are NOT virtual, and therefore must call back into virtual
1900// methods, rather than actually drawing themselves.
1901//////////////////////////////////////////////////////////////////////////////
1902
1903void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00001904 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905 SkPaint paint;
1906
1907 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00001908 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001909 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001910 }
1911 this->drawPaint(paint);
1912}
1913
reed@android.com845fdac2009-06-23 03:01:32 +00001914void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915 SkPaint paint;
1916
1917 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00001918 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00001919 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920 }
1921 this->drawPaint(paint);
1922}
1923
1924void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
1925 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00001926
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927 pt.set(x, y);
1928 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1929}
1930
1931void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
1932 SkPoint pt;
1933 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00001934
reed@android.com8a1c16f2008-12-17 15:59:43 +00001935 pt.set(x, y);
1936 paint.setColor(color);
1937 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
1938}
1939
1940void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
1941 const SkPaint& paint) {
1942 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00001943
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944 pts[0].set(x0, y0);
1945 pts[1].set(x1, y1);
1946 this->drawPoints(kLines_PointMode, 2, pts, paint);
1947}
1948
1949void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
1950 SkScalar right, SkScalar bottom,
1951 const SkPaint& paint) {
1952 SkRect r;
1953
1954 r.set(left, top, right, bottom);
1955 this->drawRect(r, paint);
1956}
1957
1958void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
1959 const SkPaint& paint) {
1960 if (radius < 0) {
1961 radius = 0;
1962 }
1963
1964 SkRect r;
1965 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4b226022011-01-11 18:32:13 +00001966
reed@android.com8a1c16f2008-12-17 15:59:43 +00001967 if (paint.canComputeFastBounds()) {
1968 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001969 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001970 return;
1971 }
1972 }
reed@google.com4b226022011-01-11 18:32:13 +00001973
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974 SkPath path;
1975 path.addOval(r);
1976 this->drawPath(path, paint);
1977}
1978
1979void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
1980 const SkPaint& paint) {
1981 if (rx > 0 && ry > 0) {
1982 if (paint.canComputeFastBounds()) {
1983 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00001984 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001985 return;
1986 }
1987 }
1988
1989 SkPath path;
1990 path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
1991 this->drawPath(path, paint);
1992 } else {
1993 this->drawRect(r, paint);
1994 }
1995}
1996
1997void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
1998 if (paint.canComputeFastBounds()) {
1999 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002000 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002001 return;
2002 }
2003 }
2004
2005 SkPath path;
2006 path.addOval(oval);
2007 this->drawPath(path, paint);
2008}
2009
2010void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2011 SkScalar sweepAngle, bool useCenter,
2012 const SkPaint& paint) {
2013 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2014 this->drawOval(oval, paint);
2015 } else {
2016 SkPath path;
2017 if (useCenter) {
2018 path.moveTo(oval.centerX(), oval.centerY());
2019 }
2020 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2021 if (useCenter) {
2022 path.close();
2023 }
2024 this->drawPath(path, paint);
2025 }
2026}
2027
2028void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2029 const SkPath& path, SkScalar hOffset,
2030 SkScalar vOffset, const SkPaint& paint) {
2031 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002032
reed@android.com8a1c16f2008-12-17 15:59:43 +00002033 matrix.setTranslate(hOffset, vOffset);
2034 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2035}
2036
reed@android.comf76bacf2009-05-13 14:00:33 +00002037///////////////////////////////////////////////////////////////////////////////
2038
reed@android.com8a1c16f2008-12-17 15:59:43 +00002039void SkCanvas::drawPicture(SkPicture& picture) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002040 picture.draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002041}
2042
reed@android.com8a1c16f2008-12-17 15:59:43 +00002043///////////////////////////////////////////////////////////////////////////////
2044///////////////////////////////////////////////////////////////////////////////
2045
2046SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002047 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002048
2049 SkASSERT(canvas);
2050
2051 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2052 fDone = !fImpl->next();
2053}
2054
2055SkCanvas::LayerIter::~LayerIter() {
2056 fImpl->~SkDrawIter();
2057}
2058
2059void SkCanvas::LayerIter::next() {
2060 fDone = !fImpl->next();
2061}
2062
2063SkDevice* SkCanvas::LayerIter::device() const {
2064 return fImpl->getDevice();
2065}
2066
2067const SkMatrix& SkCanvas::LayerIter::matrix() const {
2068 return fImpl->getMatrix();
2069}
2070
2071const SkPaint& SkCanvas::LayerIter::paint() const {
2072 const SkPaint* paint = fImpl->getPaint();
2073 if (NULL == paint) {
2074 paint = &fDefaultPaint;
2075 }
2076 return *paint;
2077}
2078
2079const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2080int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2081int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002082
2083///////////////////////////////////////////////////////////////////////////////
2084
2085SkCanvas::ClipVisitor::~ClipVisitor() { }
reed@google.com97af1a62012-08-28 12:19:02 +00002086
2087