blob: cdc9489afcc99765af039d9c2d0612d84f7c5dde [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) {
bsalomon49f085d2014-09-05 13:34:00 -070086 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 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() {
bsalomon49f085d2014-09-05 13:34:00 -070095 if (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) {
bsalomon49f085d2014-09-05 13:34:00 -0700168 if (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;
bsalomon49f085d2014-09-05 13:34:00 -0700772 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 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 */
bsalomon49f085d2014-09-05 13:34:00 -0700926 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 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
reed73e714e2014-09-04 09:02:23 -07001302static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1303 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.
reed73e714e2014-09-04 09:02:23 -07001312 if (rc->isRect()) {
1313 // FIXME: we should also be able to do this when rc->isBW(),
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001314 // but relaxing the test above triggers GM asserts in
1315 // SkRgnBuilder::blitH(). We need to investigate what's going on.
reed73e714e2014-09-04 09:02:23 -07001316 rc->setPath(devPath, rc->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001317 } else {
reed73e714e2014-09-04 09:02:23 -07001318 base.setRect(rc->getBounds());
reed@google.com00177082011-10-12 14:34:30 +00001319 SkRasterClip clip;
1320 clip.setPath(devPath, base, doAA);
reed73e714e2014-09-04 09:02:23 -07001321 rc->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) {
reed73e714e2014-09-04 09:02:23 -07001328 rc->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);
reed73e714e2014-09-04 09:02:23 -07001332 rc->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
reed73e714e2014-09-04 09:02:23 -07001362 rasterclip_path(&fMCRec->fRasterClip, this, 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
reed73e714e2014-09-04 09:02:23 -07001451 rasterclip_path(&fMCRec->fRasterClip, this, 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);
reed73e714e2014-09-04 09:02:23 -07001574 rasterclip_path(&tmpClip, this, 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
bsalomon49f085d2014-09-05 13:34:00 -07001650 if (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
bsalomon49f085d2014-09-05 13:34:00 -07001671 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001672 *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
reed@google.com9c135db2014-03-12 18:28:35 +00001685GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1686 SkBaseDevice* dev = this->getTopDevice();
1687 return dev ? dev->accessRenderTarget() : NULL;
1688}
1689
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001690SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001691 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001692 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001693}
1694
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001695GrContext* SkCanvas::getGrContext() {
1696#if SK_SUPPORT_GPU
1697 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001698 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001699 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001700 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001701 return renderTarget->getContext();
1702 }
1703 }
1704#endif
1705
1706 return NULL;
1707
1708}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001709
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001710void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1711 const SkPaint& paint) {
1712 if (outer.isEmpty()) {
1713 return;
1714 }
1715 if (inner.isEmpty()) {
1716 this->drawRRect(outer, paint);
1717 return;
1718 }
1719
1720 // We don't have this method (yet), but technically this is what we should
1721 // be able to assert...
1722 // SkASSERT(outer.contains(inner));
1723 //
1724 // For now at least check for containment of bounds
1725 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1726
1727 this->onDrawDRRect(outer, inner, paint);
1728}
1729
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730//////////////////////////////////////////////////////////////////////////////
1731// These are the virtual drawing methods
1732//////////////////////////////////////////////////////////////////////////////
1733
reed@google.com2a981812011-04-14 18:59:28 +00001734void SkCanvas::clear(SkColor color) {
1735 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001736 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001737 while (iter.next()) {
1738 iter.fDevice->clear(color);
1739 }
1740}
1741
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001742void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001743 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001744 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1745 }
1746}
1747
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001749 this->internalDrawPaint(paint);
1750}
1751
1752void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001753 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001754
1755 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001756 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757 }
1758
reed@google.com4e2b3d32011-04-07 14:18:59 +00001759 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001760}
1761
1762void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1763 const SkPaint& paint) {
1764 if ((long)count <= 0) {
1765 return;
1766 }
1767
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001768 SkRect r, storage;
1769 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001770 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001771 // special-case 2 points (common for drawing a single line)
1772 if (2 == count) {
1773 r.set(pts[0], pts[1]);
1774 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001775 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001776 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001777 bounds = &paint.computeFastStrokeBounds(r, &storage);
1778 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001779 return;
1780 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001781 }
reed@google.coma584aed2012-05-16 14:06:02 +00001782
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 SkASSERT(pts != NULL);
1784
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001785 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001786
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001788 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001789 }
reed@google.com4b226022011-01-11 18:32:13 +00001790
reed@google.com4e2b3d32011-04-07 14:18:59 +00001791 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792}
1793
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001794void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001795 SkRect storage;
1796 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001798 bounds = &paint.computeFastBounds(r, &storage);
1799 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800 return;
1801 }
1802 }
reed@google.com4b226022011-01-11 18:32:13 +00001803
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001804 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805
1806 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001807 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808 }
1809
reed@google.com4e2b3d32011-04-07 14:18:59 +00001810 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811}
1812
reed@google.com4ed0fb72012-12-12 20:48:18 +00001813void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001814 SkRect storage;
1815 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001816 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001817 bounds = &paint.computeFastBounds(oval, &storage);
1818 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001819 return;
1820 }
1821 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001822
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001823 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001824
1825 while (iter.next()) {
1826 iter.fDevice->drawOval(iter, oval, looper.paint());
1827 }
1828
1829 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001830}
1831
1832void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001833 SkRect storage;
1834 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001835 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001836 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1837 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001838 return;
1839 }
1840 }
1841
1842 if (rrect.isRect()) {
1843 // call the non-virtual version
1844 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001845 return;
1846 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001847 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001848 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1849 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001850 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001851
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001852 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001853
1854 while (iter.next()) {
1855 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1856 }
1857
1858 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001859}
1860
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001861void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1862 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001863 SkRect storage;
1864 const SkRect* bounds = NULL;
1865 if (paint.canComputeFastBounds()) {
1866 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1867 if (this->quickReject(*bounds)) {
1868 return;
1869 }
1870 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001871
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001872 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001873
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001874 while (iter.next()) {
1875 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1876 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001877
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001878 LOOPER_END
1879}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001880
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001881void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001882 if (!path.isFinite()) {
1883 return;
1884 }
1885
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001886 SkRect storage;
1887 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001888 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001889 const SkRect& pathBounds = path.getBounds();
1890 bounds = &paint.computeFastBounds(pathBounds, &storage);
1891 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001892 return;
1893 }
1894 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001895
1896 const SkRect& r = path.getBounds();
1897 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001898 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001899 this->internalDrawPaint(paint);
1900 }
1901 return;
1902 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001903
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001904 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905
1906 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001907 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001908 }
1909
reed@google.com4e2b3d32011-04-07 14:18:59 +00001910 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001911}
1912
1913void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1914 const SkPaint* paint) {
1915 SkDEBUGCODE(bitmap.validate();)
1916
reed@google.com3d608122011-11-21 15:16:16 +00001917 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001918 SkRect bounds = {
1919 x, y,
1920 x + SkIntToScalar(bitmap.width()),
1921 y + SkIntToScalar(bitmap.height())
1922 };
1923 if (paint) {
1924 (void)paint->computeFastBounds(bounds, &bounds);
1925 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001926 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927 return;
1928 }
1929 }
reed@google.com4b226022011-01-11 18:32:13 +00001930
reed@android.com8a1c16f2008-12-17 15:59:43 +00001931 SkMatrix matrix;
1932 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001933 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001934}
1935
reed@google.com9987ec32011-09-07 11:57:52 +00001936// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001937void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001938 const SkRect& dst, const SkPaint* paint,
1939 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001940 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001941 return;
1942 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001943
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001944 SkRect storage;
1945 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001946 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001947 if (paint) {
1948 bounds = &paint->computeFastBounds(dst, &storage);
1949 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001950 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001951 return;
1952 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953 }
reed@google.com3d608122011-11-21 15:16:16 +00001954
reed@google.com33535f32012-09-25 15:37:50 +00001955 SkLazyPaint lazy;
1956 if (NULL == paint) {
1957 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001958 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001959
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001960 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001961
reed@google.com33535f32012-09-25 15:37:50 +00001962 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001963 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001964 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001965
reed@google.com33535f32012-09-25 15:37:50 +00001966 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001967}
1968
reed@google.com71121732012-09-18 15:14:33 +00001969void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001970 const SkRect& dst, const SkPaint* paint,
1971 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001972 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001973 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001974}
1975
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1977 const SkPaint* paint) {
1978 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001979 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980}
1981
reed@google.com9987ec32011-09-07 11:57:52 +00001982void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1983 const SkIRect& center, const SkRect& dst,
1984 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001985 if (bitmap.drawsNothing()) {
1986 return;
1987 }
reed@google.com3d608122011-11-21 15:16:16 +00001988 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001989 SkRect storage;
1990 const SkRect* bounds = &dst;
1991 if (paint) {
1992 bounds = &paint->computeFastBounds(dst, &storage);
1993 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001994 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001995 return;
1996 }
1997 }
1998
reed@google.com9987ec32011-09-07 11:57:52 +00001999 const int32_t w = bitmap.width();
2000 const int32_t h = bitmap.height();
2001
2002 SkIRect c = center;
2003 // pin center to the bounds of the bitmap
2004 c.fLeft = SkMax32(0, center.fLeft);
2005 c.fTop = SkMax32(0, center.fTop);
2006 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2007 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2008
reed@google.com71121732012-09-18 15:14:33 +00002009 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002010 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002011 };
2012 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002013 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002014 };
reed@google.com9987ec32011-09-07 11:57:52 +00002015 SkScalar dstX[4] = {
2016 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2017 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2018 };
2019 SkScalar dstY[4] = {
2020 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2021 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2022 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002023
reed@google.com9987ec32011-09-07 11:57:52 +00002024 if (dstX[1] > dstX[2]) {
2025 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2026 dstX[2] = dstX[1];
2027 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002028
reed@google.com9987ec32011-09-07 11:57:52 +00002029 if (dstY[1] > dstY[2]) {
2030 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2031 dstY[2] = dstY[1];
2032 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002033
reed@google.com9987ec32011-09-07 11:57:52 +00002034 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002035 SkRect s, d;
2036
reed@google.com9987ec32011-09-07 11:57:52 +00002037 s.fTop = srcY[y];
2038 s.fBottom = srcY[y+1];
2039 d.fTop = dstY[y];
2040 d.fBottom = dstY[y+1];
2041 for (int x = 0; x < 3; x++) {
2042 s.fLeft = srcX[x];
2043 s.fRight = srcX[x+1];
2044 d.fLeft = dstX[x];
2045 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002046 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002047 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002048 }
2049 }
2050}
2051
2052void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2053 const SkRect& dst, const SkPaint* paint) {
2054 SkDEBUGCODE(bitmap.validate();)
2055
2056 // Need a device entry-point, so gpu can use a mesh
2057 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2058}
2059
reed@google.comf67e4cf2011-03-15 20:56:58 +00002060class SkDeviceFilteredPaint {
2061public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002062 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2063 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002064 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002065 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002066 newPaint->setFlags(flags.fFlags);
2067 newPaint->setHinting(flags.fHinting);
2068 fPaint = newPaint;
2069 } else {
2070 fPaint = &paint;
2071 }
2072 }
2073
reed@google.comf67e4cf2011-03-15 20:56:58 +00002074 const SkPaint& paint() const { return *fPaint; }
2075
2076private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002077 const SkPaint* fPaint;
2078 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002079};
2080
bungeman@google.com52c748b2011-08-22 21:30:43 +00002081void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2082 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002083 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002084 draw.fDevice->drawRect(draw, r, paint);
2085 } else {
2086 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002087 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002088 draw.fDevice->drawRect(draw, r, p);
2089 }
2090}
2091
2092void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2093 const char text[], size_t byteLength,
2094 SkScalar x, SkScalar y) {
2095 SkASSERT(byteLength == 0 || text != NULL);
2096
2097 // nothing to draw
2098 if (text == NULL || byteLength == 0 ||
2099 draw.fClip->isEmpty() ||
2100 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2101 return;
2102 }
2103
2104 SkScalar width = 0;
2105 SkPoint start;
2106
2107 start.set(0, 0); // to avoid warning
2108 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2109 SkPaint::kStrikeThruText_Flag)) {
2110 width = paint.measureText(text, byteLength);
2111
2112 SkScalar offsetX = 0;
2113 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2114 offsetX = SkScalarHalf(width);
2115 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2116 offsetX = width;
2117 }
2118 start.set(x - offsetX, y);
2119 }
2120
2121 if (0 == width) {
2122 return;
2123 }
2124
2125 uint32_t flags = paint.getFlags();
2126
2127 if (flags & (SkPaint::kUnderlineText_Flag |
2128 SkPaint::kStrikeThruText_Flag)) {
2129 SkScalar textSize = paint.getTextSize();
2130 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2131 SkRect r;
2132
2133 r.fLeft = start.fX;
2134 r.fRight = start.fX + width;
2135
2136 if (flags & SkPaint::kUnderlineText_Flag) {
2137 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2138 start.fY);
2139 r.fTop = offset;
2140 r.fBottom = offset + height;
2141 DrawRect(draw, paint, r, textSize);
2142 }
2143 if (flags & SkPaint::kStrikeThruText_Flag) {
2144 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2145 start.fY);
2146 r.fTop = offset;
2147 r.fBottom = offset + height;
2148 DrawRect(draw, paint, r, textSize);
2149 }
2150 }
2151}
2152
reed@google.come0d9ce82014-04-23 04:00:17 +00002153void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2154 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002155 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002156
2157 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002158 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002159 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002160 DrawTextDecorations(iter, dfp.paint(),
2161 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162 }
2163
reed@google.com4e2b3d32011-04-07 14:18:59 +00002164 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002165}
2166
reed@google.come0d9ce82014-04-23 04:00:17 +00002167void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2168 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002169 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002170
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002172 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002174 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002176
reed@google.com4e2b3d32011-04-07 14:18:59 +00002177 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178}
2179
reed@google.come0d9ce82014-04-23 04:00:17 +00002180void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2181 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002182 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002183
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002185 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002187 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002189
reed@google.com4e2b3d32011-04-07 14:18:59 +00002190 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191}
2192
reed@google.come0d9ce82014-04-23 04:00:17 +00002193void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2194 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002195 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002196
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197 while (iter.next()) {
2198 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002199 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002201
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002202 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002203}
2204
fmalita00d5c2c2014-08-21 08:53:26 -07002205void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2206 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002207
2208 // FIXME: temporarily disable quickreject for empty bounds,
2209 // pending implicit blob bounds implementation.
2210 if (!blob->bounds().isEmpty() && paint.canComputeFastBounds()) {
2211 SkRect storage;
2212
2213 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2214 return;
2215 }
2216 }
2217
fmalitaaa1b9122014-08-28 14:32:24 -07002218 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002219
fmalitaaa1b9122014-08-28 14:32:24 -07002220 while (iter.next()) {
2221 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2222 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002223 }
2224
fmalitaaa1b9122014-08-28 14:32:24 -07002225 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002226}
2227
reed@google.come0d9ce82014-04-23 04:00:17 +00002228// These will become non-virtual, so they always call the (virtual) onDraw... method
2229void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2230 const SkPaint& paint) {
2231 this->onDrawText(text, byteLength, x, y, paint);
2232}
2233void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2234 const SkPaint& paint) {
2235 this->onDrawPosText(text, byteLength, pos, paint);
2236}
2237void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2238 SkScalar constY, const SkPaint& paint) {
2239 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2240}
2241void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2242 const SkMatrix* matrix, const SkPaint& paint) {
2243 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2244}
fmalita00d5c2c2014-08-21 08:53:26 -07002245void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2246 const SkPaint& paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002247 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002248 this->onDrawTextBlob(blob, x, y, paint);
2249 }
2250}
reed@google.come0d9ce82014-04-23 04:00:17 +00002251
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2253 const SkPoint verts[], const SkPoint texs[],
2254 const SkColor colors[], SkXfermode* xmode,
2255 const uint16_t indices[], int indexCount,
2256 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002257 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002258
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259 while (iter.next()) {
2260 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002261 colors, xmode, indices, indexCount,
2262 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002263 }
reed@google.com4b226022011-01-11 18:32:13 +00002264
reed@google.com4e2b3d32011-04-07 14:18:59 +00002265 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002266}
2267
dandovb3c9d1c2014-08-12 08:34:29 -07002268void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2269 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2270 if (NULL == cubics) {
2271 return;
2272 }
mtklein6cfa73a2014-08-13 13:33:49 -07002273
dandovecfff212014-08-04 10:02:00 -07002274 // Since a patch is always within the convex hull of the control points, we discard it when its
2275 // bounding rectangle is completely outside the current clip.
2276 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002277 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002278 if (this->quickReject(bounds)) {
2279 return;
2280 }
mtklein6cfa73a2014-08-13 13:33:49 -07002281
dandovb3c9d1c2014-08-12 08:34:29 -07002282 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2283}
2284
2285void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2286 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2287
dandovecfff212014-08-04 10:02:00 -07002288 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002289
dandovecfff212014-08-04 10:02:00 -07002290 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002291 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002292 }
mtklein6cfa73a2014-08-13 13:33:49 -07002293
dandovecfff212014-08-04 10:02:00 -07002294 LOOPER_END
2295}
2296
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297//////////////////////////////////////////////////////////////////////////////
2298// These methods are NOT virtual, and therefore must call back into virtual
2299// methods, rather than actually drawing themselves.
2300//////////////////////////////////////////////////////////////////////////////
2301
2302void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002303 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002304 SkPaint paint;
2305
2306 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002307 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002308 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309 }
2310 this->drawPaint(paint);
2311}
2312
reed@android.com845fdac2009-06-23 03:01:32 +00002313void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002314 SkPaint paint;
2315
2316 paint.setColor(c);
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
2323void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2324 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002325
reed@android.com8a1c16f2008-12-17 15:59:43 +00002326 pt.set(x, y);
2327 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2328}
2329
2330void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2331 SkPoint pt;
2332 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002333
reed@android.com8a1c16f2008-12-17 15:59:43 +00002334 pt.set(x, y);
2335 paint.setColor(color);
2336 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2337}
2338
2339void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2340 const SkPaint& paint) {
2341 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002342
reed@android.com8a1c16f2008-12-17 15:59:43 +00002343 pts[0].set(x0, y0);
2344 pts[1].set(x1, y1);
2345 this->drawPoints(kLines_PointMode, 2, pts, paint);
2346}
2347
2348void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2349 SkScalar right, SkScalar bottom,
2350 const SkPaint& paint) {
2351 SkRect r;
2352
2353 r.set(left, top, right, bottom);
2354 this->drawRect(r, paint);
2355}
2356
2357void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2358 const SkPaint& paint) {
2359 if (radius < 0) {
2360 radius = 0;
2361 }
2362
2363 SkRect r;
2364 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002365 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366}
2367
2368void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2369 const SkPaint& paint) {
2370 if (rx > 0 && ry > 0) {
2371 if (paint.canComputeFastBounds()) {
2372 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002373 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002374 return;
2375 }
2376 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002377 SkRRect rrect;
2378 rrect.setRectXY(r, rx, ry);
2379 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002380 } else {
2381 this->drawRect(r, paint);
2382 }
2383}
2384
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2386 SkScalar sweepAngle, bool useCenter,
2387 const SkPaint& paint) {
2388 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2389 this->drawOval(oval, paint);
2390 } else {
2391 SkPath path;
2392 if (useCenter) {
2393 path.moveTo(oval.centerX(), oval.centerY());
2394 }
2395 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2396 if (useCenter) {
2397 path.close();
2398 }
2399 this->drawPath(path, paint);
2400 }
2401}
2402
2403void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2404 const SkPath& path, SkScalar hOffset,
2405 SkScalar vOffset, const SkPaint& paint) {
2406 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002407
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408 matrix.setTranslate(hOffset, vOffset);
2409 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2410}
2411
reed@android.comf76bacf2009-05-13 14:00:33 +00002412///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002413void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002414 SkBaseDevice* device = this->getDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002415 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002416 device->EXPERIMENTAL_optimize(picture);
2417 }
2418}
reed@android.comf76bacf2009-05-13 14:00:33 +00002419
robertphillips9b14f262014-06-04 05:40:44 -07002420void SkCanvas::drawPicture(const SkPicture* picture) {
bsalomon49f085d2014-09-05 13:34:00 -07002421 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002422 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002423 }
2424}
2425
reedd5fa1a42014-08-09 11:08:05 -07002426void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002427 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002428 if (matrix && matrix->isIdentity()) {
2429 matrix = NULL;
2430 }
2431 this->onDrawPicture(picture, matrix, paint);
2432 }
2433}
robertphillips9b14f262014-06-04 05:40:44 -07002434
reedd5fa1a42014-08-09 11:08:05 -07002435void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2436 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002437 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002438 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002439 // Canvas has to first give the device the opportunity to render
2440 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002441 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002442 return; // the device has rendered the entire picture
2443 }
2444 }
2445
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002446 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002447
robertphillipsc5ba71d2014-09-04 08:42:50 -07002448 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002449}
2450
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451///////////////////////////////////////////////////////////////////////////////
2452///////////////////////////////////////////////////////////////////////////////
2453
2454SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002455 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002456
2457 SkASSERT(canvas);
2458
2459 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2460 fDone = !fImpl->next();
2461}
2462
2463SkCanvas::LayerIter::~LayerIter() {
2464 fImpl->~SkDrawIter();
2465}
2466
2467void SkCanvas::LayerIter::next() {
2468 fDone = !fImpl->next();
2469}
2470
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002471SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002472 return fImpl->getDevice();
2473}
2474
2475const SkMatrix& SkCanvas::LayerIter::matrix() const {
2476 return fImpl->getMatrix();
2477}
2478
2479const SkPaint& SkCanvas::LayerIter::paint() const {
2480 const SkPaint* paint = fImpl->getPaint();
2481 if (NULL == paint) {
2482 paint = &fDefaultPaint;
2483 }
2484 return *paint;
2485}
2486
2487const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2488int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2489int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002490
2491///////////////////////////////////////////////////////////////////////////////
2492
fmalitac3b589a2014-06-05 12:40:07 -07002493SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002494
2495///////////////////////////////////////////////////////////////////////////////
2496
2497static bool supported_for_raster_canvas(const SkImageInfo& info) {
2498 switch (info.alphaType()) {
2499 case kPremul_SkAlphaType:
2500 case kOpaque_SkAlphaType:
2501 break;
2502 default:
2503 return false;
2504 }
2505
2506 switch (info.colorType()) {
2507 case kAlpha_8_SkColorType:
2508 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002509 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002510 break;
2511 default:
2512 return false;
2513 }
2514
2515 return true;
2516}
2517
2518SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2519 if (!supported_for_raster_canvas(info)) {
2520 return NULL;
2521 }
2522
2523 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -07002524 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002525 return NULL;
2526 }
2527
2528 // should this functionality be moved into allocPixels()?
2529 if (!bitmap.info().isOpaque()) {
2530 bitmap.eraseColor(0);
2531 }
2532 return SkNEW_ARGS(SkCanvas, (bitmap));
2533}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002534
2535SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2536 if (!supported_for_raster_canvas(info)) {
2537 return NULL;
2538 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002539
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002540 SkBitmap bitmap;
2541 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2542 return NULL;
2543 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002544 return SkNEW_ARGS(SkCanvas, (bitmap));
2545}
reedd5fa1a42014-08-09 11:08:05 -07002546
2547///////////////////////////////////////////////////////////////////////////////
2548
2549SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002550 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002551 : fCanvas(canvas)
2552 , fSaveCount(canvas->getSaveCount())
2553{
bsalomon49f085d2014-09-05 13:34:00 -07002554 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002555 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002556 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002557 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002558 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002559 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002560 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002561 canvas->save();
2562 }
mtklein6cfa73a2014-08-13 13:33:49 -07002563
bsalomon49f085d2014-09-05 13:34:00 -07002564 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002565 canvas->concat(*matrix);
2566 }
2567}
2568
2569SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2570 fCanvas->restoreToCount(fSaveCount);
2571}