blob: 717878c82a008a05adea1453d04328f787240aee [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
reed9f014712014-06-18 15:51:20 -07008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000012#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000016#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000017#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070018#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000022#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070025#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000026#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000027#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000030#if SK_SUPPORT_GPU
31#include "GrRenderTarget.h"
32#endif
33
reed@google.comda17f752012-08-16 18:27:05 +000034// experimental for faster tiled drawing...
35//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000036
reed@android.com8a1c16f2008-12-17 15:59:43 +000037//#define SK_TRACE_SAVERESTORE
38
39#ifdef SK_TRACE_SAVERESTORE
40 static int gLayerCounter;
41 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
42 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
43
44 static int gRecCounter;
45 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
46 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
47
48 static int gCanvasCounter;
49 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
50 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
51#else
52 #define inc_layer()
53 #define dec_layer()
54 #define inc_rec()
55 #define dec_rec()
56 #define inc_canvas()
57 #define dec_canvas()
58#endif
59
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000060typedef SkTLazy<SkPaint> SkLazyPaint;
61
reed@google.com97af1a62012-08-28 12:19:02 +000062void SkCanvas::predrawNotify() {
63 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000064 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000065 }
66}
67
reed@android.com8a1c16f2008-12-17 15:59:43 +000068///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000069
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000070/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 The clip/matrix/proc are fields that reflect the top of the save/restore
72 stack. Whenever the canvas changes, it marks a dirty flag, and then before
73 these are used (assuming we're not on a layer) we rebuild these cache
74 values: they reflect the top of the save stack, but translated and clipped
75 by the device's XY offset and bitmap-bounds.
76*/
77struct DeviceCM {
78 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000079 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000080 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000082 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000083
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000084 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 : fNext(NULL) {
86 if (NULL != device) {
87 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000088 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 }
reed@google.com4b226022011-01-11 18:32:13 +000090 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000091 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000092 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000093
bungeman@google.com88edf1e2011-08-08 19:41:56 +000094 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000096 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000097 fDevice->unref();
98 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000099 SkDELETE(fPaint);
100 }
reed@google.com4b226022011-01-11 18:32:13 +0000101
reed@google.com045e62d2011-10-24 12:19:46 +0000102 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
103 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000104 int x = fDevice->getOrigin().x();
105 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 int width = fDevice->width();
107 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000108
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 if ((x | y) == 0) {
110 fMatrix = &totalMatrix;
111 fClip = totalClip;
112 } else {
113 fMatrixStorage = totalMatrix;
114 fMatrixStorage.postTranslate(SkIntToScalar(-x),
115 SkIntToScalar(-y));
116 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000117
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118 totalClip.translate(-x, -y, &fClip);
119 }
120
reed@google.com045e62d2011-10-24 12:19:46 +0000121 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122
123 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000124
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000126 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 SkRegion::kDifference_Op);
128 }
reed@google.com4b226022011-01-11 18:32:13 +0000129
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000130 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
131
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132#ifdef SK_DEBUG
133 if (!fClip.isEmpty()) {
134 SkIRect deviceR;
135 deviceR.set(0, 0, width, height);
136 SkASSERT(deviceR.contains(fClip.getBounds()));
137 }
138#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000139 }
140
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000142 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143};
144
145/* This is the record we keep for each save/restore level in the stack.
146 Since a level optionally copies the matrix and/or stack, we have pointers
147 for these fields. If the value is copied for this level, the copy is
148 stored in the ...Storage field, and the pointer points to that. If the
149 value is not copied for this level, we ignore ...Storage, and just point
150 at the corresponding value in the previous level in the stack.
151*/
152class SkCanvas::MCRec {
153public:
reed1f836ee2014-07-07 07:49:34 -0700154 SkMatrix fMatrix;
155 SkRasterClip fRasterClip;
156 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000157
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 DeviceCM* fLayer;
159 /* If there are any layers in the stack, this points to the top-most
160 one that is at or below this level in the stack (so we know what
161 bitmap/device to draw into from this level. This value is NOT
162 reference counted, since the real owner is either our fLayer field,
163 or a previous one in a lower level.)
164 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000165 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166
Florin Malita5f6102d2014-06-30 10:13:28 -0400167 MCRec(const MCRec* prev) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 if (NULL != prev) {
reed1f836ee2014-07-07 07:49:34 -0700169 fMatrix = prev->fMatrix;
170 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171
172 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000173 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174
175 fTopLayer = prev->fTopLayer;
176 } else { // no prev
reed1f836ee2014-07-07 07:49:34 -0700177 fMatrix.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178 fFilter = NULL;
179 fTopLayer = NULL;
180 }
181 fLayer = NULL;
182
183 // don't bother initializing fNext
184 inc_rec();
185 }
186 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000187 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188 SkDELETE(fLayer);
189 dec_rec();
190 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191};
192
193class SkDrawIter : public SkDraw {
194public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000195 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000196 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000197 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 canvas->updateDeviceCMCache();
199
reed@google.com90c07ea2012-04-13 13:50:27 +0000200 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000202 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 }
reed@google.com4b226022011-01-11 18:32:13 +0000204
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205 bool next() {
206 // skip over recs with empty clips
207 if (fSkipEmptyClips) {
208 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
209 fCurrLayer = fCurrLayer->fNext;
210 }
211 }
212
reed@google.comf68c5e22012-02-24 16:38:58 +0000213 const DeviceCM* rec = fCurrLayer;
214 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215
216 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000217 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
218 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 fDevice = rec->fDevice;
220 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000222 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223
224 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000226
reed@android.com8a1c16f2008-12-17 15:59:43 +0000227 return true;
228 }
229 return false;
230 }
reed@google.com4b226022011-01-11 18:32:13 +0000231
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000232 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000233 int getX() const { return fDevice->getOrigin().x(); }
234 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 const SkMatrix& getMatrix() const { return *fMatrix; }
236 const SkRegion& getClip() const { return *fClip; }
237 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239private:
240 SkCanvas* fCanvas;
241 const DeviceCM* fCurrLayer;
242 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 SkBool8 fSkipEmptyClips;
244
245 typedef SkDraw INHERITED;
246};
247
248/////////////////////////////////////////////////////////////////////////////
249
250class AutoDrawLooper {
251public:
reed@google.com8926b162012-03-23 15:36:36 +0000252 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000253 bool skipLayerForImageFilter = false,
254 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000255 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000257 fPaint = NULL;
258 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000259 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000260 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261
reed@google.com8926b162012-03-23 15:36:36 +0000262 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
263 SkPaint tmp;
264 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000265 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
266 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000267 // we'll clear the imageFilter for the actual draws in next(), so
268 // it will only be applied during the restore().
269 fDoClearImageFilter = true;
270 }
271
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000272 if (SkDrawLooper* looper = paint.getLooper()) {
273 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
274 looper->contextSize());
275 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000276 fIsSimple = false;
277 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000278 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000279 // can we be marked as simple?
280 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000281 }
282 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000283
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000285 if (fDoClearImageFilter) {
286 fCanvas->internalRestore();
287 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000290
reed@google.com4e2b3d32011-04-07 14:18:59 +0000291 const SkPaint& paint() const {
292 SkASSERT(fPaint);
293 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000295
reed@google.com129ec222012-05-15 13:24:09 +0000296 bool next(SkDrawFilter::Type drawType) {
297 if (fDone) {
298 return false;
299 } else if (fIsSimple) {
300 fDone = true;
301 fPaint = &fOrigPaint;
302 return !fPaint->nothingToDraw();
303 } else {
304 return this->doNext(drawType);
305 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000306 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000307
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000309 SkLazyPaint fLazyPaint;
310 SkCanvas* fCanvas;
311 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000312 SkDrawFilter* fFilter;
313 const SkPaint* fPaint;
314 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000315 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000316 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000317 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000318 SkDrawLooper::Context* fLooperContext;
319 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000320
321 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322};
323
reed@google.com129ec222012-05-15 13:24:09 +0000324bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000325 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000326 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000327 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000328
329 SkPaint* paint = fLazyPaint.set(fOrigPaint);
330
331 if (fDoClearImageFilter) {
332 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000333 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000334
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000335 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000336 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000337 return false;
338 }
339 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000340 if (!fFilter->filter(paint, drawType)) {
341 fDone = true;
342 return false;
343 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000344 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000345 // no looper means we only draw once
346 fDone = true;
347 }
348 }
349 fPaint = paint;
350
351 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000352 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000353 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000354 }
355
356 // call this after any possible paint modifiers
357 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358 fPaint = NULL;
359 return false;
360 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000361 return true;
362}
363
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364#include "SkColorPriv.h"
365
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366////////// macros to place around the internal draw calls //////////////////
367
reed@google.com8926b162012-03-23 15:36:36 +0000368#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000369 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000370 AutoDrawLooper looper(this, paint, true); \
371 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000372 SkDrawIter iter(this);
373
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000374#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000375 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000376 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000377 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000379
reed@google.com4e2b3d32011-04-07 14:18:59 +0000380#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381
382////////////////////////////////////////////////////////////////////////////
383
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000384SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@google.comc0784db2013-12-13 21:16:12 +0000385 fCachedLocalClipBounds.setEmpty();
386 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000387 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000388 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700389 fDeviceCMDirty = true;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000390 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000391 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000392 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393
394 fMCRec = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400395 new (fMCRec) MCRec(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000397 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399
reed@google.com97af1a62012-08-28 12:19:02 +0000400 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000401
reedf92c8662014-08-18 08:02:43 -0700402 if (device) {
403 device->onAttachToCanvas(this);
404 fMCRec->fLayer->fDevice = SkRef(device);
405 fMCRec->fRasterClip.setRect(SkIRect::MakeWH(device->width(), device->height()));
406 }
407 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000408}
409
reed@google.comcde92112011-07-06 20:00:52 +0000410SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000411 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
412{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000413 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000414
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000415 this->init(NULL);
416}
417
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000418SkCanvas::SkCanvas(int width, int height)
419 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
420{
421 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000422
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000423 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000424 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000425 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
426}
427
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000428SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000429 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
430{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 inc_canvas();
432
433 this->init(device);
434}
435
436SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000437 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
438{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439 inc_canvas();
440
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000441 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442}
443
444SkCanvas::~SkCanvas() {
445 // free up the contents of our deque
446 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000447 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 this->internalRestore(); // restore the last, since we're going away
450
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000451 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000452
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 dec_canvas();
454}
455
reed@android.com8a1c16f2008-12-17 15:59:43 +0000456SkDrawFilter* SkCanvas::getDrawFilter() const {
457 return fMCRec->fFilter;
458}
459
460SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
461 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
462 return filter;
463}
464
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000465SkMetaData& SkCanvas::getMetaData() {
466 // metadata users are rare, so we lazily allocate it. If that changes we
467 // can decide to just make it a field in the device (rather than a ptr)
468 if (NULL == fMetaData) {
469 fMetaData = new SkMetaData;
470 }
471 return *fMetaData;
472}
473
reed@android.com8a1c16f2008-12-17 15:59:43 +0000474///////////////////////////////////////////////////////////////////////////////
475
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000476void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000477 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000478 if (device) {
479 device->flush();
480 }
481}
482
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000483SkISize SkCanvas::getTopLayerSize() const {
484 SkBaseDevice* d = this->getTopDevice();
485 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
486}
487
488SkIPoint SkCanvas::getTopLayerOrigin() const {
489 SkBaseDevice* d = this->getTopDevice();
490 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
491}
492
493SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000494 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000495 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
496}
497
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000498SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000500 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000501 SkASSERT(rec && rec->fLayer);
502 return rec->fLayer->fDevice;
503}
504
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000505SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000506 if (updateMatrixClip) {
507 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
508 }
reed@google.com9266fed2011-03-30 00:18:03 +0000509 return fMCRec->fTopLayer->fDevice;
510}
511
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000512SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000513 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000514 SkDeque::F2BIter iter(fMCStack);
515 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000517 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518
519 if (rootDevice == device) {
520 return device;
521 }
reed@google.com4b226022011-01-11 18:32:13 +0000522
reed@android.com8a1c16f2008-12-17 15:59:43 +0000523 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000524 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 }
526 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000527 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 }
529
530 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
531 rootDevice = device;
532
533 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000534
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 /* Now we update our initial region to have the bounds of the new device,
536 and then intersect all of the clips in our stack with these bounds,
537 to ensure that we can't draw outside of the device's bounds (and trash
538 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000539
reed@android.com8a1c16f2008-12-17 15:59:43 +0000540 NOTE: this is only a partial-fix, since if the new device is larger than
541 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000542 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
544 reconstruct the correct clips, so this approximation will have to do.
545 The caller really needs to restore() back to the base if they want to
546 accurately take advantage of the new device bounds.
547 */
548
reed@google.com42aea282012-03-28 16:19:15 +0000549 SkIRect bounds;
550 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000552 } else {
553 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554 }
reed@google.com42aea282012-03-28 16:19:15 +0000555 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700556 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000557 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700558 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000559 }
560
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561 return device;
562}
563
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000564bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
565 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
566 return false;
567 }
568
569 bool weAllocated = false;
570 if (NULL == bitmap->pixelRef()) {
571 if (!bitmap->allocPixels()) {
572 return false;
573 }
574 weAllocated = true;
575 }
576
577 SkBitmap bm(*bitmap);
578 bm.lockPixels();
579 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
580 return true;
581 }
582
583 if (weAllocated) {
584 bitmap->setPixelRef(NULL);
585 }
586 return false;
587}
reed@google.com51df9e32010-12-23 19:29:18 +0000588
bsalomon@google.comc6980972011-11-02 19:57:21 +0000589bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000590 SkIRect r = srcRect;
591 const SkISize size = this->getBaseLayerSize();
592 if (!r.intersect(0, 0, size.width(), size.height())) {
593 bitmap->reset();
594 return false;
595 }
596
597 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
598 // bitmap will already be reset.
599 return false;
600 }
601 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
602 bitmap->reset();
603 return false;
604 }
605 return true;
606}
607
608bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
609 switch (origInfo.colorType()) {
610 case kUnknown_SkColorType:
611 case kIndex_8_SkColorType:
612 return false;
613 default:
614 break;
615 }
616 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
617 return false;
618 }
619 if (0 == origInfo.width() || 0 == origInfo.height()) {
620 return false;
621 }
622
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000623 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000624 if (!device) {
625 return false;
626 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000627
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000628 const SkISize size = this->getBaseLayerSize();
629 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
630 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000631 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000632 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000633
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000634 SkImageInfo info = origInfo;
635 // the intersect may have shrunk info's logical size
636 info.fWidth = srcR.width();
637 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000638
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000639 // if x or y are negative, then we have to adjust pixels
640 if (x > 0) {
641 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000642 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000643 if (y > 0) {
644 y = 0;
645 }
646 // here x,y are either 0 or negative
647 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000648
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000649 // The device can assert that the requested area is always contained in its bounds
650 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000651}
652
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000653bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
654 if (bitmap.getTexture()) {
655 return false;
656 }
657 SkBitmap bm(bitmap);
658 bm.lockPixels();
659 if (bm.getPixels()) {
660 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
661 }
662 return false;
663}
664
665bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
666 int x, int y) {
667 switch (origInfo.colorType()) {
668 case kUnknown_SkColorType:
669 case kIndex_8_SkColorType:
670 return false;
671 default:
672 break;
673 }
674 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
675 return false;
676 }
677
678 const SkISize size = this->getBaseLayerSize();
679 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
680 if (!target.intersect(0, 0, size.width(), size.height())) {
681 return false;
682 }
683
684 SkBaseDevice* device = this->getDevice();
685 if (!device) {
686 return false;
687 }
688
689 SkImageInfo info = origInfo;
690 // the intersect may have shrunk info's logical size
691 info.fWidth = target.width();
692 info.fHeight = target.height();
693
694 // if x or y are negative, then we have to adjust pixels
695 if (x > 0) {
696 x = 0;
697 }
698 if (y > 0) {
699 y = 0;
700 }
701 // here x,y are either 0 or negative
702 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
703
reed4af35f32014-06-27 17:47:49 -0700704 // Tell our owning surface to bump its generation ID
705 this->predrawNotify();
706
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000707 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000708 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000709}
reed@google.com51df9e32010-12-23 19:29:18 +0000710
junov@google.com4370aed2012-01-18 16:21:08 +0000711SkCanvas* SkCanvas::canvasForDrawIter() {
712 return this;
713}
714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715//////////////////////////////////////////////////////////////////////////////
716
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717void SkCanvas::updateDeviceCMCache() {
718 if (fDeviceCMDirty) {
719 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700720 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000722
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000724 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000726 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000728 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 } while ((layer = layer->fNext) != NULL);
730 }
731 fDeviceCMDirty = false;
732 }
733}
734
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735///////////////////////////////////////////////////////////////////////////////
736
Florin Malita5f6102d2014-06-30 10:13:28 -0400737int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000739
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 MCRec* newTop = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400741 new (newTop) MCRec(fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000743
Florin Malita5f6102d2014-06-30 10:13:28 -0400744 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000745
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 return saveCount;
747}
748
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000749int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400750 this->willSave();
751 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752}
753
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000755#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000757#else
758 return true;
759#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760}
761
junov@chromium.orga907ac32012-02-24 21:54:07 +0000762bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000763 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000764 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000765 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000766 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000767 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000768 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000769
770 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700771 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000772 // Filters may grow the bounds beyond the device bounds.
773 op = SkRegion::kReplace_Op;
774 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000775 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000776 if (NULL != bounds) {
777 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000778
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779 this->getTotalMatrix().mapRect(&r, *bounds);
780 r.roundOut(&ir);
781 // early exit if the layer's bounds are clipped out
782 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000783 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700784 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000785 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000786 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 }
788 } else { // no user bounds, so just use the clip
789 ir = clipBounds;
790 }
791
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000792 if (bounds_affects_clip(flags)) {
793 fClipStack.clipDevRect(ir, op);
794 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700795 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000796 return false;
797 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000798 }
799
800 if (intersection) {
801 *intersection = ir;
802 }
803 return true;
804}
805
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000806int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
807 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
808 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
809}
810
junov@chromium.orga907ac32012-02-24 21:54:07 +0000811int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
812 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000813 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
814 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000815}
816
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000817int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
818 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000819#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000820 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000821#endif
822
junov@chromium.orga907ac32012-02-24 21:54:07 +0000823 // do this before we create the layer. We don't call the public save() since
824 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400825 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000826
827 fDeviceCMDirty = true;
828
829 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000830 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000831 return count;
832 }
833
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000834 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
835 // the clipRectBounds() call above?
836 if (kNoLayer_SaveLayerStrategy == strategy) {
837 return count;
838 }
839
reed@google.comb55deeb2012-01-06 14:43:09 +0000840 // Kill the imagefilter if our device doesn't allow it
841 SkLazyPaint lazyP;
842 if (paint && paint->getImageFilter()) {
843 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000844 if (justForImageFilter) {
845 // early exit if the layer was just for the imageFilter
846 return count;
847 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000848 SkPaint* p = lazyP.set(*paint);
849 p->setImageFilter(NULL);
850 paint = p;
851 }
852 }
853
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000854 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
855 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
856 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000857
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000858 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000859 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700860 device = this->getDevice();
861 if (device) {
862 device = device->createCompatibleDevice(info);
863 }
reed@google.com76dd2772012-01-05 21:15:07 +0000864 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000865 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000866 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000867 if (NULL == device) {
868 SkDebugf("Unable to create device for layer.");
869 return count;
870 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000871
reed@google.com6f8f2922011-03-04 22:27:10 +0000872 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000873 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 device->unref();
875
876 layer->fNext = fMCRec->fTopLayer;
877 fMCRec->fLayer = layer;
878 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
879
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000880 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881 return count;
882}
883
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000884int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
885 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
886}
887
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
889 SaveFlags flags) {
890 if (0xFF == alpha) {
891 return this->saveLayer(bounds, NULL, flags);
892 } else {
893 SkPaint tmpPaint;
894 tmpPaint.setAlpha(alpha);
895 return this->saveLayer(bounds, &tmpPaint, flags);
896 }
897}
898
899void SkCanvas::restore() {
900 // check for underflow
901 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000902 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700904 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 }
906}
907
908void SkCanvas::internalRestore() {
909 SkASSERT(fMCStack.count() != 0);
910
911 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000912 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913
Florin Malita5f6102d2014-06-30 10:13:28 -0400914 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000915
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000916 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000917 DeviceCM* layer = fMCRec->fLayer; // may be null
918 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
919 fMCRec->fLayer = NULL;
920
921 // now do the normal restore()
922 fMCRec->~MCRec(); // balanced in save()
923 fMCStack.pop_back();
924 fMCRec = (MCRec*)fMCStack.back();
925
926 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
927 since if we're being recorded, we don't want to record this (the
928 recorder will have already recorded the restore).
929 */
930 if (NULL != layer) {
931 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000932 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000933 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
934 layer->fPaint);
935 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000937
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000938 SkASSERT(fSaveLayerCount > 0);
939 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000940 }
941 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000942 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000943}
944
945int SkCanvas::getSaveCount() const {
946 return fMCStack.count();
947}
948
949void SkCanvas::restoreToCount(int count) {
950 // sanity check
951 if (count < 1) {
952 count = 1;
953 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000954
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000955 int n = this->getSaveCount() - count;
956 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 this->restore();
958 }
959}
960
reed@google.com7c202932011-12-14 18:48:05 +0000961bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000962 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000963}
964
reed@google.com76f10a32014-02-05 15:32:21 +0000965SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
966 return this->onNewSurface(info);
967}
968
969SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
970 SkBaseDevice* dev = this->getDevice();
971 return dev ? dev->newSurface(info) : NULL;
972}
973
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000974SkImageInfo SkCanvas::imageInfo() const {
975 SkBaseDevice* dev = this->getDevice();
976 if (dev) {
977 return dev->imageInfo();
978 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000979 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000980 }
981}
982
983const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
984 return this->onPeekPixels(info, rowBytes);
985}
986
987const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
988 SkBaseDevice* dev = this->getDevice();
989 return dev ? dev->peekPixels(info, rowBytes) : NULL;
990}
991
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +0000992void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
993 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
994 if (pixels && origin) {
995 *origin = this->getTopDevice(false)->getOrigin();
996 }
997 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +0000998}
999
1000void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1001 SkBaseDevice* dev = this->getTopDevice();
1002 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1003}
1004
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001005SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1006 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1007 if (NULL == fAddr) {
1008 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001009 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001010 return; // failure, fAddr is NULL
1011 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001012 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1013 return; // failure, fAddr is NULL
1014 }
1015 fAddr = fBitmap.getPixels();
1016 fRowBytes = fBitmap.rowBytes();
1017 }
1018 SkASSERT(fAddr); // success
1019}
1020
1021bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1022 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001023 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001024 } else {
1025 bitmap->reset();
1026 return false;
1027 }
1028}
1029
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001030void SkCanvas::onPushCull(const SkRect& cullRect) {
1031 // do nothing. Subclasses may do something
1032}
1033
1034void SkCanvas::onPopCull() {
1035 // do nothing. Subclasses may do something
1036}
1037
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001039#ifdef SK_DEBUG
1040// Ensure that cull rects are monotonically nested in device space.
1041void SkCanvas::validateCull(const SkIRect& devCull) {
1042 if (fCullStack.isEmpty()
1043 || devCull.isEmpty()
1044 || fCullStack.top().contains(devCull)) {
1045 return;
1046 }
1047
1048 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1049 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1050 fCullStack.top().x(), fCullStack.top().y(),
1051 fCullStack.top().right(), fCullStack.top().bottom()));
1052
1053#ifdef ASSERT_NESTED_CULLING
1054 SkDEBUGFAIL("Invalid cull.");
1055#endif
1056}
1057#endif
1058
1059void SkCanvas::pushCull(const SkRect& cullRect) {
1060 ++fCullCount;
1061 this->onPushCull(cullRect);
1062
1063#ifdef SK_DEBUG
1064 // Map the cull rect into device space.
1065 SkRect mappedCull;
1066 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1067
1068 // Take clipping into account.
1069 SkIRect devClip, devCull;
1070 mappedCull.roundOut(&devCull);
1071 this->getClipDeviceBounds(&devClip);
1072 if (!devCull.intersect(devClip)) {
1073 devCull.setEmpty();
1074 }
1075
1076 this->validateCull(devCull);
1077 fCullStack.push(devCull); // balanced in popCull
1078#endif
1079}
1080
1081void SkCanvas::popCull() {
1082 SkASSERT(fCullStack.count() == fCullCount);
1083
1084 if (fCullCount > 0) {
1085 --fCullCount;
1086 this->onPopCull();
1087
1088 SkDEBUGCODE(fCullStack.pop());
1089 }
1090}
1091
1092/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001094void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001096 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 return;
1098 }
1099
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001100 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001102 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001104
1105 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001106
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001107 SkRect storage;
1108 const SkRect* bounds = NULL;
1109 if (paint && paint->canComputeFastBounds()) {
1110 bitmap.getBounds(&storage);
1111 matrix.mapRect(&storage);
1112 bounds = &paint->computeFastBounds(storage, &storage);
1113 }
1114
1115 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001116
1117 while (iter.next()) {
1118 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1119 }
1120
1121 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122}
1123
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001124void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001125 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 SkPaint tmp;
1127 if (NULL == paint) {
1128 tmp.setDither(true);
1129 paint = &tmp;
1130 }
reed@google.com4b226022011-01-11 18:32:13 +00001131
reed@google.com8926b162012-03-23 15:36:36 +00001132 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001134 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001135 paint = &looper.paint();
1136 SkImageFilter* filter = paint->getImageFilter();
1137 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001138 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001139 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001140 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001141 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001142 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001143 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001144 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001145 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001146 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001147 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001148 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001149 SkPaint tmpUnfiltered(*paint);
1150 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001151 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1152 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001153 }
1154 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001155 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001156 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001157 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001158 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001159}
1160
reed@google.com8926b162012-03-23 15:36:36 +00001161void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1162 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001163 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001164 return;
1165 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001166 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001167
reed@google.com8926b162012-03-23 15:36:36 +00001168 SkPaint tmp;
1169 if (NULL == paint) {
1170 paint = &tmp;
1171 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001172
reed@google.com8926b162012-03-23 15:36:36 +00001173 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001174
reed@google.com8926b162012-03-23 15:36:36 +00001175 while (iter.next()) {
1176 paint = &looper.paint();
1177 SkImageFilter* filter = paint->getImageFilter();
1178 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1179 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001180 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001181 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001182 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001183 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001184 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001185 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001186 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001187 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001188 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001189 SkPaint tmpUnfiltered(*paint);
1190 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001191 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001192 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001193 }
1194 } else {
1195 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1196 }
1197 }
1198 LOOPER_END
1199}
1200
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001202void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001203 SkMatrix m;
1204 m.setTranslate(dx, dy);
1205 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206}
1207
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001208void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001209 SkMatrix m;
1210 m.setScale(sx, sy);
1211 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212}
1213
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001214void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001215 SkMatrix m;
1216 m.setRotate(degrees);
1217 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001218}
1219
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001220void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001221 SkMatrix m;
1222 m.setSkew(sx, sy);
1223 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001224}
1225
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001226void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001227 if (matrix.isIdentity()) {
1228 return;
1229 }
1230
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001232 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001233 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001234
1235 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001236}
1237
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238void SkCanvas::setMatrix(const SkMatrix& matrix) {
1239 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001240 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001241 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001242 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001243}
1244
reed@android.com8a1c16f2008-12-17 15:59:43 +00001245void SkCanvas::resetMatrix() {
1246 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001247
reed@android.com8a1c16f2008-12-17 15:59:43 +00001248 matrix.reset();
1249 this->setMatrix(matrix);
1250}
1251
1252//////////////////////////////////////////////////////////////////////////////
1253
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001254void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001255 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1256 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001257}
1258
1259void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001260#ifdef SK_ENABLE_CLIP_QUICKREJECT
1261 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001262 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001263 return false;
1264 }
1265
reed@google.com3b3e8952012-08-16 20:53:31 +00001266 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001267 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001268 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001269
1270 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001271 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001272 }
1273 }
1274#endif
1275
reed@google.com5c3d1472011-02-22 19:12:23 +00001276 AutoValidateClip avc(this);
1277
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001279 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001280 if (!fAllowSoftClip) {
1281 edgeStyle = kHard_ClipEdgeStyle;
1282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283
reed1f836ee2014-07-07 07:49:34 -07001284 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001285 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001286 // the matrix. This means we don't have to a) make a path, and b) tell
1287 // the region code to scan-convert the path, only to discover that it
1288 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290
reed1f836ee2014-07-07 07:49:34 -07001291 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001292 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed1f836ee2014-07-07 07:49:34 -07001293 fMCRec->fRasterClip.op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001295 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001296 // and clip against that, since it can handle any matrix. However, to
1297 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1298 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 SkPath path;
1300
1301 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001302 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 }
1304}
1305
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001306static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1307 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001308 // base is used to limit the size (and therefore memory allocation) of the
1309 // region that results from scan converting devPath.
1310 SkRegion base;
1311
reed@google.com819c9212011-02-23 18:56:55 +00001312 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001313 // since we are intersect, we can do better (tighter) with currRgn's
1314 // bounds, than just using the device. However, if currRgn is complex,
1315 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001316 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001317 // FIXME: we should also be able to do this when currClip->isBW(),
1318 // but relaxing the test above triggers GM asserts in
1319 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001320 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001321 } else {
reed@google.com00177082011-10-12 14:34:30 +00001322 base.setRect(currClip->getBounds());
1323 SkRasterClip clip;
1324 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001325 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001326 }
reed@google.com819c9212011-02-23 18:56:55 +00001327 } else {
reed52d9ac62014-06-30 09:05:34 -07001328 const SkISize size = canvas->getBaseLayerSize();
1329 base.setRect(0, 0, size.width(), size.height());
reed@google.com819c9212011-02-23 18:56:55 +00001330
1331 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001332 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001333 } else {
reed@google.com00177082011-10-12 14:34:30 +00001334 SkRasterClip clip;
1335 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001336 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001337 }
1338 }
1339}
1340
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001341void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001342 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001343 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001344 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1345 } else {
1346 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001347 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001348}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001349
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001350void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001351 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001352 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001353 AutoValidateClip avc(this);
1354
1355 fDeviceCMDirty = true;
1356 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001357 if (!fAllowSoftClip) {
1358 edgeStyle = kHard_ClipEdgeStyle;
1359 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001360
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001361 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001362
1363 SkPath devPath;
1364 devPath.addRRect(transformedRRect);
1365
reed1f836ee2014-07-07 07:49:34 -07001366 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001367 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001368 }
1369
1370 SkPath path;
1371 path.addRRect(rrect);
1372 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001373 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001374}
1375
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001376void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001377 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1378 SkRect r;
1379 if (!path.isInverseFillType() && path.isRect(&r)) {
1380 this->onClipRect(r, op, edgeStyle);
1381 } else {
1382 this->onClipPath(path, op, edgeStyle);
1383 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001384}
1385
1386void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001387#ifdef SK_ENABLE_CLIP_QUICKREJECT
1388 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001389 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001390 return false;
1391 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001392
reed@google.com3b3e8952012-08-16 20:53:31 +00001393 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001394 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001395 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001396
reed@google.comda17f752012-08-16 18:27:05 +00001397 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001398 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001399 }
1400 }
1401#endif
1402
reed@google.com5c3d1472011-02-22 19:12:23 +00001403 AutoValidateClip avc(this);
1404
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001406 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001407 if (!fAllowSoftClip) {
1408 edgeStyle = kHard_ClipEdgeStyle;
1409 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410
1411 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001412 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413
reed@google.comfe701122011-11-08 19:41:23 +00001414 // Check if the transfomation, or the original path itself
1415 // made us empty. Note this can also happen if we contained NaN
1416 // values. computing the bounds detects this, and will set our
1417 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1418 if (devPath.getBounds().isEmpty()) {
1419 // resetting the path will remove any NaN or other wanky values
1420 // that might upset our scan converter.
1421 devPath.reset();
1422 }
1423
reed@google.com5c3d1472011-02-22 19:12:23 +00001424 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001425 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001426
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001427 if (fAllowSimplifyClip) {
1428 devPath.reset();
1429 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1430 const SkClipStack* clipStack = getClipStack();
1431 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1432 const SkClipStack::Element* element;
1433 while ((element = iter.next())) {
1434 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001435 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001436 if (type != SkClipStack::Element::kEmpty_Type) {
1437 element->asPath(&operand);
1438 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001439 SkRegion::Op elementOp = element->getOp();
1440 if (elementOp == SkRegion::kReplace_Op) {
1441 devPath = operand;
1442 } else {
1443 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1444 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001445 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1446 // perhaps we need an API change to avoid this sort of mixed-signals about
1447 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001448 if (element->isAA()) {
1449 edgeStyle = kSoft_ClipEdgeStyle;
1450 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001451 }
1452 op = SkRegion::kReplace_Op;
1453 }
1454
reed1f836ee2014-07-07 07:49:34 -07001455 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456}
1457
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001458void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001459 bool inverseFilled) {
1460 // This is for updating the clip conservatively using only bounds
1461 // information.
1462 // Contract:
1463 // The current clip must contain the true clip. The true
1464 // clip is the clip that would have normally been computed
1465 // by calls to clipPath and clipRRect
1466 // Objective:
1467 // Keep the current clip as small as possible without
1468 // breaking the contract, using only clip bounding rectangles
1469 // (for performance).
1470
1471 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1472 // don't have to worry about getting caught in a loop. Thus anywhere
1473 // we call a virtual method, we explicitly prefix it with
1474 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001475
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001476 if (inverseFilled) {
1477 switch (op) {
1478 case SkRegion::kIntersect_Op:
1479 case SkRegion::kDifference_Op:
1480 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001481 // the clip unchanged conservatively respects the contract.
1482 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001483 case SkRegion::kUnion_Op:
1484 case SkRegion::kReplace_Op:
1485 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001486 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001487 // These ops can grow the current clip up to the extents of
1488 // the input clip, which is inverse filled, so we just set
1489 // the current clip to the device bounds.
1490 SkRect deviceBounds;
1491 SkIRect deviceIBounds;
1492 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001493 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001494
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001495 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001496 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001497 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001498 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001499 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001500 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001501 break;
1502 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001503 default:
1504 SkASSERT(0); // unhandled op?
1505 }
1506 } else {
1507 // Not inverse filled
1508 switch (op) {
1509 case SkRegion::kIntersect_Op:
1510 case SkRegion::kUnion_Op:
1511 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001512 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1513 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001514 case SkRegion::kDifference_Op:
1515 // Difference can only shrink the current clip.
1516 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001517 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001518 case SkRegion::kReverseDifference_Op:
1519 // To reverse, we swap in the bounds with a replace op.
1520 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001521 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1522 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001523 case SkRegion::kXOR_Op:
1524 // Be conservative, based on (A XOR B) always included in (A union B),
1525 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1527 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001528 default:
1529 SkASSERT(0); // unhandled op?
1530 }
1531 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001532}
1533
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001534void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001535 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001536}
1537
1538void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001539 AutoValidateClip avc(this);
1540
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001542 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543
reed@google.com5c3d1472011-02-22 19:12:23 +00001544 // todo: signal fClipStack that we have a region, and therefore (I guess)
1545 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001546 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001547
reed1f836ee2014-07-07 07:49:34 -07001548 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001549}
1550
reed@google.com819c9212011-02-23 18:56:55 +00001551#ifdef SK_DEBUG
1552void SkCanvas::validateClip() const {
1553 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001554 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001555 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001556 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001557 return;
1558 }
1559
reed@google.com819c9212011-02-23 18:56:55 +00001560 SkIRect ir;
1561 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001562 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001563
robertphillips@google.com80214e22012-07-20 15:33:18 +00001564 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001565 const SkClipStack::Element* element;
1566 while ((element = iter.next()) != NULL) {
1567 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001568 case SkClipStack::Element::kRect_Type:
1569 element->getRect().round(&ir);
1570 tmpClip.op(ir, element->getOp());
1571 break;
1572 case SkClipStack::Element::kEmpty_Type:
1573 tmpClip.setEmpty();
1574 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001575 default: {
1576 SkPath path;
1577 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001578 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001579 break;
1580 }
reed@google.com819c9212011-02-23 18:56:55 +00001581 }
1582 }
reed@google.com819c9212011-02-23 18:56:55 +00001583}
1584#endif
1585
reed@google.com90c07ea2012-04-13 13:50:27 +00001586void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001587 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001588 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001589
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001590 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001591 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001592 }
1593}
1594
reed@google.com5c3d1472011-02-22 19:12:23 +00001595///////////////////////////////////////////////////////////////////////////////
1596
reed@google.com754de5f2014-02-24 19:38:20 +00001597bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001598 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001599}
1600
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001601bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001602 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001603}
1604
reed@google.com3b3e8952012-08-16 20:53:31 +00001605bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001606
reed@google.com16078632011-12-06 18:56:37 +00001607 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001608 return true;
1609
reed1f836ee2014-07-07 07:49:34 -07001610 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 return true;
1612 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613
reed1f836ee2014-07-07 07:49:34 -07001614 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001615 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001616 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001617 SkIRect idst;
1618 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001619 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001620 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001621 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001622
reed@android.coma380ae42009-07-21 01:17:02 +00001623 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001624 // TODO: should we use | instead, or compare all 4 at once?
1625 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001626 return true;
1627 }
reed@google.comc0784db2013-12-13 21:16:12 +00001628 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001629 return true;
1630 }
1631 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633}
1634
reed@google.com3b3e8952012-08-16 20:53:31 +00001635bool SkCanvas::quickReject(const SkPath& path) const {
1636 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637}
1638
reed@google.com3b3e8952012-08-16 20:53:31 +00001639bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001640 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001641 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001642 return false;
1643 }
1644
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001645 SkMatrix inverse;
1646 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001647 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001648 if (bounds) {
1649 bounds->setEmpty();
1650 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001651 return false;
1652 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001654 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001655 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001656 // adjust it outwards in case we are antialiasing
1657 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001658
reed@google.com8f4d2302013-12-17 16:44:46 +00001659 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1660 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001661 inverse.mapRect(bounds, r);
1662 }
1663 return true;
1664}
1665
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001666bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001667 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001668 if (clip.isEmpty()) {
1669 if (bounds) {
1670 bounds->setEmpty();
1671 }
1672 return false;
1673 }
1674
1675 if (NULL != bounds) {
1676 *bounds = clip.getBounds();
1677 }
1678 return true;
1679}
1680
reed@android.com8a1c16f2008-12-17 15:59:43 +00001681const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001682 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683}
1684
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001685const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001686 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001687}
1688
1689void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1690 path->reset();
1691
reed1f836ee2014-07-07 07:49:34 -07001692 const SkRegion& rgn = fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001693 if (rgn.isEmpty()) {
1694 return;
1695 }
1696 (void)rgn.getBoundaryPath(path);
1697}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001698
reed@google.com9c135db2014-03-12 18:28:35 +00001699GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1700 SkBaseDevice* dev = this->getTopDevice();
1701 return dev ? dev->accessRenderTarget() : NULL;
1702}
1703
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001704SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001705 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001706 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001707}
1708
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001709GrContext* SkCanvas::getGrContext() {
1710#if SK_SUPPORT_GPU
1711 SkBaseDevice* device = this->getTopDevice();
1712 if (NULL != device) {
1713 GrRenderTarget* renderTarget = device->accessRenderTarget();
1714 if (NULL != renderTarget) {
1715 return renderTarget->getContext();
1716 }
1717 }
1718#endif
1719
1720 return NULL;
1721
1722}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001723
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001724void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1725 const SkPaint& paint) {
1726 if (outer.isEmpty()) {
1727 return;
1728 }
1729 if (inner.isEmpty()) {
1730 this->drawRRect(outer, paint);
1731 return;
1732 }
1733
1734 // We don't have this method (yet), but technically this is what we should
1735 // be able to assert...
1736 // SkASSERT(outer.contains(inner));
1737 //
1738 // For now at least check for containment of bounds
1739 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1740
1741 this->onDrawDRRect(outer, inner, paint);
1742}
1743
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744//////////////////////////////////////////////////////////////////////////////
1745// These are the virtual drawing methods
1746//////////////////////////////////////////////////////////////////////////////
1747
reed@google.com2a981812011-04-14 18:59:28 +00001748void SkCanvas::clear(SkColor color) {
1749 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001750 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001751 while (iter.next()) {
1752 iter.fDevice->clear(color);
1753 }
1754}
1755
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001756void SkCanvas::onDiscard() {
1757 if (NULL != fSurfaceBase) {
1758 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1759 }
1760}
1761
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001763 this->internalDrawPaint(paint);
1764}
1765
1766void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001767 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768
1769 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001770 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771 }
1772
reed@google.com4e2b3d32011-04-07 14:18:59 +00001773 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774}
1775
1776void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1777 const SkPaint& paint) {
1778 if ((long)count <= 0) {
1779 return;
1780 }
1781
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001782 SkRect r, storage;
1783 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001784 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001785 // special-case 2 points (common for drawing a single line)
1786 if (2 == count) {
1787 r.set(pts[0], pts[1]);
1788 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001789 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001790 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001791 bounds = &paint.computeFastStrokeBounds(r, &storage);
1792 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001793 return;
1794 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001795 }
reed@google.coma584aed2012-05-16 14:06:02 +00001796
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797 SkASSERT(pts != NULL);
1798
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001799 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001800
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001802 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 }
reed@google.com4b226022011-01-11 18:32:13 +00001804
reed@google.com4e2b3d32011-04-07 14:18:59 +00001805 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806}
1807
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001808void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001809 SkRect storage;
1810 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001812 bounds = &paint.computeFastBounds(r, &storage);
1813 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 return;
1815 }
1816 }
reed@google.com4b226022011-01-11 18:32:13 +00001817
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001818 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819
1820 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001821 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822 }
1823
reed@google.com4e2b3d32011-04-07 14:18:59 +00001824 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825}
1826
reed@google.com4ed0fb72012-12-12 20:48:18 +00001827void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001828 SkRect storage;
1829 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001830 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001831 bounds = &paint.computeFastBounds(oval, &storage);
1832 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001833 return;
1834 }
1835 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001836
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001837 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001838
1839 while (iter.next()) {
1840 iter.fDevice->drawOval(iter, oval, looper.paint());
1841 }
1842
1843 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001844}
1845
1846void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001847 SkRect storage;
1848 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001849 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001850 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1851 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001852 return;
1853 }
1854 }
1855
1856 if (rrect.isRect()) {
1857 // call the non-virtual version
1858 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001859 return;
1860 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001861 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001862 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1863 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001864 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001865
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001866 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001867
1868 while (iter.next()) {
1869 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1870 }
1871
1872 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001873}
1874
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001875void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1876 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001877 SkRect storage;
1878 const SkRect* bounds = NULL;
1879 if (paint.canComputeFastBounds()) {
1880 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1881 if (this->quickReject(*bounds)) {
1882 return;
1883 }
1884 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001885
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001886 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001887
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001888 while (iter.next()) {
1889 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1890 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001891
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001892 LOOPER_END
1893}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001894
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001895void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001896 if (!path.isFinite()) {
1897 return;
1898 }
1899
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001900 SkRect storage;
1901 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001902 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001903 const SkRect& pathBounds = path.getBounds();
1904 bounds = &paint.computeFastBounds(pathBounds, &storage);
1905 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001906 return;
1907 }
1908 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001909
1910 const SkRect& r = path.getBounds();
1911 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001912 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001913 this->internalDrawPaint(paint);
1914 }
1915 return;
1916 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001918 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001919
1920 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001921 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001922 }
1923
reed@google.com4e2b3d32011-04-07 14:18:59 +00001924 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925}
1926
1927void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1928 const SkPaint* paint) {
1929 SkDEBUGCODE(bitmap.validate();)
1930
reed@google.com3d608122011-11-21 15:16:16 +00001931 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001932 SkRect bounds = {
1933 x, y,
1934 x + SkIntToScalar(bitmap.width()),
1935 y + SkIntToScalar(bitmap.height())
1936 };
1937 if (paint) {
1938 (void)paint->computeFastBounds(bounds, &bounds);
1939 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001940 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941 return;
1942 }
1943 }
reed@google.com4b226022011-01-11 18:32:13 +00001944
reed@android.com8a1c16f2008-12-17 15:59:43 +00001945 SkMatrix matrix;
1946 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001947 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948}
1949
reed@google.com9987ec32011-09-07 11:57:52 +00001950// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001951void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001952 const SkRect& dst, const SkPaint* paint,
1953 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001954 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001955 return;
1956 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001957
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001958 SkRect storage;
1959 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001960 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001961 if (paint) {
1962 bounds = &paint->computeFastBounds(dst, &storage);
1963 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001964 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001965 return;
1966 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001967 }
reed@google.com3d608122011-11-21 15:16:16 +00001968
reed@google.com33535f32012-09-25 15:37:50 +00001969 SkLazyPaint lazy;
1970 if (NULL == paint) {
1971 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001972 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001973
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001974 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001975
reed@google.com33535f32012-09-25 15:37:50 +00001976 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001977 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001978 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001979
reed@google.com33535f32012-09-25 15:37:50 +00001980 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001981}
1982
reed@google.com71121732012-09-18 15:14:33 +00001983void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001984 const SkRect& dst, const SkPaint* paint,
1985 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001986 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001987 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001988}
1989
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1991 const SkPaint* paint) {
1992 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001993 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001994}
1995
reed@google.com9987ec32011-09-07 11:57:52 +00001996void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1997 const SkIRect& center, const SkRect& dst,
1998 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001999 if (bitmap.drawsNothing()) {
2000 return;
2001 }
reed@google.com3d608122011-11-21 15:16:16 +00002002 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002003 SkRect storage;
2004 const SkRect* bounds = &dst;
2005 if (paint) {
2006 bounds = &paint->computeFastBounds(dst, &storage);
2007 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002008 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002009 return;
2010 }
2011 }
2012
reed@google.com9987ec32011-09-07 11:57:52 +00002013 const int32_t w = bitmap.width();
2014 const int32_t h = bitmap.height();
2015
2016 SkIRect c = center;
2017 // pin center to the bounds of the bitmap
2018 c.fLeft = SkMax32(0, center.fLeft);
2019 c.fTop = SkMax32(0, center.fTop);
2020 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2021 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2022
reed@google.com71121732012-09-18 15:14:33 +00002023 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002024 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002025 };
2026 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002027 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002028 };
reed@google.com9987ec32011-09-07 11:57:52 +00002029 SkScalar dstX[4] = {
2030 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2031 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2032 };
2033 SkScalar dstY[4] = {
2034 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2035 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2036 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002037
reed@google.com9987ec32011-09-07 11:57:52 +00002038 if (dstX[1] > dstX[2]) {
2039 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2040 dstX[2] = dstX[1];
2041 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002042
reed@google.com9987ec32011-09-07 11:57:52 +00002043 if (dstY[1] > dstY[2]) {
2044 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2045 dstY[2] = dstY[1];
2046 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002047
reed@google.com9987ec32011-09-07 11:57:52 +00002048 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002049 SkRect s, d;
2050
reed@google.com9987ec32011-09-07 11:57:52 +00002051 s.fTop = srcY[y];
2052 s.fBottom = srcY[y+1];
2053 d.fTop = dstY[y];
2054 d.fBottom = dstY[y+1];
2055 for (int x = 0; x < 3; x++) {
2056 s.fLeft = srcX[x];
2057 s.fRight = srcX[x+1];
2058 d.fLeft = dstX[x];
2059 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002060 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002061 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002062 }
2063 }
2064}
2065
2066void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2067 const SkRect& dst, const SkPaint* paint) {
2068 SkDEBUGCODE(bitmap.validate();)
2069
2070 // Need a device entry-point, so gpu can use a mesh
2071 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2072}
2073
reed@google.comf67e4cf2011-03-15 20:56:58 +00002074class SkDeviceFilteredPaint {
2075public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002076 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2077 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002078 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002079 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002080 newPaint->setFlags(flags.fFlags);
2081 newPaint->setHinting(flags.fHinting);
2082 fPaint = newPaint;
2083 } else {
2084 fPaint = &paint;
2085 }
2086 }
2087
reed@google.comf67e4cf2011-03-15 20:56:58 +00002088 const SkPaint& paint() const { return *fPaint; }
2089
2090private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002091 const SkPaint* fPaint;
2092 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002093};
2094
bungeman@google.com52c748b2011-08-22 21:30:43 +00002095void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2096 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002097 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002098 draw.fDevice->drawRect(draw, r, paint);
2099 } else {
2100 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002101 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002102 draw.fDevice->drawRect(draw, r, p);
2103 }
2104}
2105
2106void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2107 const char text[], size_t byteLength,
2108 SkScalar x, SkScalar y) {
2109 SkASSERT(byteLength == 0 || text != NULL);
2110
2111 // nothing to draw
2112 if (text == NULL || byteLength == 0 ||
2113 draw.fClip->isEmpty() ||
2114 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2115 return;
2116 }
2117
2118 SkScalar width = 0;
2119 SkPoint start;
2120
2121 start.set(0, 0); // to avoid warning
2122 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2123 SkPaint::kStrikeThruText_Flag)) {
2124 width = paint.measureText(text, byteLength);
2125
2126 SkScalar offsetX = 0;
2127 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2128 offsetX = SkScalarHalf(width);
2129 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2130 offsetX = width;
2131 }
2132 start.set(x - offsetX, y);
2133 }
2134
2135 if (0 == width) {
2136 return;
2137 }
2138
2139 uint32_t flags = paint.getFlags();
2140
2141 if (flags & (SkPaint::kUnderlineText_Flag |
2142 SkPaint::kStrikeThruText_Flag)) {
2143 SkScalar textSize = paint.getTextSize();
2144 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2145 SkRect r;
2146
2147 r.fLeft = start.fX;
2148 r.fRight = start.fX + width;
2149
2150 if (flags & SkPaint::kUnderlineText_Flag) {
2151 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2152 start.fY);
2153 r.fTop = offset;
2154 r.fBottom = offset + height;
2155 DrawRect(draw, paint, r, textSize);
2156 }
2157 if (flags & SkPaint::kStrikeThruText_Flag) {
2158 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2159 start.fY);
2160 r.fTop = offset;
2161 r.fBottom = offset + height;
2162 DrawRect(draw, paint, r, textSize);
2163 }
2164 }
2165}
2166
reed@google.come0d9ce82014-04-23 04:00:17 +00002167void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2168 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002169 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170
2171 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002172 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002173 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002174 DrawTextDecorations(iter, dfp.paint(),
2175 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176 }
2177
reed@google.com4e2b3d32011-04-07 14:18:59 +00002178 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179}
2180
reed@google.come0d9ce82014-04-23 04:00:17 +00002181void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2182 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002183 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002184
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002186 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002188 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002190
reed@google.com4e2b3d32011-04-07 14:18:59 +00002191 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192}
2193
reed@google.come0d9ce82014-04-23 04:00:17 +00002194void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2195 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002196 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002197
reed@android.com8a1c16f2008-12-17 15:59:43 +00002198 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002199 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002201 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002203
reed@google.com4e2b3d32011-04-07 14:18:59 +00002204 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205}
2206
reed@google.come0d9ce82014-04-23 04:00:17 +00002207void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2208 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002209 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002210
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211 while (iter.next()) {
2212 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002213 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002215
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002216 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002217}
2218
fmalita00d5c2c2014-08-21 08:53:26 -07002219void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2220 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002221
2222 // FIXME: temporarily disable quickreject for empty bounds,
2223 // pending implicit blob bounds implementation.
2224 if (!blob->bounds().isEmpty() && paint.canComputeFastBounds()) {
2225 SkRect storage;
2226
2227 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2228 return;
2229 }
2230 }
2231
fmalitaaa1b9122014-08-28 14:32:24 -07002232 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002233
fmalitaaa1b9122014-08-28 14:32:24 -07002234 while (iter.next()) {
2235 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2236 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002237 }
2238
fmalitaaa1b9122014-08-28 14:32:24 -07002239 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002240}
2241
reed@google.come0d9ce82014-04-23 04:00:17 +00002242// These will become non-virtual, so they always call the (virtual) onDraw... method
2243void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2244 const SkPaint& paint) {
2245 this->onDrawText(text, byteLength, x, y, paint);
2246}
2247void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2248 const SkPaint& paint) {
2249 this->onDrawPosText(text, byteLength, pos, paint);
2250}
2251void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2252 SkScalar constY, const SkPaint& paint) {
2253 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2254}
2255void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2256 const SkMatrix* matrix, const SkPaint& paint) {
2257 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2258}
fmalita00d5c2c2014-08-21 08:53:26 -07002259void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2260 const SkPaint& paint) {
2261 if (NULL != blob) {
2262 this->onDrawTextBlob(blob, x, y, paint);
2263 }
2264}
reed@google.come0d9ce82014-04-23 04:00:17 +00002265
reed@android.com8a1c16f2008-12-17 15:59:43 +00002266void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2267 const SkPoint verts[], const SkPoint texs[],
2268 const SkColor colors[], SkXfermode* xmode,
2269 const uint16_t indices[], int indexCount,
2270 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002271 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002272
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273 while (iter.next()) {
2274 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002275 colors, xmode, indices, indexCount,
2276 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002277 }
reed@google.com4b226022011-01-11 18:32:13 +00002278
reed@google.com4e2b3d32011-04-07 14:18:59 +00002279 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002280}
2281
dandovb3c9d1c2014-08-12 08:34:29 -07002282void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2283 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2284 if (NULL == cubics) {
2285 return;
2286 }
mtklein6cfa73a2014-08-13 13:33:49 -07002287
dandovecfff212014-08-04 10:02:00 -07002288 // Since a patch is always within the convex hull of the control points, we discard it when its
2289 // bounding rectangle is completely outside the current clip.
2290 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002291 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002292 if (this->quickReject(bounds)) {
2293 return;
2294 }
mtklein6cfa73a2014-08-13 13:33:49 -07002295
dandovb3c9d1c2014-08-12 08:34:29 -07002296 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2297}
2298
2299void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2300 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2301
dandovecfff212014-08-04 10:02:00 -07002302 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002303
dandovecfff212014-08-04 10:02:00 -07002304 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002305 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002306 }
mtklein6cfa73a2014-08-13 13:33:49 -07002307
dandovecfff212014-08-04 10:02:00 -07002308 LOOPER_END
2309}
2310
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311//////////////////////////////////////////////////////////////////////////////
2312// These methods are NOT virtual, and therefore must call back into virtual
2313// methods, rather than actually drawing themselves.
2314//////////////////////////////////////////////////////////////////////////////
2315
2316void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002317 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002318 SkPaint paint;
2319
2320 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002321 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002322 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002323 }
2324 this->drawPaint(paint);
2325}
2326
reed@android.com845fdac2009-06-23 03:01:32 +00002327void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328 SkPaint paint;
2329
2330 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002331 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002332 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002333 }
2334 this->drawPaint(paint);
2335}
2336
2337void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2338 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002339
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 pt.set(x, y);
2341 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2342}
2343
2344void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2345 SkPoint pt;
2346 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002347
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348 pt.set(x, y);
2349 paint.setColor(color);
2350 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2351}
2352
2353void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2354 const SkPaint& paint) {
2355 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002356
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357 pts[0].set(x0, y0);
2358 pts[1].set(x1, y1);
2359 this->drawPoints(kLines_PointMode, 2, pts, paint);
2360}
2361
2362void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2363 SkScalar right, SkScalar bottom,
2364 const SkPaint& paint) {
2365 SkRect r;
2366
2367 r.set(left, top, right, bottom);
2368 this->drawRect(r, paint);
2369}
2370
2371void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2372 const SkPaint& paint) {
2373 if (radius < 0) {
2374 radius = 0;
2375 }
2376
2377 SkRect r;
2378 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002379 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002380}
2381
2382void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2383 const SkPaint& paint) {
2384 if (rx > 0 && ry > 0) {
2385 if (paint.canComputeFastBounds()) {
2386 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002387 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388 return;
2389 }
2390 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002391 SkRRect rrect;
2392 rrect.setRectXY(r, rx, ry);
2393 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394 } else {
2395 this->drawRect(r, paint);
2396 }
2397}
2398
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2400 SkScalar sweepAngle, bool useCenter,
2401 const SkPaint& paint) {
2402 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2403 this->drawOval(oval, paint);
2404 } else {
2405 SkPath path;
2406 if (useCenter) {
2407 path.moveTo(oval.centerX(), oval.centerY());
2408 }
2409 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2410 if (useCenter) {
2411 path.close();
2412 }
2413 this->drawPath(path, paint);
2414 }
2415}
2416
2417void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2418 const SkPath& path, SkScalar hOffset,
2419 SkScalar vOffset, const SkPaint& paint) {
2420 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002421
reed@android.com8a1c16f2008-12-17 15:59:43 +00002422 matrix.setTranslate(hOffset, vOffset);
2423 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2424}
2425
reed@android.comf76bacf2009-05-13 14:00:33 +00002426///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002427void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002428 SkBaseDevice* device = this->getDevice();
2429 if (NULL != device) {
2430 device->EXPERIMENTAL_optimize(picture);
2431 }
2432}
reed@android.comf76bacf2009-05-13 14:00:33 +00002433
robertphillips9b14f262014-06-04 05:40:44 -07002434void SkCanvas::drawPicture(const SkPicture* picture) {
2435 if (NULL != picture) {
reedd5fa1a42014-08-09 11:08:05 -07002436 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002437 }
2438}
2439
reedd5fa1a42014-08-09 11:08:05 -07002440void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
2441 if (NULL != picture) {
2442 if (matrix && matrix->isIdentity()) {
2443 matrix = NULL;
2444 }
2445 this->onDrawPicture(picture, matrix, paint);
2446 }
2447}
robertphillips9b14f262014-06-04 05:40:44 -07002448
reedd5fa1a42014-08-09 11:08:05 -07002449void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2450 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002451 SkBaseDevice* device = this->getTopDevice();
2452 if (NULL != device) {
2453 // Canvas has to first give the device the opportunity to render
2454 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002455 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002456 return; // the device has rendered the entire picture
2457 }
2458 }
2459
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002460 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002461
robertphillips9b14f262014-06-04 05:40:44 -07002462 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002463}
2464
reed@android.com8a1c16f2008-12-17 15:59:43 +00002465///////////////////////////////////////////////////////////////////////////////
2466///////////////////////////////////////////////////////////////////////////////
2467
2468SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002469 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002470
2471 SkASSERT(canvas);
2472
2473 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2474 fDone = !fImpl->next();
2475}
2476
2477SkCanvas::LayerIter::~LayerIter() {
2478 fImpl->~SkDrawIter();
2479}
2480
2481void SkCanvas::LayerIter::next() {
2482 fDone = !fImpl->next();
2483}
2484
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002485SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002486 return fImpl->getDevice();
2487}
2488
2489const SkMatrix& SkCanvas::LayerIter::matrix() const {
2490 return fImpl->getMatrix();
2491}
2492
2493const SkPaint& SkCanvas::LayerIter::paint() const {
2494 const SkPaint* paint = fImpl->getPaint();
2495 if (NULL == paint) {
2496 paint = &fDefaultPaint;
2497 }
2498 return *paint;
2499}
2500
2501const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2502int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2503int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002504
2505///////////////////////////////////////////////////////////////////////////////
2506
fmalitac3b589a2014-06-05 12:40:07 -07002507SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002508
2509///////////////////////////////////////////////////////////////////////////////
2510
2511static bool supported_for_raster_canvas(const SkImageInfo& info) {
2512 switch (info.alphaType()) {
2513 case kPremul_SkAlphaType:
2514 case kOpaque_SkAlphaType:
2515 break;
2516 default:
2517 return false;
2518 }
2519
2520 switch (info.colorType()) {
2521 case kAlpha_8_SkColorType:
2522 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002523 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002524 break;
2525 default:
2526 return false;
2527 }
2528
2529 return true;
2530}
2531
2532SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2533 if (!supported_for_raster_canvas(info)) {
2534 return NULL;
2535 }
2536
2537 SkBitmap bitmap;
2538 if (!bitmap.allocPixels(info)) {
2539 return NULL;
2540 }
2541
2542 // should this functionality be moved into allocPixels()?
2543 if (!bitmap.info().isOpaque()) {
2544 bitmap.eraseColor(0);
2545 }
2546 return SkNEW_ARGS(SkCanvas, (bitmap));
2547}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002548
2549SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2550 if (!supported_for_raster_canvas(info)) {
2551 return NULL;
2552 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002553
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002554 SkBitmap bitmap;
2555 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2556 return NULL;
2557 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002558 return SkNEW_ARGS(SkCanvas, (bitmap));
2559}
reedd5fa1a42014-08-09 11:08:05 -07002560
2561///////////////////////////////////////////////////////////////////////////////
2562
2563SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002564 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002565 : fCanvas(canvas)
2566 , fSaveCount(canvas->getSaveCount())
2567{
2568 if (NULL != paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002569 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002570 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002571 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002572 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002573 canvas->saveLayer(&newBounds, paint);
reedd5fa1a42014-08-09 11:08:05 -07002574 } else if (NULL != matrix) {
2575 canvas->save();
2576 }
mtklein6cfa73a2014-08-13 13:33:49 -07002577
reedd5fa1a42014-08-09 11:08:05 -07002578 if (NULL != matrix) {
2579 canvas->concat(*matrix);
2580 }
2581}
2582
2583SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2584 fCanvas->restoreToCount(fSaveCount);
2585}