blob: 3d2fc8c498646a8c5aad8ba8a8d1eb76f2d441b6 [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()) {
reed84825042014-09-02 12:50:45 -0700571 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000572 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
reed84825042014-09-02 12:50:45 -0700597 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000598 // 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 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700635 const SkImageInfo info = origInfo.makeWH(srcR.width(), srcR.height());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000636
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000637 // if x or y are negative, then we have to adjust pixels
638 if (x > 0) {
639 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000640 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000641 if (y > 0) {
642 y = 0;
643 }
644 // here x,y are either 0 or negative
645 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000646
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000647 // The device can assert that the requested area is always contained in its bounds
648 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000649}
650
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000651bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
652 if (bitmap.getTexture()) {
653 return false;
654 }
655 SkBitmap bm(bitmap);
656 bm.lockPixels();
657 if (bm.getPixels()) {
658 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
659 }
660 return false;
661}
662
663bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
664 int x, int y) {
665 switch (origInfo.colorType()) {
666 case kUnknown_SkColorType:
667 case kIndex_8_SkColorType:
668 return false;
669 default:
670 break;
671 }
672 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
673 return false;
674 }
675
676 const SkISize size = this->getBaseLayerSize();
677 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
678 if (!target.intersect(0, 0, size.width(), size.height())) {
679 return false;
680 }
681
682 SkBaseDevice* device = this->getDevice();
683 if (!device) {
684 return false;
685 }
686
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000687 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700688 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000689
690 // if x or y are negative, then we have to adjust pixels
691 if (x > 0) {
692 x = 0;
693 }
694 if (y > 0) {
695 y = 0;
696 }
697 // here x,y are either 0 or negative
698 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
699
reed4af35f32014-06-27 17:47:49 -0700700 // Tell our owning surface to bump its generation ID
701 this->predrawNotify();
702
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000703 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000704 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000705}
reed@google.com51df9e32010-12-23 19:29:18 +0000706
junov@google.com4370aed2012-01-18 16:21:08 +0000707SkCanvas* SkCanvas::canvasForDrawIter() {
708 return this;
709}
710
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711//////////////////////////////////////////////////////////////////////////////
712
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713void SkCanvas::updateDeviceCMCache() {
714 if (fDeviceCMDirty) {
715 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700716 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000718
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000720 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000722 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000724 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000725 } while ((layer = layer->fNext) != NULL);
726 }
727 fDeviceCMDirty = false;
728 }
729}
730
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731///////////////////////////////////////////////////////////////////////////////
732
Florin Malita5f6102d2014-06-30 10:13:28 -0400733int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000735
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 MCRec* newTop = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400737 new (newTop) MCRec(fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000739
Florin Malita5f6102d2014-06-30 10:13:28 -0400740 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000741
reed@android.com8a1c16f2008-12-17 15:59:43 +0000742 return saveCount;
743}
744
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000745int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400746 this->willSave();
747 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748}
749
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000751#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000753#else
754 return true;
755#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756}
757
junov@chromium.orga907ac32012-02-24 21:54:07 +0000758bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000759 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000760 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000761 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000762 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000763 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000764 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000765
766 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700767 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000768 // Filters may grow the bounds beyond the device bounds.
769 op = SkRegion::kReplace_Op;
770 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000771 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 if (NULL != bounds) {
773 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000774
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 this->getTotalMatrix().mapRect(&r, *bounds);
776 r.roundOut(&ir);
777 // early exit if the layer's bounds are clipped out
778 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000779 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700780 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000781 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000782 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783 }
784 } else { // no user bounds, so just use the clip
785 ir = clipBounds;
786 }
787
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000788 if (bounds_affects_clip(flags)) {
789 fClipStack.clipDevRect(ir, op);
790 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700791 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000792 return false;
793 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000794 }
795
796 if (intersection) {
797 *intersection = ir;
798 }
799 return true;
800}
801
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000802int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
803 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
804 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
805}
806
junov@chromium.orga907ac32012-02-24 21:54:07 +0000807int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
808 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000809 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
810 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000811}
812
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000813int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
814 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000815#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000816 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000817#endif
818
junov@chromium.orga907ac32012-02-24 21:54:07 +0000819 // do this before we create the layer. We don't call the public save() since
820 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400821 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000822
823 fDeviceCMDirty = true;
824
825 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000826 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827 return count;
828 }
829
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000830 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
831 // the clipRectBounds() call above?
832 if (kNoLayer_SaveLayerStrategy == strategy) {
833 return count;
834 }
835
reed@google.comb55deeb2012-01-06 14:43:09 +0000836 // Kill the imagefilter if our device doesn't allow it
837 SkLazyPaint lazyP;
838 if (paint && paint->getImageFilter()) {
839 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000840 if (justForImageFilter) {
841 // early exit if the layer was just for the imageFilter
842 return count;
843 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000844 SkPaint* p = lazyP.set(*paint);
845 p->setImageFilter(NULL);
846 paint = p;
847 }
848 }
849
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000850 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
851 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
852 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000854 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000855 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700856 device = this->getDevice();
857 if (device) {
858 device = device->createCompatibleDevice(info);
859 }
reed@google.com76dd2772012-01-05 21:15:07 +0000860 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000861 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000862 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000863 if (NULL == device) {
864 SkDebugf("Unable to create device for layer.");
865 return count;
866 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000867
reed@google.com6f8f2922011-03-04 22:27:10 +0000868 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000869 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000870 device->unref();
871
872 layer->fNext = fMCRec->fTopLayer;
873 fMCRec->fLayer = layer;
874 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
875
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000876 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000877 return count;
878}
879
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000880int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
881 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
882}
883
reed@android.com8a1c16f2008-12-17 15:59:43 +0000884int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
885 SaveFlags flags) {
886 if (0xFF == alpha) {
887 return this->saveLayer(bounds, NULL, flags);
888 } else {
889 SkPaint tmpPaint;
890 tmpPaint.setAlpha(alpha);
891 return this->saveLayer(bounds, &tmpPaint, flags);
892 }
893}
894
895void SkCanvas::restore() {
896 // check for underflow
897 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000898 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700900 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000901 }
902}
903
904void SkCanvas::internalRestore() {
905 SkASSERT(fMCStack.count() != 0);
906
907 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000908 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909
Florin Malita5f6102d2014-06-30 10:13:28 -0400910 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000911
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000912 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 DeviceCM* layer = fMCRec->fLayer; // may be null
914 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
915 fMCRec->fLayer = NULL;
916
917 // now do the normal restore()
918 fMCRec->~MCRec(); // balanced in save()
919 fMCStack.pop_back();
920 fMCRec = (MCRec*)fMCStack.back();
921
922 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
923 since if we're being recorded, we don't want to record this (the
924 recorder will have already recorded the restore).
925 */
926 if (NULL != layer) {
927 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000928 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000929 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
930 layer->fPaint);
931 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000933
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000934 SkASSERT(fSaveLayerCount > 0);
935 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936 }
937 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000938 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939}
940
941int SkCanvas::getSaveCount() const {
942 return fMCStack.count();
943}
944
945void SkCanvas::restoreToCount(int count) {
946 // sanity check
947 if (count < 1) {
948 count = 1;
949 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000950
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000951 int n = this->getSaveCount() - count;
952 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000953 this->restore();
954 }
955}
956
reed@google.com7c202932011-12-14 18:48:05 +0000957bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000958 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000959}
960
reed@google.com76f10a32014-02-05 15:32:21 +0000961SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
962 return this->onNewSurface(info);
963}
964
965SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
966 SkBaseDevice* dev = this->getDevice();
967 return dev ? dev->newSurface(info) : NULL;
968}
969
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000970SkImageInfo SkCanvas::imageInfo() const {
971 SkBaseDevice* dev = this->getDevice();
972 if (dev) {
973 return dev->imageInfo();
974 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000975 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000976 }
977}
978
979const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
980 return this->onPeekPixels(info, rowBytes);
981}
982
983const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
984 SkBaseDevice* dev = this->getDevice();
985 return dev ? dev->peekPixels(info, rowBytes) : NULL;
986}
987
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +0000988void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
989 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
990 if (pixels && origin) {
991 *origin = this->getTopDevice(false)->getOrigin();
992 }
993 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +0000994}
995
996void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
997 SkBaseDevice* dev = this->getTopDevice();
998 return dev ? dev->accessPixels(info, rowBytes) : NULL;
999}
1000
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001001SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1002 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1003 if (NULL == fAddr) {
1004 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001005 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001006 return; // failure, fAddr is NULL
1007 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001008 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1009 return; // failure, fAddr is NULL
1010 }
1011 fAddr = fBitmap.getPixels();
1012 fRowBytes = fBitmap.rowBytes();
1013 }
1014 SkASSERT(fAddr); // success
1015}
1016
1017bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1018 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001019 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001020 } else {
1021 bitmap->reset();
1022 return false;
1023 }
1024}
1025
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001026void SkCanvas::onPushCull(const SkRect& cullRect) {
1027 // do nothing. Subclasses may do something
1028}
1029
1030void SkCanvas::onPopCull() {
1031 // do nothing. Subclasses may do something
1032}
1033
reed@android.com8a1c16f2008-12-17 15:59:43 +00001034/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001035#ifdef SK_DEBUG
1036// Ensure that cull rects are monotonically nested in device space.
1037void SkCanvas::validateCull(const SkIRect& devCull) {
1038 if (fCullStack.isEmpty()
1039 || devCull.isEmpty()
1040 || fCullStack.top().contains(devCull)) {
1041 return;
1042 }
1043
1044 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1045 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1046 fCullStack.top().x(), fCullStack.top().y(),
1047 fCullStack.top().right(), fCullStack.top().bottom()));
1048
1049#ifdef ASSERT_NESTED_CULLING
1050 SkDEBUGFAIL("Invalid cull.");
1051#endif
1052}
1053#endif
1054
1055void SkCanvas::pushCull(const SkRect& cullRect) {
1056 ++fCullCount;
1057 this->onPushCull(cullRect);
1058
1059#ifdef SK_DEBUG
1060 // Map the cull rect into device space.
1061 SkRect mappedCull;
1062 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1063
1064 // Take clipping into account.
1065 SkIRect devClip, devCull;
1066 mappedCull.roundOut(&devCull);
1067 this->getClipDeviceBounds(&devClip);
1068 if (!devCull.intersect(devClip)) {
1069 devCull.setEmpty();
1070 }
1071
1072 this->validateCull(devCull);
1073 fCullStack.push(devCull); // balanced in popCull
1074#endif
1075}
1076
1077void SkCanvas::popCull() {
1078 SkASSERT(fCullStack.count() == fCullCount);
1079
1080 if (fCullCount > 0) {
1081 --fCullCount;
1082 this->onPopCull();
1083
1084 SkDEBUGCODE(fCullStack.pop());
1085 }
1086}
1087
1088/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001090void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001092 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 return;
1094 }
1095
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001096 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001098 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001099 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001100
1101 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001102
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001103 SkRect storage;
1104 const SkRect* bounds = NULL;
1105 if (paint && paint->canComputeFastBounds()) {
1106 bitmap.getBounds(&storage);
1107 matrix.mapRect(&storage);
1108 bounds = &paint->computeFastBounds(storage, &storage);
1109 }
1110
1111 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001112
1113 while (iter.next()) {
1114 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1115 }
1116
1117 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118}
1119
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001120void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001121 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001122 SkPaint tmp;
1123 if (NULL == paint) {
1124 tmp.setDither(true);
1125 paint = &tmp;
1126 }
reed@google.com4b226022011-01-11 18:32:13 +00001127
reed@google.com8926b162012-03-23 15:36:36 +00001128 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001130 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001131 paint = &looper.paint();
1132 SkImageFilter* filter = paint->getImageFilter();
1133 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001134 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001135 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001136 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001137 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001138 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001139 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001140 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001141 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001142 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001143 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001144 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001145 SkPaint tmpUnfiltered(*paint);
1146 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001147 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1148 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001149 }
1150 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001151 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001152 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001154 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001155}
1156
reed@google.com8926b162012-03-23 15:36:36 +00001157void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1158 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001159 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001160 return;
1161 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001162 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001163
reed@google.com8926b162012-03-23 15:36:36 +00001164 SkPaint tmp;
1165 if (NULL == paint) {
1166 paint = &tmp;
1167 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001168
reed@google.com8926b162012-03-23 15:36:36 +00001169 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001170
reed@google.com8926b162012-03-23 15:36:36 +00001171 while (iter.next()) {
1172 paint = &looper.paint();
1173 SkImageFilter* filter = paint->getImageFilter();
1174 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1175 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001176 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001177 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001178 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001179 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001180 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001181 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001182 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001183 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001184 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001185 SkPaint tmpUnfiltered(*paint);
1186 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001187 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001188 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001189 }
1190 } else {
1191 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1192 }
1193 }
1194 LOOPER_END
1195}
1196
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001198void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001199 SkMatrix m;
1200 m.setTranslate(dx, dy);
1201 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001202}
1203
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001204void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001205 SkMatrix m;
1206 m.setScale(sx, sy);
1207 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001208}
1209
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001210void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001211 SkMatrix m;
1212 m.setRotate(degrees);
1213 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214}
1215
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001216void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001217 SkMatrix m;
1218 m.setSkew(sx, sy);
1219 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001220}
1221
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001222void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001223 if (matrix.isIdentity()) {
1224 return;
1225 }
1226
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001228 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001229 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001230
1231 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001232}
1233
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234void SkCanvas::setMatrix(const SkMatrix& matrix) {
1235 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001236 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001237 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001238 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239}
1240
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241void SkCanvas::resetMatrix() {
1242 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001243
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244 matrix.reset();
1245 this->setMatrix(matrix);
1246}
1247
1248//////////////////////////////////////////////////////////////////////////////
1249
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001250void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001251 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1252 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001253}
1254
1255void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001256#ifdef SK_ENABLE_CLIP_QUICKREJECT
1257 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001258 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001259 return false;
1260 }
1261
reed@google.com3b3e8952012-08-16 20:53:31 +00001262 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001263 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001264 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001265
1266 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001267 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001268 }
1269 }
1270#endif
1271
reed@google.com5c3d1472011-02-22 19:12:23 +00001272 AutoValidateClip avc(this);
1273
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001275 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001276 if (!fAllowSoftClip) {
1277 edgeStyle = kHard_ClipEdgeStyle;
1278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279
reed1f836ee2014-07-07 07:49:34 -07001280 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001281 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001282 // the matrix. This means we don't have to a) make a path, and b) tell
1283 // the region code to scan-convert the path, only to discover that it
1284 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286
reed1f836ee2014-07-07 07:49:34 -07001287 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001288 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed1f836ee2014-07-07 07:49:34 -07001289 fMCRec->fRasterClip.op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001291 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001292 // and clip against that, since it can handle any matrix. However, to
1293 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1294 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 SkPath path;
1296
1297 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001298 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 }
1300}
1301
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001302static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1303 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001304 // base is used to limit the size (and therefore memory allocation) of the
1305 // region that results from scan converting devPath.
1306 SkRegion base;
1307
reed@google.com819c9212011-02-23 18:56:55 +00001308 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001309 // since we are intersect, we can do better (tighter) with currRgn's
1310 // bounds, than just using the device. However, if currRgn is complex,
1311 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001312 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001313 // FIXME: we should also be able to do this when currClip->isBW(),
1314 // but relaxing the test above triggers GM asserts in
1315 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001316 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001317 } else {
reed@google.com00177082011-10-12 14:34:30 +00001318 base.setRect(currClip->getBounds());
1319 SkRasterClip clip;
1320 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001321 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001322 }
reed@google.com819c9212011-02-23 18:56:55 +00001323 } else {
reed52d9ac62014-06-30 09:05:34 -07001324 const SkISize size = canvas->getBaseLayerSize();
1325 base.setRect(0, 0, size.width(), size.height());
reed@google.com819c9212011-02-23 18:56:55 +00001326
1327 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001328 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001329 } else {
reed@google.com00177082011-10-12 14:34:30 +00001330 SkRasterClip clip;
1331 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001332 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001333 }
1334 }
1335}
1336
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001337void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001338 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001339 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001340 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1341 } else {
1342 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001343 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001344}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001345
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001346void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001347 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001348 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001349 AutoValidateClip avc(this);
1350
1351 fDeviceCMDirty = true;
1352 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001353 if (!fAllowSoftClip) {
1354 edgeStyle = kHard_ClipEdgeStyle;
1355 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001356
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001357 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001358
1359 SkPath devPath;
1360 devPath.addRRect(transformedRRect);
1361
reed1f836ee2014-07-07 07:49:34 -07001362 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001363 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001364 }
1365
1366 SkPath path;
1367 path.addRRect(rrect);
1368 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001369 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001370}
1371
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001372void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001373 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1374 SkRect r;
1375 if (!path.isInverseFillType() && path.isRect(&r)) {
1376 this->onClipRect(r, op, edgeStyle);
1377 } else {
1378 this->onClipPath(path, op, edgeStyle);
1379 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380}
1381
1382void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001383#ifdef SK_ENABLE_CLIP_QUICKREJECT
1384 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001385 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001386 return false;
1387 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001388
reed@google.com3b3e8952012-08-16 20:53:31 +00001389 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001390 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001391 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001392
reed@google.comda17f752012-08-16 18:27:05 +00001393 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001394 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001395 }
1396 }
1397#endif
1398
reed@google.com5c3d1472011-02-22 19:12:23 +00001399 AutoValidateClip avc(this);
1400
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001402 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001403 if (!fAllowSoftClip) {
1404 edgeStyle = kHard_ClipEdgeStyle;
1405 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406
1407 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001408 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409
reed@google.comfe701122011-11-08 19:41:23 +00001410 // Check if the transfomation, or the original path itself
1411 // made us empty. Note this can also happen if we contained NaN
1412 // values. computing the bounds detects this, and will set our
1413 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1414 if (devPath.getBounds().isEmpty()) {
1415 // resetting the path will remove any NaN or other wanky values
1416 // that might upset our scan converter.
1417 devPath.reset();
1418 }
1419
reed@google.com5c3d1472011-02-22 19:12:23 +00001420 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001421 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001422
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001423 if (fAllowSimplifyClip) {
1424 devPath.reset();
1425 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1426 const SkClipStack* clipStack = getClipStack();
1427 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1428 const SkClipStack::Element* element;
1429 while ((element = iter.next())) {
1430 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001431 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001432 if (type != SkClipStack::Element::kEmpty_Type) {
1433 element->asPath(&operand);
1434 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001435 SkRegion::Op elementOp = element->getOp();
1436 if (elementOp == SkRegion::kReplace_Op) {
1437 devPath = operand;
1438 } else {
1439 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1440 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001441 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1442 // perhaps we need an API change to avoid this sort of mixed-signals about
1443 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001444 if (element->isAA()) {
1445 edgeStyle = kSoft_ClipEdgeStyle;
1446 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001447 }
1448 op = SkRegion::kReplace_Op;
1449 }
1450
reed1f836ee2014-07-07 07:49:34 -07001451 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452}
1453
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001454void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001455 bool inverseFilled) {
1456 // This is for updating the clip conservatively using only bounds
1457 // information.
1458 // Contract:
1459 // The current clip must contain the true clip. The true
1460 // clip is the clip that would have normally been computed
1461 // by calls to clipPath and clipRRect
1462 // Objective:
1463 // Keep the current clip as small as possible without
1464 // breaking the contract, using only clip bounding rectangles
1465 // (for performance).
1466
1467 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1468 // don't have to worry about getting caught in a loop. Thus anywhere
1469 // we call a virtual method, we explicitly prefix it with
1470 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001471
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001472 if (inverseFilled) {
1473 switch (op) {
1474 case SkRegion::kIntersect_Op:
1475 case SkRegion::kDifference_Op:
1476 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001477 // the clip unchanged conservatively respects the contract.
1478 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001479 case SkRegion::kUnion_Op:
1480 case SkRegion::kReplace_Op:
1481 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001482 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001483 // These ops can grow the current clip up to the extents of
1484 // the input clip, which is inverse filled, so we just set
1485 // the current clip to the device bounds.
1486 SkRect deviceBounds;
1487 SkIRect deviceIBounds;
1488 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001489 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001490
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001491 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001492 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001493 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001494 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001495 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001496 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001497 break;
1498 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001499 default:
1500 SkASSERT(0); // unhandled op?
1501 }
1502 } else {
1503 // Not inverse filled
1504 switch (op) {
1505 case SkRegion::kIntersect_Op:
1506 case SkRegion::kUnion_Op:
1507 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1509 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001510 case SkRegion::kDifference_Op:
1511 // Difference can only shrink the current clip.
1512 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001513 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001514 case SkRegion::kReverseDifference_Op:
1515 // To reverse, we swap in the bounds with a replace op.
1516 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001517 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1518 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001519 case SkRegion::kXOR_Op:
1520 // Be conservative, based on (A XOR B) always included in (A union B),
1521 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001522 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1523 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001524 default:
1525 SkASSERT(0); // unhandled op?
1526 }
1527 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001528}
1529
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001530void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001531 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001532}
1533
1534void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001535 AutoValidateClip avc(this);
1536
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001538 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539
reed@google.com5c3d1472011-02-22 19:12:23 +00001540 // todo: signal fClipStack that we have a region, and therefore (I guess)
1541 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001542 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001543
reed1f836ee2014-07-07 07:49:34 -07001544 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001545}
1546
reed@google.com819c9212011-02-23 18:56:55 +00001547#ifdef SK_DEBUG
1548void SkCanvas::validateClip() const {
1549 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001550 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001551 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001552 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001553 return;
1554 }
1555
reed@google.com819c9212011-02-23 18:56:55 +00001556 SkIRect ir;
1557 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001558 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001559
robertphillips@google.com80214e22012-07-20 15:33:18 +00001560 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001561 const SkClipStack::Element* element;
1562 while ((element = iter.next()) != NULL) {
1563 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001564 case SkClipStack::Element::kRect_Type:
1565 element->getRect().round(&ir);
1566 tmpClip.op(ir, element->getOp());
1567 break;
1568 case SkClipStack::Element::kEmpty_Type:
1569 tmpClip.setEmpty();
1570 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001571 default: {
1572 SkPath path;
1573 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001574 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001575 break;
1576 }
reed@google.com819c9212011-02-23 18:56:55 +00001577 }
1578 }
reed@google.com819c9212011-02-23 18:56:55 +00001579}
1580#endif
1581
reed@google.com90c07ea2012-04-13 13:50:27 +00001582void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001583 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001584 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001585
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001586 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001587 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001588 }
1589}
1590
reed@google.com5c3d1472011-02-22 19:12:23 +00001591///////////////////////////////////////////////////////////////////////////////
1592
reed@google.com754de5f2014-02-24 19:38:20 +00001593bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001594 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001595}
1596
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001597bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001598 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001599}
1600
reed@google.com3b3e8952012-08-16 20:53:31 +00001601bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001602
reed@google.com16078632011-12-06 18:56:37 +00001603 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001604 return true;
1605
reed1f836ee2014-07-07 07:49:34 -07001606 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607 return true;
1608 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609
reed1f836ee2014-07-07 07:49:34 -07001610 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001611 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001612 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001613 SkIRect idst;
1614 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001615 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001616 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001617 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001618
reed@android.coma380ae42009-07-21 01:17:02 +00001619 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001620 // TODO: should we use | instead, or compare all 4 at once?
1621 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001622 return true;
1623 }
reed@google.comc0784db2013-12-13 21:16:12 +00001624 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001625 return true;
1626 }
1627 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629}
1630
reed@google.com3b3e8952012-08-16 20:53:31 +00001631bool SkCanvas::quickReject(const SkPath& path) const {
1632 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633}
1634
reed@google.com3b3e8952012-08-16 20:53:31 +00001635bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001636 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001638 return false;
1639 }
1640
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001641 SkMatrix inverse;
1642 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001643 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001644 if (bounds) {
1645 bounds->setEmpty();
1646 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001647 return false;
1648 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001649
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001650 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001651 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001652 // adjust it outwards in case we are antialiasing
1653 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001654
reed@google.com8f4d2302013-12-17 16:44:46 +00001655 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1656 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001657 inverse.mapRect(bounds, r);
1658 }
1659 return true;
1660}
1661
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001662bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001663 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001664 if (clip.isEmpty()) {
1665 if (bounds) {
1666 bounds->setEmpty();
1667 }
1668 return false;
1669 }
1670
1671 if (NULL != bounds) {
1672 *bounds = clip.getBounds();
1673 }
1674 return true;
1675}
1676
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001678 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001679}
1680
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001681const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001682 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001683}
1684
1685void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1686 path->reset();
1687
reed1f836ee2014-07-07 07:49:34 -07001688 const SkRegion& rgn = fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001689 if (rgn.isEmpty()) {
1690 return;
1691 }
1692 (void)rgn.getBoundaryPath(path);
1693}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001694
reed@google.com9c135db2014-03-12 18:28:35 +00001695GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1696 SkBaseDevice* dev = this->getTopDevice();
1697 return dev ? dev->accessRenderTarget() : NULL;
1698}
1699
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001700SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001701 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001702 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001703}
1704
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001705GrContext* SkCanvas::getGrContext() {
1706#if SK_SUPPORT_GPU
1707 SkBaseDevice* device = this->getTopDevice();
1708 if (NULL != device) {
1709 GrRenderTarget* renderTarget = device->accessRenderTarget();
1710 if (NULL != renderTarget) {
1711 return renderTarget->getContext();
1712 }
1713 }
1714#endif
1715
1716 return NULL;
1717
1718}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001719
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001720void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1721 const SkPaint& paint) {
1722 if (outer.isEmpty()) {
1723 return;
1724 }
1725 if (inner.isEmpty()) {
1726 this->drawRRect(outer, paint);
1727 return;
1728 }
1729
1730 // We don't have this method (yet), but technically this is what we should
1731 // be able to assert...
1732 // SkASSERT(outer.contains(inner));
1733 //
1734 // For now at least check for containment of bounds
1735 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1736
1737 this->onDrawDRRect(outer, inner, paint);
1738}
1739
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740//////////////////////////////////////////////////////////////////////////////
1741// These are the virtual drawing methods
1742//////////////////////////////////////////////////////////////////////////////
1743
reed@google.com2a981812011-04-14 18:59:28 +00001744void SkCanvas::clear(SkColor color) {
1745 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001746 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001747 while (iter.next()) {
1748 iter.fDevice->clear(color);
1749 }
1750}
1751
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001752void SkCanvas::onDiscard() {
1753 if (NULL != fSurfaceBase) {
1754 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1755 }
1756}
1757
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001759 this->internalDrawPaint(paint);
1760}
1761
1762void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001763 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764
1765 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001766 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767 }
1768
reed@google.com4e2b3d32011-04-07 14:18:59 +00001769 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001770}
1771
1772void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1773 const SkPaint& paint) {
1774 if ((long)count <= 0) {
1775 return;
1776 }
1777
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001778 SkRect r, storage;
1779 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001780 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001781 // special-case 2 points (common for drawing a single line)
1782 if (2 == count) {
1783 r.set(pts[0], pts[1]);
1784 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001785 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001786 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001787 bounds = &paint.computeFastStrokeBounds(r, &storage);
1788 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001789 return;
1790 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001791 }
reed@google.coma584aed2012-05-16 14:06:02 +00001792
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793 SkASSERT(pts != NULL);
1794
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001795 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001796
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001798 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799 }
reed@google.com4b226022011-01-11 18:32:13 +00001800
reed@google.com4e2b3d32011-04-07 14:18:59 +00001801 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802}
1803
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001804void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001805 SkRect storage;
1806 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001808 bounds = &paint.computeFastBounds(r, &storage);
1809 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810 return;
1811 }
1812 }
reed@google.com4b226022011-01-11 18:32:13 +00001813
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001814 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815
1816 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001817 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818 }
1819
reed@google.com4e2b3d32011-04-07 14:18:59 +00001820 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821}
1822
reed@google.com4ed0fb72012-12-12 20:48:18 +00001823void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001824 SkRect storage;
1825 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001826 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001827 bounds = &paint.computeFastBounds(oval, &storage);
1828 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001829 return;
1830 }
1831 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001832
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001833 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001834
1835 while (iter.next()) {
1836 iter.fDevice->drawOval(iter, oval, looper.paint());
1837 }
1838
1839 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001840}
1841
1842void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001843 SkRect storage;
1844 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001845 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001846 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1847 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001848 return;
1849 }
1850 }
1851
1852 if (rrect.isRect()) {
1853 // call the non-virtual version
1854 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001855 return;
1856 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001857 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001858 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1859 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001860 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001861
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001862 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001863
1864 while (iter.next()) {
1865 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1866 }
1867
1868 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001869}
1870
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001871void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1872 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001873 SkRect storage;
1874 const SkRect* bounds = NULL;
1875 if (paint.canComputeFastBounds()) {
1876 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1877 if (this->quickReject(*bounds)) {
1878 return;
1879 }
1880 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001881
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001882 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001883
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001884 while (iter.next()) {
1885 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1886 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001887
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001888 LOOPER_END
1889}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001890
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001891void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001892 if (!path.isFinite()) {
1893 return;
1894 }
1895
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001896 SkRect storage;
1897 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001898 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001899 const SkRect& pathBounds = path.getBounds();
1900 bounds = &paint.computeFastBounds(pathBounds, &storage);
1901 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001902 return;
1903 }
1904 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001905
1906 const SkRect& r = path.getBounds();
1907 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001908 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001909 this->internalDrawPaint(paint);
1910 }
1911 return;
1912 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001914 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001915
1916 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001917 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001918 }
1919
reed@google.com4e2b3d32011-04-07 14:18:59 +00001920 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921}
1922
1923void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1924 const SkPaint* paint) {
1925 SkDEBUGCODE(bitmap.validate();)
1926
reed@google.com3d608122011-11-21 15:16:16 +00001927 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001928 SkRect bounds = {
1929 x, y,
1930 x + SkIntToScalar(bitmap.width()),
1931 y + SkIntToScalar(bitmap.height())
1932 };
1933 if (paint) {
1934 (void)paint->computeFastBounds(bounds, &bounds);
1935 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001936 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001937 return;
1938 }
1939 }
reed@google.com4b226022011-01-11 18:32:13 +00001940
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941 SkMatrix matrix;
1942 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001943 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944}
1945
reed@google.com9987ec32011-09-07 11:57:52 +00001946// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001947void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001948 const SkRect& dst, const SkPaint* paint,
1949 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001950 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951 return;
1952 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001953
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001954 SkRect storage;
1955 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001956 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001957 if (paint) {
1958 bounds = &paint->computeFastBounds(dst, &storage);
1959 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001960 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001961 return;
1962 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001963 }
reed@google.com3d608122011-11-21 15:16:16 +00001964
reed@google.com33535f32012-09-25 15:37:50 +00001965 SkLazyPaint lazy;
1966 if (NULL == paint) {
1967 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001968 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001969
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001970 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001971
reed@google.com33535f32012-09-25 15:37:50 +00001972 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001973 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001974 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001975
reed@google.com33535f32012-09-25 15:37:50 +00001976 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977}
1978
reed@google.com71121732012-09-18 15:14:33 +00001979void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001980 const SkRect& dst, const SkPaint* paint,
1981 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001982 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001983 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001984}
1985
reed@android.com8a1c16f2008-12-17 15:59:43 +00001986void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1987 const SkPaint* paint) {
1988 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001989 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001990}
1991
reed@google.com9987ec32011-09-07 11:57:52 +00001992void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1993 const SkIRect& center, const SkRect& dst,
1994 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001995 if (bitmap.drawsNothing()) {
1996 return;
1997 }
reed@google.com3d608122011-11-21 15:16:16 +00001998 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001999 SkRect storage;
2000 const SkRect* bounds = &dst;
2001 if (paint) {
2002 bounds = &paint->computeFastBounds(dst, &storage);
2003 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002004 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002005 return;
2006 }
2007 }
2008
reed@google.com9987ec32011-09-07 11:57:52 +00002009 const int32_t w = bitmap.width();
2010 const int32_t h = bitmap.height();
2011
2012 SkIRect c = center;
2013 // pin center to the bounds of the bitmap
2014 c.fLeft = SkMax32(0, center.fLeft);
2015 c.fTop = SkMax32(0, center.fTop);
2016 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2017 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2018
reed@google.com71121732012-09-18 15:14:33 +00002019 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002020 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002021 };
2022 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002023 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002024 };
reed@google.com9987ec32011-09-07 11:57:52 +00002025 SkScalar dstX[4] = {
2026 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2027 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2028 };
2029 SkScalar dstY[4] = {
2030 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2031 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2032 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002033
reed@google.com9987ec32011-09-07 11:57:52 +00002034 if (dstX[1] > dstX[2]) {
2035 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2036 dstX[2] = dstX[1];
2037 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002038
reed@google.com9987ec32011-09-07 11:57:52 +00002039 if (dstY[1] > dstY[2]) {
2040 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2041 dstY[2] = dstY[1];
2042 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002043
reed@google.com9987ec32011-09-07 11:57:52 +00002044 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002045 SkRect s, d;
2046
reed@google.com9987ec32011-09-07 11:57:52 +00002047 s.fTop = srcY[y];
2048 s.fBottom = srcY[y+1];
2049 d.fTop = dstY[y];
2050 d.fBottom = dstY[y+1];
2051 for (int x = 0; x < 3; x++) {
2052 s.fLeft = srcX[x];
2053 s.fRight = srcX[x+1];
2054 d.fLeft = dstX[x];
2055 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002056 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002057 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002058 }
2059 }
2060}
2061
2062void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2063 const SkRect& dst, const SkPaint* paint) {
2064 SkDEBUGCODE(bitmap.validate();)
2065
2066 // Need a device entry-point, so gpu can use a mesh
2067 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2068}
2069
reed@google.comf67e4cf2011-03-15 20:56:58 +00002070class SkDeviceFilteredPaint {
2071public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002072 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2073 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002074 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002075 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002076 newPaint->setFlags(flags.fFlags);
2077 newPaint->setHinting(flags.fHinting);
2078 fPaint = newPaint;
2079 } else {
2080 fPaint = &paint;
2081 }
2082 }
2083
reed@google.comf67e4cf2011-03-15 20:56:58 +00002084 const SkPaint& paint() const { return *fPaint; }
2085
2086private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002087 const SkPaint* fPaint;
2088 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002089};
2090
bungeman@google.com52c748b2011-08-22 21:30:43 +00002091void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2092 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002093 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002094 draw.fDevice->drawRect(draw, r, paint);
2095 } else {
2096 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002097 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002098 draw.fDevice->drawRect(draw, r, p);
2099 }
2100}
2101
2102void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2103 const char text[], size_t byteLength,
2104 SkScalar x, SkScalar y) {
2105 SkASSERT(byteLength == 0 || text != NULL);
2106
2107 // nothing to draw
2108 if (text == NULL || byteLength == 0 ||
2109 draw.fClip->isEmpty() ||
2110 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2111 return;
2112 }
2113
2114 SkScalar width = 0;
2115 SkPoint start;
2116
2117 start.set(0, 0); // to avoid warning
2118 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2119 SkPaint::kStrikeThruText_Flag)) {
2120 width = paint.measureText(text, byteLength);
2121
2122 SkScalar offsetX = 0;
2123 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2124 offsetX = SkScalarHalf(width);
2125 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2126 offsetX = width;
2127 }
2128 start.set(x - offsetX, y);
2129 }
2130
2131 if (0 == width) {
2132 return;
2133 }
2134
2135 uint32_t flags = paint.getFlags();
2136
2137 if (flags & (SkPaint::kUnderlineText_Flag |
2138 SkPaint::kStrikeThruText_Flag)) {
2139 SkScalar textSize = paint.getTextSize();
2140 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2141 SkRect r;
2142
2143 r.fLeft = start.fX;
2144 r.fRight = start.fX + width;
2145
2146 if (flags & SkPaint::kUnderlineText_Flag) {
2147 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2148 start.fY);
2149 r.fTop = offset;
2150 r.fBottom = offset + height;
2151 DrawRect(draw, paint, r, textSize);
2152 }
2153 if (flags & SkPaint::kStrikeThruText_Flag) {
2154 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2155 start.fY);
2156 r.fTop = offset;
2157 r.fBottom = offset + height;
2158 DrawRect(draw, paint, r, textSize);
2159 }
2160 }
2161}
2162
reed@google.come0d9ce82014-04-23 04:00:17 +00002163void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2164 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002165 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166
2167 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002168 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002169 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002170 DrawTextDecorations(iter, dfp.paint(),
2171 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 }
2173
reed@google.com4e2b3d32011-04-07 14:18:59 +00002174 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175}
2176
reed@google.come0d9ce82014-04-23 04:00:17 +00002177void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2178 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002179 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002180
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002182 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002184 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002186
reed@google.com4e2b3d32011-04-07 14:18:59 +00002187 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188}
2189
reed@google.come0d9ce82014-04-23 04:00:17 +00002190void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2191 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002192 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002193
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002195 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002197 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002198 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002199
reed@google.com4e2b3d32011-04-07 14:18:59 +00002200 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201}
2202
reed@google.come0d9ce82014-04-23 04:00:17 +00002203void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2204 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002205 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002206
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207 while (iter.next()) {
2208 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002209 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002211
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002212 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002213}
2214
fmalita00d5c2c2014-08-21 08:53:26 -07002215void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2216 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002217
2218 // FIXME: temporarily disable quickreject for empty bounds,
2219 // pending implicit blob bounds implementation.
2220 if (!blob->bounds().isEmpty() && paint.canComputeFastBounds()) {
2221 SkRect storage;
2222
2223 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2224 return;
2225 }
2226 }
2227
fmalitaaa1b9122014-08-28 14:32:24 -07002228 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002229
fmalitaaa1b9122014-08-28 14:32:24 -07002230 while (iter.next()) {
2231 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2232 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002233 }
2234
fmalitaaa1b9122014-08-28 14:32:24 -07002235 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002236}
2237
reed@google.come0d9ce82014-04-23 04:00:17 +00002238// These will become non-virtual, so they always call the (virtual) onDraw... method
2239void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2240 const SkPaint& paint) {
2241 this->onDrawText(text, byteLength, x, y, paint);
2242}
2243void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2244 const SkPaint& paint) {
2245 this->onDrawPosText(text, byteLength, pos, paint);
2246}
2247void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2248 SkScalar constY, const SkPaint& paint) {
2249 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2250}
2251void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2252 const SkMatrix* matrix, const SkPaint& paint) {
2253 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2254}
fmalita00d5c2c2014-08-21 08:53:26 -07002255void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2256 const SkPaint& paint) {
2257 if (NULL != blob) {
2258 this->onDrawTextBlob(blob, x, y, paint);
2259 }
2260}
reed@google.come0d9ce82014-04-23 04:00:17 +00002261
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2263 const SkPoint verts[], const SkPoint texs[],
2264 const SkColor colors[], SkXfermode* xmode,
2265 const uint16_t indices[], int indexCount,
2266 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002267 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002268
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 while (iter.next()) {
2270 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002271 colors, xmode, indices, indexCount,
2272 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273 }
reed@google.com4b226022011-01-11 18:32:13 +00002274
reed@google.com4e2b3d32011-04-07 14:18:59 +00002275 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002276}
2277
dandovb3c9d1c2014-08-12 08:34:29 -07002278void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2279 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2280 if (NULL == cubics) {
2281 return;
2282 }
mtklein6cfa73a2014-08-13 13:33:49 -07002283
dandovecfff212014-08-04 10:02:00 -07002284 // Since a patch is always within the convex hull of the control points, we discard it when its
2285 // bounding rectangle is completely outside the current clip.
2286 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002287 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002288 if (this->quickReject(bounds)) {
2289 return;
2290 }
mtklein6cfa73a2014-08-13 13:33:49 -07002291
dandovb3c9d1c2014-08-12 08:34:29 -07002292 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2293}
2294
2295void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2296 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2297
dandovecfff212014-08-04 10:02:00 -07002298 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002299
dandovecfff212014-08-04 10:02:00 -07002300 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002301 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002302 }
mtklein6cfa73a2014-08-13 13:33:49 -07002303
dandovecfff212014-08-04 10:02:00 -07002304 LOOPER_END
2305}
2306
reed@android.com8a1c16f2008-12-17 15:59:43 +00002307//////////////////////////////////////////////////////////////////////////////
2308// These methods are NOT virtual, and therefore must call back into virtual
2309// methods, rather than actually drawing themselves.
2310//////////////////////////////////////////////////////////////////////////////
2311
2312void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002313 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002314 SkPaint paint;
2315
2316 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002317 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002318 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002319 }
2320 this->drawPaint(paint);
2321}
2322
reed@android.com845fdac2009-06-23 03:01:32 +00002323void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002324 SkPaint paint;
2325
2326 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002327 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002328 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329 }
2330 this->drawPaint(paint);
2331}
2332
2333void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2334 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002335
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 pt.set(x, y);
2337 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2338}
2339
2340void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2341 SkPoint pt;
2342 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002343
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344 pt.set(x, y);
2345 paint.setColor(color);
2346 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2347}
2348
2349void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2350 const SkPaint& paint) {
2351 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002352
reed@android.com8a1c16f2008-12-17 15:59:43 +00002353 pts[0].set(x0, y0);
2354 pts[1].set(x1, y1);
2355 this->drawPoints(kLines_PointMode, 2, pts, paint);
2356}
2357
2358void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2359 SkScalar right, SkScalar bottom,
2360 const SkPaint& paint) {
2361 SkRect r;
2362
2363 r.set(left, top, right, bottom);
2364 this->drawRect(r, paint);
2365}
2366
2367void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2368 const SkPaint& paint) {
2369 if (radius < 0) {
2370 radius = 0;
2371 }
2372
2373 SkRect r;
2374 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002375 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376}
2377
2378void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2379 const SkPaint& paint) {
2380 if (rx > 0 && ry > 0) {
2381 if (paint.canComputeFastBounds()) {
2382 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002383 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002384 return;
2385 }
2386 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002387 SkRRect rrect;
2388 rrect.setRectXY(r, rx, ry);
2389 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002390 } else {
2391 this->drawRect(r, paint);
2392 }
2393}
2394
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2396 SkScalar sweepAngle, bool useCenter,
2397 const SkPaint& paint) {
2398 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2399 this->drawOval(oval, paint);
2400 } else {
2401 SkPath path;
2402 if (useCenter) {
2403 path.moveTo(oval.centerX(), oval.centerY());
2404 }
2405 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2406 if (useCenter) {
2407 path.close();
2408 }
2409 this->drawPath(path, paint);
2410 }
2411}
2412
2413void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2414 const SkPath& path, SkScalar hOffset,
2415 SkScalar vOffset, const SkPaint& paint) {
2416 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002417
reed@android.com8a1c16f2008-12-17 15:59:43 +00002418 matrix.setTranslate(hOffset, vOffset);
2419 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2420}
2421
reed@android.comf76bacf2009-05-13 14:00:33 +00002422///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002423void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002424 SkBaseDevice* device = this->getDevice();
2425 if (NULL != device) {
2426 device->EXPERIMENTAL_optimize(picture);
2427 }
2428}
reed@android.comf76bacf2009-05-13 14:00:33 +00002429
robertphillips9b14f262014-06-04 05:40:44 -07002430void SkCanvas::drawPicture(const SkPicture* picture) {
2431 if (NULL != picture) {
reedd5fa1a42014-08-09 11:08:05 -07002432 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002433 }
2434}
2435
reedd5fa1a42014-08-09 11:08:05 -07002436void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
2437 if (NULL != picture) {
2438 if (matrix && matrix->isIdentity()) {
2439 matrix = NULL;
2440 }
2441 this->onDrawPicture(picture, matrix, paint);
2442 }
2443}
robertphillips9b14f262014-06-04 05:40:44 -07002444
reedd5fa1a42014-08-09 11:08:05 -07002445void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2446 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002447 SkBaseDevice* device = this->getTopDevice();
2448 if (NULL != device) {
2449 // Canvas has to first give the device the opportunity to render
2450 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002451 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002452 return; // the device has rendered the entire picture
2453 }
2454 }
2455
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002456 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002457
robertphillipsc5ba71d2014-09-04 08:42:50 -07002458 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459}
2460
reed@android.com8a1c16f2008-12-17 15:59:43 +00002461///////////////////////////////////////////////////////////////////////////////
2462///////////////////////////////////////////////////////////////////////////////
2463
2464SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002465 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002466
2467 SkASSERT(canvas);
2468
2469 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2470 fDone = !fImpl->next();
2471}
2472
2473SkCanvas::LayerIter::~LayerIter() {
2474 fImpl->~SkDrawIter();
2475}
2476
2477void SkCanvas::LayerIter::next() {
2478 fDone = !fImpl->next();
2479}
2480
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002481SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482 return fImpl->getDevice();
2483}
2484
2485const SkMatrix& SkCanvas::LayerIter::matrix() const {
2486 return fImpl->getMatrix();
2487}
2488
2489const SkPaint& SkCanvas::LayerIter::paint() const {
2490 const SkPaint* paint = fImpl->getPaint();
2491 if (NULL == paint) {
2492 paint = &fDefaultPaint;
2493 }
2494 return *paint;
2495}
2496
2497const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2498int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2499int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002500
2501///////////////////////////////////////////////////////////////////////////////
2502
fmalitac3b589a2014-06-05 12:40:07 -07002503SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002504
2505///////////////////////////////////////////////////////////////////////////////
2506
2507static bool supported_for_raster_canvas(const SkImageInfo& info) {
2508 switch (info.alphaType()) {
2509 case kPremul_SkAlphaType:
2510 case kOpaque_SkAlphaType:
2511 break;
2512 default:
2513 return false;
2514 }
2515
2516 switch (info.colorType()) {
2517 case kAlpha_8_SkColorType:
2518 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002519 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002520 break;
2521 default:
2522 return false;
2523 }
2524
2525 return true;
2526}
2527
2528SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2529 if (!supported_for_raster_canvas(info)) {
2530 return NULL;
2531 }
2532
2533 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -07002534 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002535 return NULL;
2536 }
2537
2538 // should this functionality be moved into allocPixels()?
2539 if (!bitmap.info().isOpaque()) {
2540 bitmap.eraseColor(0);
2541 }
2542 return SkNEW_ARGS(SkCanvas, (bitmap));
2543}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002544
2545SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2546 if (!supported_for_raster_canvas(info)) {
2547 return NULL;
2548 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002549
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002550 SkBitmap bitmap;
2551 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2552 return NULL;
2553 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002554 return SkNEW_ARGS(SkCanvas, (bitmap));
2555}
reedd5fa1a42014-08-09 11:08:05 -07002556
2557///////////////////////////////////////////////////////////////////////////////
2558
2559SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002560 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002561 : fCanvas(canvas)
2562 , fSaveCount(canvas->getSaveCount())
2563{
2564 if (NULL != paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002565 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002566 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002567 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002568 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002569 canvas->saveLayer(&newBounds, paint);
reedd5fa1a42014-08-09 11:08:05 -07002570 } else if (NULL != matrix) {
2571 canvas->save();
2572 }
mtklein6cfa73a2014-08-13 13:33:49 -07002573
reedd5fa1a42014-08-09 11:08:05 -07002574 if (NULL != matrix) {
2575 canvas->concat(*matrix);
2576 }
2577}
2578
2579SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2580 fCanvas->restoreToCount(fSaveCount);
2581}