blob: 240dc9ccc52c87e7fbba3e515bc40c369a7c6975 [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"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000059typedef SkTLazy<SkPaint> SkLazyPaint;
60
reed@google.com97af1a62012-08-28 12:19:02 +000061void SkCanvas::predrawNotify() {
62 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000063 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000064 }
65}
66
reed@android.com8a1c16f2008-12-17 15:59:43 +000067///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000068
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000069/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 The clip/matrix/proc are fields that reflect the top of the save/restore
71 stack. Whenever the canvas changes, it marks a dirty flag, and then before
72 these are used (assuming we're not on a layer) we rebuild these cache
73 values: they reflect the top of the save stack, but translated and clipped
74 by the device's XY offset and bitmap-bounds.
75*/
76struct DeviceCM {
77 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000078 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000079 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000081 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000082
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000083 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 : fNext(NULL) {
85 if (NULL != device) {
86 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000087 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 }
reed@google.com4b226022011-01-11 18:32:13 +000089 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000092
bungeman@google.com88edf1e2011-08-08 19:41:56 +000093 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000095 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 fDevice->unref();
97 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000098 SkDELETE(fPaint);
99 }
reed@google.com4b226022011-01-11 18:32:13 +0000100
reed@google.com045e62d2011-10-24 12:19:46 +0000101 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
102 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000103 int x = fDevice->getOrigin().x();
104 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 int width = fDevice->width();
106 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 if ((x | y) == 0) {
109 fMatrix = &totalMatrix;
110 fClip = totalClip;
111 } else {
112 fMatrixStorage = totalMatrix;
113 fMatrixStorage.postTranslate(SkIntToScalar(-x),
114 SkIntToScalar(-y));
115 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 totalClip.translate(-x, -y, &fClip);
118 }
119
reed@google.com045e62d2011-10-24 12:19:46 +0000120 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121
122 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000125 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 SkRegion::kDifference_Op);
127 }
reed@google.com4b226022011-01-11 18:32:13 +0000128
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000129 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
130
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131#ifdef SK_DEBUG
132 if (!fClip.isEmpty()) {
133 SkIRect deviceR;
134 deviceR.set(0, 0, width, height);
135 SkASSERT(deviceR.contains(fClip.getBounds()));
136 }
137#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000138 }
139
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000141 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142};
143
144/* This is the record we keep for each save/restore level in the stack.
145 Since a level optionally copies the matrix and/or stack, we have pointers
146 for these fields. If the value is copied for this level, the copy is
147 stored in the ...Storage field, and the pointer points to that. If the
148 value is not copied for this level, we ignore ...Storage, and just point
149 at the corresponding value in the previous level in the stack.
150*/
151class SkCanvas::MCRec {
152public:
reed1f836ee2014-07-07 07:49:34 -0700153 SkMatrix fMatrix;
154 SkRasterClip fRasterClip;
155 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000156
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 DeviceCM* fLayer;
158 /* If there are any layers in the stack, this points to the top-most
159 one that is at or below this level in the stack (so we know what
160 bitmap/device to draw into from this level. This value is NOT
161 reference counted, since the real owner is either our fLayer field,
162 or a previous one in a lower level.)
163 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000164 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
Florin Malita5f6102d2014-06-30 10:13:28 -0400166 MCRec(const MCRec* prev) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 if (NULL != prev) {
reed1f836ee2014-07-07 07:49:34 -0700168 fMatrix = prev->fMatrix;
169 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170
171 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000172 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173
174 fTopLayer = prev->fTopLayer;
175 } else { // no prev
reed1f836ee2014-07-07 07:49:34 -0700176 fMatrix.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 fFilter = NULL;
178 fTopLayer = NULL;
179 }
180 fLayer = NULL;
181
182 // don't bother initializing fNext
183 inc_rec();
184 }
185 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000186 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 SkDELETE(fLayer);
188 dec_rec();
189 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190};
191
192class SkDrawIter : public SkDraw {
193public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000194 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000195 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000196 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 canvas->updateDeviceCMCache();
198
reed@google.com90c07ea2012-04-13 13:50:27 +0000199 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000201 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 }
reed@google.com4b226022011-01-11 18:32:13 +0000203
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 bool next() {
205 // skip over recs with empty clips
206 if (fSkipEmptyClips) {
207 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
208 fCurrLayer = fCurrLayer->fNext;
209 }
210 }
211
reed@google.comf68c5e22012-02-24 16:38:58 +0000212 const DeviceCM* rec = fCurrLayer;
213 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214
215 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000216 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
217 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 fDevice = rec->fDevice;
219 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000221 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222
223 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 return true;
227 }
228 return false;
229 }
reed@google.com4b226022011-01-11 18:32:13 +0000230
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000231 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000232 int getX() const { return fDevice->getOrigin().x(); }
233 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 const SkMatrix& getMatrix() const { return *fMatrix; }
235 const SkRegion& getClip() const { return *fClip; }
236 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238private:
239 SkCanvas* fCanvas;
240 const DeviceCM* fCurrLayer;
241 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 SkBool8 fSkipEmptyClips;
243
244 typedef SkDraw INHERITED;
245};
246
247/////////////////////////////////////////////////////////////////////////////
248
249class AutoDrawLooper {
250public:
reed@google.com8926b162012-03-23 15:36:36 +0000251 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000252 bool skipLayerForImageFilter = false,
253 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000254 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000256 fPaint = NULL;
257 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000258 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000259 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260
reed@google.com8926b162012-03-23 15:36:36 +0000261 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
262 SkPaint tmp;
263 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000264 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
265 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000266 // we'll clear the imageFilter for the actual draws in next(), so
267 // it will only be applied during the restore().
268 fDoClearImageFilter = true;
269 }
270
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000271 if (SkDrawLooper* looper = paint.getLooper()) {
272 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
273 looper->contextSize());
274 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000275 fIsSimple = false;
276 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000277 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000278 // can we be marked as simple?
279 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000280 }
281 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000284 if (fDoClearImageFilter) {
285 fCanvas->internalRestore();
286 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000287 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000289
reed@google.com4e2b3d32011-04-07 14:18:59 +0000290 const SkPaint& paint() const {
291 SkASSERT(fPaint);
292 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000294
reed@google.com129ec222012-05-15 13:24:09 +0000295 bool next(SkDrawFilter::Type drawType) {
296 if (fDone) {
297 return false;
298 } else if (fIsSimple) {
299 fDone = true;
300 fPaint = &fOrigPaint;
301 return !fPaint->nothingToDraw();
302 } else {
303 return this->doNext(drawType);
304 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000305 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000306
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000308 SkLazyPaint fLazyPaint;
309 SkCanvas* fCanvas;
310 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000311 SkDrawFilter* fFilter;
312 const SkPaint* fPaint;
313 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000314 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000315 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000316 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000317 SkDrawLooper::Context* fLooperContext;
318 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000319
320 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321};
322
reed@google.com129ec222012-05-15 13:24:09 +0000323bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000324 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000325 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000326 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000327
328 SkPaint* paint = fLazyPaint.set(fOrigPaint);
329
330 if (fDoClearImageFilter) {
331 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000332 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000333
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000334 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000336 return false;
337 }
338 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000339 if (!fFilter->filter(paint, drawType)) {
340 fDone = true;
341 return false;
342 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000343 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000344 // no looper means we only draw once
345 fDone = true;
346 }
347 }
348 fPaint = paint;
349
350 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000351 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000352 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000353 }
354
355 // call this after any possible paint modifiers
356 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000357 fPaint = NULL;
358 return false;
359 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000360 return true;
361}
362
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363#include "SkColorPriv.h"
364
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365////////// macros to place around the internal draw calls //////////////////
366
reed@google.com8926b162012-03-23 15:36:36 +0000367#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000368 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000369 AutoDrawLooper looper(this, paint, true); \
370 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000371 SkDrawIter iter(this);
372
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000373#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000374 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000375 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000378
reed@google.com4e2b3d32011-04-07 14:18:59 +0000379#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380
381////////////////////////////////////////////////////////////////////////////
382
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000383SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@google.comc0784db2013-12-13 21:16:12 +0000384 fCachedLocalClipBounds.setEmpty();
385 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000386 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000387 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000388 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000389 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000390 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000391 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392
393 fMCRec = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400394 new (fMCRec) MCRec(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000396 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398
reed@google.com97af1a62012-08-28 12:19:02 +0000399 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000400
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000401 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402}
403
reed@google.comcde92112011-07-06 20:00:52 +0000404SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000405 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
406{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000407 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000408
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000409 this->init(NULL);
410}
411
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000412SkCanvas::SkCanvas(int width, int height)
413 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
414{
415 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000416
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000417 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000418 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000419 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
420}
421
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000422SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000423 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
424{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 inc_canvas();
426
427 this->init(device);
428}
429
430SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000431 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
432{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 inc_canvas();
434
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000435 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436}
437
438SkCanvas::~SkCanvas() {
439 // free up the contents of our deque
440 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000441 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000442
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 this->internalRestore(); // restore the last, since we're going away
444
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000445 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 dec_canvas();
448}
449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450SkDrawFilter* SkCanvas::getDrawFilter() const {
451 return fMCRec->fFilter;
452}
453
454SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
455 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
456 return filter;
457}
458
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000459SkMetaData& SkCanvas::getMetaData() {
460 // metadata users are rare, so we lazily allocate it. If that changes we
461 // can decide to just make it a field in the device (rather than a ptr)
462 if (NULL == fMetaData) {
463 fMetaData = new SkMetaData;
464 }
465 return *fMetaData;
466}
467
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468///////////////////////////////////////////////////////////////////////////////
469
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000470void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000471 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000472 if (device) {
473 device->flush();
474 }
475}
476
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000477SkISize SkCanvas::getTopLayerSize() const {
478 SkBaseDevice* d = this->getTopDevice();
479 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
480}
481
482SkIPoint SkCanvas::getTopLayerOrigin() const {
483 SkBaseDevice* d = this->getTopDevice();
484 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
485}
486
487SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000488 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000489 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
490}
491
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000492SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000494 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 SkASSERT(rec && rec->fLayer);
496 return rec->fLayer->fDevice;
497}
498
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000499SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000500 if (updateMatrixClip) {
501 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
502 }
reed@google.com9266fed2011-03-30 00:18:03 +0000503 return fMCRec->fTopLayer->fDevice;
504}
505
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000506SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000508 SkDeque::F2BIter iter(fMCStack);
509 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000511 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512
513 if (rootDevice == device) {
514 return device;
515 }
reed@google.com4b226022011-01-11 18:32:13 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000518 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 }
520 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000521 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 }
523
524 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
525 rootDevice = device;
526
527 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 /* Now we update our initial region to have the bounds of the new device,
530 and then intersect all of the clips in our stack with these bounds,
531 to ensure that we can't draw outside of the device's bounds (and trash
532 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 NOTE: this is only a partial-fix, since if the new device is larger than
535 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000536 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
538 reconstruct the correct clips, so this approximation will have to do.
539 The caller really needs to restore() back to the base if they want to
540 accurately take advantage of the new device bounds.
541 */
542
reed@google.com42aea282012-03-28 16:19:15 +0000543 SkIRect bounds;
544 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000546 } else {
547 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 }
reed@google.com42aea282012-03-28 16:19:15 +0000549 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700550 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000551 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700552 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000553 }
554
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 return device;
556}
557
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000558bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
559 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
560 return false;
561 }
562
563 bool weAllocated = false;
564 if (NULL == bitmap->pixelRef()) {
565 if (!bitmap->allocPixels()) {
566 return false;
567 }
568 weAllocated = true;
569 }
570
571 SkBitmap bm(*bitmap);
572 bm.lockPixels();
573 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
574 return true;
575 }
576
577 if (weAllocated) {
578 bitmap->setPixelRef(NULL);
579 }
580 return false;
581}
reed@google.com51df9e32010-12-23 19:29:18 +0000582
bsalomon@google.comc6980972011-11-02 19:57:21 +0000583bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000584 SkIRect r = srcRect;
585 const SkISize size = this->getBaseLayerSize();
586 if (!r.intersect(0, 0, size.width(), size.height())) {
587 bitmap->reset();
588 return false;
589 }
590
591 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
592 // bitmap will already be reset.
593 return false;
594 }
595 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
596 bitmap->reset();
597 return false;
598 }
599 return true;
600}
601
602bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
603 switch (origInfo.colorType()) {
604 case kUnknown_SkColorType:
605 case kIndex_8_SkColorType:
606 return false;
607 default:
608 break;
609 }
610 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
611 return false;
612 }
613 if (0 == origInfo.width() || 0 == origInfo.height()) {
614 return false;
615 }
616
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000617 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000618 if (!device) {
619 return false;
620 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000621
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000622 const SkISize size = this->getBaseLayerSize();
623 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
624 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000625 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000626 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000627
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000628 SkImageInfo info = origInfo;
629 // the intersect may have shrunk info's logical size
630 info.fWidth = srcR.width();
631 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000632
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000633 // if x or y are negative, then we have to adjust pixels
634 if (x > 0) {
635 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000636 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000637 if (y > 0) {
638 y = 0;
639 }
640 // here x,y are either 0 or negative
641 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000642
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000643 // The device can assert that the requested area is always contained in its bounds
644 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000645}
646
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000647bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
648 if (bitmap.getTexture()) {
649 return false;
650 }
651 SkBitmap bm(bitmap);
652 bm.lockPixels();
653 if (bm.getPixels()) {
654 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
655 }
656 return false;
657}
658
659bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
660 int x, int y) {
661 switch (origInfo.colorType()) {
662 case kUnknown_SkColorType:
663 case kIndex_8_SkColorType:
664 return false;
665 default:
666 break;
667 }
668 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
669 return false;
670 }
671
672 const SkISize size = this->getBaseLayerSize();
673 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
674 if (!target.intersect(0, 0, size.width(), size.height())) {
675 return false;
676 }
677
678 SkBaseDevice* device = this->getDevice();
679 if (!device) {
680 return false;
681 }
682
683 SkImageInfo info = origInfo;
684 // the intersect may have shrunk info's logical size
685 info.fWidth = target.width();
686 info.fHeight = target.height();
687
688 // if x or y are negative, then we have to adjust pixels
689 if (x > 0) {
690 x = 0;
691 }
692 if (y > 0) {
693 y = 0;
694 }
695 // here x,y are either 0 or negative
696 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
697
reed4af35f32014-06-27 17:47:49 -0700698 // Tell our owning surface to bump its generation ID
699 this->predrawNotify();
700
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000701 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000702 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000703}
reed@google.com51df9e32010-12-23 19:29:18 +0000704
junov@google.com4370aed2012-01-18 16:21:08 +0000705SkCanvas* SkCanvas::canvasForDrawIter() {
706 return this;
707}
708
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709//////////////////////////////////////////////////////////////////////////////
710
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711void SkCanvas::updateDeviceCMCache() {
712 if (fDeviceCMDirty) {
713 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700714 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000716
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000718 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000720 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000722 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 } while ((layer = layer->fNext) != NULL);
724 }
725 fDeviceCMDirty = false;
726 }
727}
728
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729///////////////////////////////////////////////////////////////////////////////
730
Florin Malita5f6102d2014-06-30 10:13:28 -0400731int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000733
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 MCRec* newTop = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400735 new (newTop) MCRec(fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000737
Florin Malita5f6102d2014-06-30 10:13:28 -0400738 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000739
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 return saveCount;
741}
742
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000743int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400744 this->willSave();
745 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746}
747
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000749#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000751#else
752 return true;
753#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754}
755
junov@chromium.orga907ac32012-02-24 21:54:07 +0000756bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000757 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000758 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000759 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000760 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000761 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000762 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000763
764 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700765 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000766 // Filters may grow the bounds beyond the device bounds.
767 op = SkRegion::kReplace_Op;
768 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000769 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 if (NULL != bounds) {
771 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000772
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 this->getTotalMatrix().mapRect(&r, *bounds);
774 r.roundOut(&ir);
775 // early exit if the layer's bounds are clipped out
776 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000777 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700778 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000779 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000780 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 }
782 } else { // no user bounds, so just use the clip
783 ir = clipBounds;
784 }
785
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000786 if (bounds_affects_clip(flags)) {
787 fClipStack.clipDevRect(ir, op);
788 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700789 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000790 return false;
791 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000792 }
793
794 if (intersection) {
795 *intersection = ir;
796 }
797 return true;
798}
799
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000800int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
801 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
802 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
803}
804
junov@chromium.orga907ac32012-02-24 21:54:07 +0000805int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
806 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000807 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
808 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000809}
810
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000811int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
812 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000813#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000814 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000815#endif
816
junov@chromium.orga907ac32012-02-24 21:54:07 +0000817 // do this before we create the layer. We don't call the public save() since
818 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400819 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000820
821 fDeviceCMDirty = true;
822
823 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000824 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 return count;
826 }
827
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000828 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
829 // the clipRectBounds() call above?
830 if (kNoLayer_SaveLayerStrategy == strategy) {
831 return count;
832 }
833
reed@google.comb55deeb2012-01-06 14:43:09 +0000834 // Kill the imagefilter if our device doesn't allow it
835 SkLazyPaint lazyP;
836 if (paint && paint->getImageFilter()) {
837 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000838 if (justForImageFilter) {
839 // early exit if the layer was just for the imageFilter
840 return count;
841 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000842 SkPaint* p = lazyP.set(*paint);
843 p->setImageFilter(NULL);
844 paint = p;
845 }
846 }
847
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000848 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
849 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
850 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000852 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000853 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700854 device = this->getDevice();
855 if (device) {
856 device = device->createCompatibleDevice(info);
857 }
reed@google.com76dd2772012-01-05 21:15:07 +0000858 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000859 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000860 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000861 if (NULL == device) {
862 SkDebugf("Unable to create device for layer.");
863 return count;
864 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000865
reed@google.com6f8f2922011-03-04 22:27:10 +0000866 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000867 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 device->unref();
869
870 layer->fNext = fMCRec->fTopLayer;
871 fMCRec->fLayer = layer;
872 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
873
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000874 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 return count;
876}
877
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000878int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
879 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
880}
881
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
883 SaveFlags flags) {
884 if (0xFF == alpha) {
885 return this->saveLayer(bounds, NULL, flags);
886 } else {
887 SkPaint tmpPaint;
888 tmpPaint.setAlpha(alpha);
889 return this->saveLayer(bounds, &tmpPaint, flags);
890 }
891}
892
893void SkCanvas::restore() {
894 // check for underflow
895 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000896 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 this->internalRestore();
898 }
899}
900
901void SkCanvas::internalRestore() {
902 SkASSERT(fMCStack.count() != 0);
903
904 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000905 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906
Florin Malita5f6102d2014-06-30 10:13:28 -0400907 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000908
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000909 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910 DeviceCM* layer = fMCRec->fLayer; // may be null
911 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
912 fMCRec->fLayer = NULL;
913
914 // now do the normal restore()
915 fMCRec->~MCRec(); // balanced in save()
916 fMCStack.pop_back();
917 fMCRec = (MCRec*)fMCStack.back();
918
919 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
920 since if we're being recorded, we don't want to record this (the
921 recorder will have already recorded the restore).
922 */
923 if (NULL != layer) {
924 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000925 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000926 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
927 layer->fPaint);
928 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000930
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000931 SkASSERT(fSaveLayerCount > 0);
932 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000933 }
934 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000935 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000936}
937
938int SkCanvas::getSaveCount() const {
939 return fMCStack.count();
940}
941
942void SkCanvas::restoreToCount(int count) {
943 // sanity check
944 if (count < 1) {
945 count = 1;
946 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000947
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000948 int n = this->getSaveCount() - count;
949 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 this->restore();
951 }
952}
953
reed@google.com7c202932011-12-14 18:48:05 +0000954bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000955 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000956}
957
reed@google.com76f10a32014-02-05 15:32:21 +0000958SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
959 return this->onNewSurface(info);
960}
961
962SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
963 SkBaseDevice* dev = this->getDevice();
964 return dev ? dev->newSurface(info) : NULL;
965}
966
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000967SkImageInfo SkCanvas::imageInfo() const {
968 SkBaseDevice* dev = this->getDevice();
969 if (dev) {
970 return dev->imageInfo();
971 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000972 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000973 }
974}
975
976const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
977 return this->onPeekPixels(info, rowBytes);
978}
979
980const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
981 SkBaseDevice* dev = this->getDevice();
982 return dev ? dev->peekPixels(info, rowBytes) : NULL;
983}
984
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +0000985void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
986 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
987 if (pixels && origin) {
988 *origin = this->getTopDevice(false)->getOrigin();
989 }
990 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +0000991}
992
993void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
994 SkBaseDevice* dev = this->getTopDevice();
995 return dev ? dev->accessPixels(info, rowBytes) : NULL;
996}
997
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000998SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
999 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1000 if (NULL == fAddr) {
1001 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001002 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001003 return; // failure, fAddr is NULL
1004 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001005 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1006 return; // failure, fAddr is NULL
1007 }
1008 fAddr = fBitmap.getPixels();
1009 fRowBytes = fBitmap.rowBytes();
1010 }
1011 SkASSERT(fAddr); // success
1012}
1013
1014bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1015 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001016 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001017 } else {
1018 bitmap->reset();
1019 return false;
1020 }
1021}
1022
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001023void SkCanvas::onPushCull(const SkRect& cullRect) {
1024 // do nothing. Subclasses may do something
1025}
1026
1027void SkCanvas::onPopCull() {
1028 // do nothing. Subclasses may do something
1029}
1030
reed@android.com8a1c16f2008-12-17 15:59:43 +00001031/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001032#ifdef SK_DEBUG
1033// Ensure that cull rects are monotonically nested in device space.
1034void SkCanvas::validateCull(const SkIRect& devCull) {
1035 if (fCullStack.isEmpty()
1036 || devCull.isEmpty()
1037 || fCullStack.top().contains(devCull)) {
1038 return;
1039 }
1040
1041 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1042 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1043 fCullStack.top().x(), fCullStack.top().y(),
1044 fCullStack.top().right(), fCullStack.top().bottom()));
1045
1046#ifdef ASSERT_NESTED_CULLING
1047 SkDEBUGFAIL("Invalid cull.");
1048#endif
1049}
1050#endif
1051
1052void SkCanvas::pushCull(const SkRect& cullRect) {
1053 ++fCullCount;
1054 this->onPushCull(cullRect);
1055
1056#ifdef SK_DEBUG
1057 // Map the cull rect into device space.
1058 SkRect mappedCull;
1059 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1060
1061 // Take clipping into account.
1062 SkIRect devClip, devCull;
1063 mappedCull.roundOut(&devCull);
1064 this->getClipDeviceBounds(&devClip);
1065 if (!devCull.intersect(devClip)) {
1066 devCull.setEmpty();
1067 }
1068
1069 this->validateCull(devCull);
1070 fCullStack.push(devCull); // balanced in popCull
1071#endif
1072}
1073
1074void SkCanvas::popCull() {
1075 SkASSERT(fCullStack.count() == fCullCount);
1076
1077 if (fCullCount > 0) {
1078 --fCullCount;
1079 this->onPopCull();
1080
1081 SkDEBUGCODE(fCullStack.pop());
1082 }
1083}
1084
1085/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001087void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001089 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 return;
1091 }
1092
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001093 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001095 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001097
1098 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001099
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001100 SkRect storage;
1101 const SkRect* bounds = NULL;
1102 if (paint && paint->canComputeFastBounds()) {
1103 bitmap.getBounds(&storage);
1104 matrix.mapRect(&storage);
1105 bounds = &paint->computeFastBounds(storage, &storage);
1106 }
1107
1108 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001109
1110 while (iter.next()) {
1111 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1112 }
1113
1114 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001115}
1116
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001117void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001118 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001119 SkPaint tmp;
1120 if (NULL == paint) {
1121 tmp.setDither(true);
1122 paint = &tmp;
1123 }
reed@google.com4b226022011-01-11 18:32:13 +00001124
reed@google.com8926b162012-03-23 15:36:36 +00001125 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001126 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001127 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001128 paint = &looper.paint();
1129 SkImageFilter* filter = paint->getImageFilter();
1130 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001131 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001132 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001133 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001134 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001135 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001136 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001137 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001138 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001139 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001140 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001141 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001142 SkPaint tmpUnfiltered(*paint);
1143 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001144 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1145 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001146 }
1147 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001148 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001149 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001151 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001152}
1153
reed@google.com8926b162012-03-23 15:36:36 +00001154void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1155 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001156 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001157 return;
1158 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001159 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001160
reed@google.com8926b162012-03-23 15:36:36 +00001161 SkPaint tmp;
1162 if (NULL == paint) {
1163 paint = &tmp;
1164 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001165
reed@google.com8926b162012-03-23 15:36:36 +00001166 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001167
reed@google.com8926b162012-03-23 15:36:36 +00001168 while (iter.next()) {
1169 paint = &looper.paint();
1170 SkImageFilter* filter = paint->getImageFilter();
1171 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1172 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001173 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001174 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001175 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001176 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001177 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001178 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001179 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001180 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001181 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001182 SkPaint tmpUnfiltered(*paint);
1183 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001184 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001185 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001186 }
1187 } else {
1188 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1189 }
1190 }
1191 LOOPER_END
1192}
1193
reed@android.com8a1c16f2008-12-17 15:59:43 +00001194/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001195void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001196 SkMatrix m;
1197 m.setTranslate(dx, dy);
1198 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001199}
1200
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001201void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001202 SkMatrix m;
1203 m.setScale(sx, sy);
1204 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205}
1206
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001207void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001208 SkMatrix m;
1209 m.setRotate(degrees);
1210 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211}
1212
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001213void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001214 SkMatrix m;
1215 m.setSkew(sx, sy);
1216 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001217}
1218
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001219void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001220 if (matrix.isIdentity()) {
1221 return;
1222 }
1223
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001225 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001226 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001227
1228 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001229}
1230
reed@android.com8a1c16f2008-12-17 15:59:43 +00001231void SkCanvas::setMatrix(const SkMatrix& matrix) {
1232 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001233 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001234 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001235 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236}
1237
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238void SkCanvas::resetMatrix() {
1239 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001240
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241 matrix.reset();
1242 this->setMatrix(matrix);
1243}
1244
1245//////////////////////////////////////////////////////////////////////////////
1246
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001247void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001248 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1249 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001250}
1251
1252void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001253#ifdef SK_ENABLE_CLIP_QUICKREJECT
1254 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001255 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001256 return false;
1257 }
1258
reed@google.com3b3e8952012-08-16 20:53:31 +00001259 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001260 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001261 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001262
1263 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001264 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001265 }
1266 }
1267#endif
1268
reed@google.com5c3d1472011-02-22 19:12:23 +00001269 AutoValidateClip avc(this);
1270
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001272 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001273 if (!fAllowSoftClip) {
1274 edgeStyle = kHard_ClipEdgeStyle;
1275 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276
reed1f836ee2014-07-07 07:49:34 -07001277 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001278 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001279 // the matrix. This means we don't have to a) make a path, and b) tell
1280 // the region code to scan-convert the path, only to discover that it
1281 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283
reed1f836ee2014-07-07 07:49:34 -07001284 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001285 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed1f836ee2014-07-07 07:49:34 -07001286 fMCRec->fRasterClip.op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001288 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001289 // and clip against that, since it can handle any matrix. However, to
1290 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1291 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292 SkPath path;
1293
1294 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001295 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 }
1297}
1298
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001299static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1300 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001301 // base is used to limit the size (and therefore memory allocation) of the
1302 // region that results from scan converting devPath.
1303 SkRegion base;
1304
reed@google.com819c9212011-02-23 18:56:55 +00001305 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001306 // since we are intersect, we can do better (tighter) with currRgn's
1307 // bounds, than just using the device. However, if currRgn is complex,
1308 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001309 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001310 // FIXME: we should also be able to do this when currClip->isBW(),
1311 // but relaxing the test above triggers GM asserts in
1312 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001313 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001314 } else {
reed@google.com00177082011-10-12 14:34:30 +00001315 base.setRect(currClip->getBounds());
1316 SkRasterClip clip;
1317 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001318 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001319 }
reed@google.com819c9212011-02-23 18:56:55 +00001320 } else {
reed52d9ac62014-06-30 09:05:34 -07001321 const SkISize size = canvas->getBaseLayerSize();
1322 base.setRect(0, 0, size.width(), size.height());
reed@google.com819c9212011-02-23 18:56:55 +00001323
1324 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001325 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001326 } else {
reed@google.com00177082011-10-12 14:34:30 +00001327 SkRasterClip clip;
1328 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001329 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001330 }
1331 }
1332}
1333
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001334void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001335 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001336 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001337 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1338 } else {
1339 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001340 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001341}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001342
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001343void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001344 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001345 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001346 AutoValidateClip avc(this);
1347
1348 fDeviceCMDirty = true;
1349 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001350 if (!fAllowSoftClip) {
1351 edgeStyle = kHard_ClipEdgeStyle;
1352 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001353
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001354 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001355
1356 SkPath devPath;
1357 devPath.addRRect(transformedRRect);
1358
reed1f836ee2014-07-07 07:49:34 -07001359 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001360 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001361 }
1362
1363 SkPath path;
1364 path.addRRect(rrect);
1365 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001366 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001367}
1368
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001369void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001370 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1371 SkRect r;
1372 if (!path.isInverseFillType() && path.isRect(&r)) {
1373 this->onClipRect(r, op, edgeStyle);
1374 } else {
1375 this->onClipPath(path, op, edgeStyle);
1376 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001377}
1378
1379void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001380#ifdef SK_ENABLE_CLIP_QUICKREJECT
1381 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001382 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001383 return false;
1384 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001385
reed@google.com3b3e8952012-08-16 20:53:31 +00001386 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001387 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001388 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001389
reed@google.comda17f752012-08-16 18:27:05 +00001390 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001391 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001392 }
1393 }
1394#endif
1395
reed@google.com5c3d1472011-02-22 19:12:23 +00001396 AutoValidateClip avc(this);
1397
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001399 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001400 if (!fAllowSoftClip) {
1401 edgeStyle = kHard_ClipEdgeStyle;
1402 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403
1404 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001405 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406
reed@google.comfe701122011-11-08 19:41:23 +00001407 // Check if the transfomation, or the original path itself
1408 // made us empty. Note this can also happen if we contained NaN
1409 // values. computing the bounds detects this, and will set our
1410 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1411 if (devPath.getBounds().isEmpty()) {
1412 // resetting the path will remove any NaN or other wanky values
1413 // that might upset our scan converter.
1414 devPath.reset();
1415 }
1416
reed@google.com5c3d1472011-02-22 19:12:23 +00001417 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001418 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001419
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001420 if (fAllowSimplifyClip) {
1421 devPath.reset();
1422 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1423 const SkClipStack* clipStack = getClipStack();
1424 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1425 const SkClipStack::Element* element;
1426 while ((element = iter.next())) {
1427 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001428 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001429 if (type != SkClipStack::Element::kEmpty_Type) {
1430 element->asPath(&operand);
1431 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001432 SkRegion::Op elementOp = element->getOp();
1433 if (elementOp == SkRegion::kReplace_Op) {
1434 devPath = operand;
1435 } else {
1436 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1437 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001438 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1439 // perhaps we need an API change to avoid this sort of mixed-signals about
1440 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001441 if (element->isAA()) {
1442 edgeStyle = kSoft_ClipEdgeStyle;
1443 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001444 }
1445 op = SkRegion::kReplace_Op;
1446 }
1447
reed1f836ee2014-07-07 07:49:34 -07001448 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449}
1450
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001451void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001452 bool inverseFilled) {
1453 // This is for updating the clip conservatively using only bounds
1454 // information.
1455 // Contract:
1456 // The current clip must contain the true clip. The true
1457 // clip is the clip that would have normally been computed
1458 // by calls to clipPath and clipRRect
1459 // Objective:
1460 // Keep the current clip as small as possible without
1461 // breaking the contract, using only clip bounding rectangles
1462 // (for performance).
1463
1464 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1465 // don't have to worry about getting caught in a loop. Thus anywhere
1466 // we call a virtual method, we explicitly prefix it with
1467 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001468
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001469 if (inverseFilled) {
1470 switch (op) {
1471 case SkRegion::kIntersect_Op:
1472 case SkRegion::kDifference_Op:
1473 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001474 // the clip unchanged conservatively respects the contract.
1475 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001476 case SkRegion::kUnion_Op:
1477 case SkRegion::kReplace_Op:
1478 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001479 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001480 // These ops can grow the current clip up to the extents of
1481 // the input clip, which is inverse filled, so we just set
1482 // the current clip to the device bounds.
1483 SkRect deviceBounds;
1484 SkIRect deviceIBounds;
1485 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001486 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001487
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001488 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001489 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001490 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001491 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001492 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001493 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001494 break;
1495 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001496 default:
1497 SkASSERT(0); // unhandled op?
1498 }
1499 } else {
1500 // Not inverse filled
1501 switch (op) {
1502 case SkRegion::kIntersect_Op:
1503 case SkRegion::kUnion_Op:
1504 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001505 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1506 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001507 case SkRegion::kDifference_Op:
1508 // Difference can only shrink the current clip.
1509 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001510 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001511 case SkRegion::kReverseDifference_Op:
1512 // To reverse, we swap in the bounds with a replace op.
1513 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001514 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1515 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001516 case SkRegion::kXOR_Op:
1517 // Be conservative, based on (A XOR B) always included in (A union B),
1518 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001519 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1520 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001521 default:
1522 SkASSERT(0); // unhandled op?
1523 }
1524 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001525}
1526
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001527void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001529}
1530
1531void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001532 AutoValidateClip avc(this);
1533
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001535 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536
reed@google.com5c3d1472011-02-22 19:12:23 +00001537 // todo: signal fClipStack that we have a region, and therefore (I guess)
1538 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001539 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001540
reed1f836ee2014-07-07 07:49:34 -07001541 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542}
1543
reed@google.com819c9212011-02-23 18:56:55 +00001544#ifdef SK_DEBUG
1545void SkCanvas::validateClip() const {
1546 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001547 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001548 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001549 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001550 return;
1551 }
1552
reed@google.com819c9212011-02-23 18:56:55 +00001553 SkIRect ir;
1554 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001555 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001556
robertphillips@google.com80214e22012-07-20 15:33:18 +00001557 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001558 const SkClipStack::Element* element;
1559 while ((element = iter.next()) != NULL) {
1560 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001561 case SkClipStack::Element::kRect_Type:
1562 element->getRect().round(&ir);
1563 tmpClip.op(ir, element->getOp());
1564 break;
1565 case SkClipStack::Element::kEmpty_Type:
1566 tmpClip.setEmpty();
1567 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001568 default: {
1569 SkPath path;
1570 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001571 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001572 break;
1573 }
reed@google.com819c9212011-02-23 18:56:55 +00001574 }
1575 }
reed@google.com819c9212011-02-23 18:56:55 +00001576}
1577#endif
1578
reed@google.com90c07ea2012-04-13 13:50:27 +00001579void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001580 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001581 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001582
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001583 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001584 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001585 }
1586}
1587
reed@google.com5c3d1472011-02-22 19:12:23 +00001588///////////////////////////////////////////////////////////////////////////////
1589
reed@google.com754de5f2014-02-24 19:38:20 +00001590bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001591 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001592}
1593
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001594bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001595 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001596}
1597
reed@google.com3b3e8952012-08-16 20:53:31 +00001598bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001599
reed@google.com16078632011-12-06 18:56:37 +00001600 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001601 return true;
1602
reed1f836ee2014-07-07 07:49:34 -07001603 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 return true;
1605 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606
reed1f836ee2014-07-07 07:49:34 -07001607 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001608 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001609 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001610 SkIRect idst;
1611 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001612 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001613 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001614 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001615
reed@android.coma380ae42009-07-21 01:17:02 +00001616 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001617 // TODO: should we use | instead, or compare all 4 at once?
1618 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001619 return true;
1620 }
reed@google.comc0784db2013-12-13 21:16:12 +00001621 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001622 return true;
1623 }
1624 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626}
1627
reed@google.com3b3e8952012-08-16 20:53:31 +00001628bool SkCanvas::quickReject(const SkPath& path) const {
1629 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001630}
1631
reed@google.com3b3e8952012-08-16 20:53:31 +00001632bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001633 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001634 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635 return false;
1636 }
1637
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001638 SkMatrix inverse;
1639 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001640 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001641 if (bounds) {
1642 bounds->setEmpty();
1643 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001644 return false;
1645 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001646
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001647 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001648 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001649 // adjust it outwards in case we are antialiasing
1650 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001651
reed@google.com8f4d2302013-12-17 16:44:46 +00001652 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1653 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654 inverse.mapRect(bounds, r);
1655 }
1656 return true;
1657}
1658
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001659bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001660 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001661 if (clip.isEmpty()) {
1662 if (bounds) {
1663 bounds->setEmpty();
1664 }
1665 return false;
1666 }
1667
1668 if (NULL != bounds) {
1669 *bounds = clip.getBounds();
1670 }
1671 return true;
1672}
1673
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001675 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001676}
1677
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001678#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001679SkCanvas::ClipType SkCanvas::getClipType() const {
reed1f836ee2014-07-07 07:49:34 -07001680 if (fMCRec->fRasterClip.isEmpty()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001681 return kEmpty_ClipType;
1682 }
reed1f836ee2014-07-07 07:49:34 -07001683 if (fMCRec->fRasterClip.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001684 return kRect_ClipType;
1685 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001686 return kComplex_ClipType;
1687}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001688#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001689
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001690const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001691 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001692}
1693
1694void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1695 path->reset();
1696
reed1f836ee2014-07-07 07:49:34 -07001697 const SkRegion& rgn = fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001698 if (rgn.isEmpty()) {
1699 return;
1700 }
1701 (void)rgn.getBoundaryPath(path);
1702}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001703
reed@google.com9c135db2014-03-12 18:28:35 +00001704GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1705 SkBaseDevice* dev = this->getTopDevice();
1706 return dev ? dev->accessRenderTarget() : NULL;
1707}
1708
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001709SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001710 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001711 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712}
1713
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001714GrContext* SkCanvas::getGrContext() {
1715#if SK_SUPPORT_GPU
1716 SkBaseDevice* device = this->getTopDevice();
1717 if (NULL != device) {
1718 GrRenderTarget* renderTarget = device->accessRenderTarget();
1719 if (NULL != renderTarget) {
1720 return renderTarget->getContext();
1721 }
1722 }
1723#endif
1724
1725 return NULL;
1726
1727}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001728
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001729void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1730 const SkPaint& paint) {
1731 if (outer.isEmpty()) {
1732 return;
1733 }
1734 if (inner.isEmpty()) {
1735 this->drawRRect(outer, paint);
1736 return;
1737 }
1738
1739 // We don't have this method (yet), but technically this is what we should
1740 // be able to assert...
1741 // SkASSERT(outer.contains(inner));
1742 //
1743 // For now at least check for containment of bounds
1744 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1745
1746 this->onDrawDRRect(outer, inner, paint);
1747}
1748
reed@android.com8a1c16f2008-12-17 15:59:43 +00001749//////////////////////////////////////////////////////////////////////////////
1750// These are the virtual drawing methods
1751//////////////////////////////////////////////////////////////////////////////
1752
reed@google.com2a981812011-04-14 18:59:28 +00001753void SkCanvas::clear(SkColor color) {
1754 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001755 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001756 while (iter.next()) {
1757 iter.fDevice->clear(color);
1758 }
1759}
1760
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001761void SkCanvas::onDiscard() {
1762 if (NULL != fSurfaceBase) {
1763 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1764 }
1765}
1766
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001768 this->internalDrawPaint(paint);
1769}
1770
1771void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001772 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773
1774 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001775 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776 }
1777
reed@google.com4e2b3d32011-04-07 14:18:59 +00001778 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779}
1780
1781void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1782 const SkPaint& paint) {
1783 if ((long)count <= 0) {
1784 return;
1785 }
1786
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001787 SkRect r, storage;
1788 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001789 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001790 // special-case 2 points (common for drawing a single line)
1791 if (2 == count) {
1792 r.set(pts[0], pts[1]);
1793 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001794 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001795 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001796 bounds = &paint.computeFastStrokeBounds(r, &storage);
1797 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001798 return;
1799 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001800 }
reed@google.coma584aed2012-05-16 14:06:02 +00001801
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 SkASSERT(pts != NULL);
1803
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001804 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001805
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001807 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808 }
reed@google.com4b226022011-01-11 18:32:13 +00001809
reed@google.com4e2b3d32011-04-07 14:18:59 +00001810 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001811}
1812
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001813void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001814 SkRect storage;
1815 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001816 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001817 bounds = &paint.computeFastBounds(r, &storage);
1818 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819 return;
1820 }
1821 }
reed@google.com4b226022011-01-11 18:32:13 +00001822
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001823 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824
1825 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001826 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001827 }
1828
reed@google.com4e2b3d32011-04-07 14:18:59 +00001829 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830}
1831
reed@google.com4ed0fb72012-12-12 20:48:18 +00001832void SkCanvas::drawOval(const SkRect& oval, 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(oval, &storage);
1837 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001838 return;
1839 }
1840 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001841
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001842 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001843
1844 while (iter.next()) {
1845 iter.fDevice->drawOval(iter, oval, looper.paint());
1846 }
1847
1848 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001849}
1850
1851void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001852 SkRect storage;
1853 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001854 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001855 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1856 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001857 return;
1858 }
1859 }
1860
1861 if (rrect.isRect()) {
1862 // call the non-virtual version
1863 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001864 return;
1865 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001866 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001867 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1868 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001869 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001870
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001871 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001872
1873 while (iter.next()) {
1874 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1875 }
1876
1877 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001878}
1879
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001880void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1881 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001882 SkRect storage;
1883 const SkRect* bounds = NULL;
1884 if (paint.canComputeFastBounds()) {
1885 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1886 if (this->quickReject(*bounds)) {
1887 return;
1888 }
1889 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001890
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001891 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001892
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001893 while (iter.next()) {
1894 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1895 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001896
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001897 LOOPER_END
1898}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001899
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001900void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001901 if (!path.isFinite()) {
1902 return;
1903 }
1904
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001905 SkRect storage;
1906 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001907 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001908 const SkRect& pathBounds = path.getBounds();
1909 bounds = &paint.computeFastBounds(pathBounds, &storage);
1910 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001911 return;
1912 }
1913 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001914
1915 const SkRect& r = path.getBounds();
1916 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001917 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001918 this->internalDrawPaint(paint);
1919 }
1920 return;
1921 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001922
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001923 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001924
1925 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001926 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927 }
1928
reed@google.com4e2b3d32011-04-07 14:18:59 +00001929 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001930}
1931
1932void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1933 const SkPaint* paint) {
1934 SkDEBUGCODE(bitmap.validate();)
1935
reed@google.com3d608122011-11-21 15:16:16 +00001936 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001937 SkRect bounds = {
1938 x, y,
1939 x + SkIntToScalar(bitmap.width()),
1940 y + SkIntToScalar(bitmap.height())
1941 };
1942 if (paint) {
1943 (void)paint->computeFastBounds(bounds, &bounds);
1944 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001945 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001946 return;
1947 }
1948 }
reed@google.com4b226022011-01-11 18:32:13 +00001949
reed@android.com8a1c16f2008-12-17 15:59:43 +00001950 SkMatrix matrix;
1951 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001952 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953}
1954
reed@google.com9987ec32011-09-07 11:57:52 +00001955// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001956void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001957 const SkRect& dst, const SkPaint* paint,
1958 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001959 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001960 return;
1961 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001962
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001963 SkRect storage;
1964 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001965 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001966 if (paint) {
1967 bounds = &paint->computeFastBounds(dst, &storage);
1968 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001969 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001970 return;
1971 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001972 }
reed@google.com3d608122011-11-21 15:16:16 +00001973
reed@google.com33535f32012-09-25 15:37:50 +00001974 SkLazyPaint lazy;
1975 if (NULL == paint) {
1976 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001977 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001978
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001979 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001980
reed@google.com33535f32012-09-25 15:37:50 +00001981 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001982 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001983 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001984
reed@google.com33535f32012-09-25 15:37:50 +00001985 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001986}
1987
reed@google.com71121732012-09-18 15:14:33 +00001988void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001989 const SkRect& dst, const SkPaint* paint,
1990 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001991 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001992 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001993}
1994
reed@android.com8a1c16f2008-12-17 15:59:43 +00001995void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1996 const SkPaint* paint) {
1997 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001998 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001999}
2000
reed@google.com9987ec32011-09-07 11:57:52 +00002001void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2002 const SkIRect& center, const SkRect& dst,
2003 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002004 if (bitmap.drawsNothing()) {
2005 return;
2006 }
reed@google.com3d608122011-11-21 15:16:16 +00002007 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002008 SkRect storage;
2009 const SkRect* bounds = &dst;
2010 if (paint) {
2011 bounds = &paint->computeFastBounds(dst, &storage);
2012 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002013 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002014 return;
2015 }
2016 }
2017
reed@google.com9987ec32011-09-07 11:57:52 +00002018 const int32_t w = bitmap.width();
2019 const int32_t h = bitmap.height();
2020
2021 SkIRect c = center;
2022 // pin center to the bounds of the bitmap
2023 c.fLeft = SkMax32(0, center.fLeft);
2024 c.fTop = SkMax32(0, center.fTop);
2025 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2026 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2027
reed@google.com71121732012-09-18 15:14:33 +00002028 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002029 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002030 };
2031 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002032 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002033 };
reed@google.com9987ec32011-09-07 11:57:52 +00002034 SkScalar dstX[4] = {
2035 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2036 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2037 };
2038 SkScalar dstY[4] = {
2039 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2040 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2041 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002042
reed@google.com9987ec32011-09-07 11:57:52 +00002043 if (dstX[1] > dstX[2]) {
2044 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2045 dstX[2] = dstX[1];
2046 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002047
reed@google.com9987ec32011-09-07 11:57:52 +00002048 if (dstY[1] > dstY[2]) {
2049 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2050 dstY[2] = dstY[1];
2051 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002052
reed@google.com9987ec32011-09-07 11:57:52 +00002053 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002054 SkRect s, d;
2055
reed@google.com9987ec32011-09-07 11:57:52 +00002056 s.fTop = srcY[y];
2057 s.fBottom = srcY[y+1];
2058 d.fTop = dstY[y];
2059 d.fBottom = dstY[y+1];
2060 for (int x = 0; x < 3; x++) {
2061 s.fLeft = srcX[x];
2062 s.fRight = srcX[x+1];
2063 d.fLeft = dstX[x];
2064 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002065 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002066 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002067 }
2068 }
2069}
2070
2071void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2072 const SkRect& dst, const SkPaint* paint) {
2073 SkDEBUGCODE(bitmap.validate();)
2074
2075 // Need a device entry-point, so gpu can use a mesh
2076 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2077}
2078
reed@google.comf67e4cf2011-03-15 20:56:58 +00002079class SkDeviceFilteredPaint {
2080public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002081 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2082 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002083 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002084 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002085 newPaint->setFlags(flags.fFlags);
2086 newPaint->setHinting(flags.fHinting);
2087 fPaint = newPaint;
2088 } else {
2089 fPaint = &paint;
2090 }
2091 }
2092
reed@google.comf67e4cf2011-03-15 20:56:58 +00002093 const SkPaint& paint() const { return *fPaint; }
2094
2095private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002096 const SkPaint* fPaint;
2097 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002098};
2099
bungeman@google.com52c748b2011-08-22 21:30:43 +00002100void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2101 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002102 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002103 draw.fDevice->drawRect(draw, r, paint);
2104 } else {
2105 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002106 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002107 draw.fDevice->drawRect(draw, r, p);
2108 }
2109}
2110
2111void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2112 const char text[], size_t byteLength,
2113 SkScalar x, SkScalar y) {
2114 SkASSERT(byteLength == 0 || text != NULL);
2115
2116 // nothing to draw
2117 if (text == NULL || byteLength == 0 ||
2118 draw.fClip->isEmpty() ||
2119 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2120 return;
2121 }
2122
2123 SkScalar width = 0;
2124 SkPoint start;
2125
2126 start.set(0, 0); // to avoid warning
2127 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2128 SkPaint::kStrikeThruText_Flag)) {
2129 width = paint.measureText(text, byteLength);
2130
2131 SkScalar offsetX = 0;
2132 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2133 offsetX = SkScalarHalf(width);
2134 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2135 offsetX = width;
2136 }
2137 start.set(x - offsetX, y);
2138 }
2139
2140 if (0 == width) {
2141 return;
2142 }
2143
2144 uint32_t flags = paint.getFlags();
2145
2146 if (flags & (SkPaint::kUnderlineText_Flag |
2147 SkPaint::kStrikeThruText_Flag)) {
2148 SkScalar textSize = paint.getTextSize();
2149 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2150 SkRect r;
2151
2152 r.fLeft = start.fX;
2153 r.fRight = start.fX + width;
2154
2155 if (flags & SkPaint::kUnderlineText_Flag) {
2156 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2157 start.fY);
2158 r.fTop = offset;
2159 r.fBottom = offset + height;
2160 DrawRect(draw, paint, r, textSize);
2161 }
2162 if (flags & SkPaint::kStrikeThruText_Flag) {
2163 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2164 start.fY);
2165 r.fTop = offset;
2166 r.fBottom = offset + height;
2167 DrawRect(draw, paint, r, textSize);
2168 }
2169 }
2170}
2171
reed@google.come0d9ce82014-04-23 04:00:17 +00002172void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2173 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002174 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175
2176 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002177 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002178 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002179 DrawTextDecorations(iter, dfp.paint(),
2180 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181 }
2182
reed@google.com4e2b3d32011-04-07 14:18:59 +00002183 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184}
2185
reed@google.come0d9ce82014-04-23 04:00:17 +00002186void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2187 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002188 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002189
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002191 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002193 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002195
reed@google.com4e2b3d32011-04-07 14:18:59 +00002196 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197}
2198
reed@google.come0d9ce82014-04-23 04:00:17 +00002199void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2200 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002201 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002202
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002204 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002206 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002207 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002208
reed@google.com4e2b3d32011-04-07 14:18:59 +00002209 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210}
2211
reed@google.come0d9ce82014-04-23 04:00:17 +00002212void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2213 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002214 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002215
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216 while (iter.next()) {
2217 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002218 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002220
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002221 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002222}
2223
reed@google.come0d9ce82014-04-23 04:00:17 +00002224// These will become non-virtual, so they always call the (virtual) onDraw... method
2225void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2226 const SkPaint& paint) {
2227 this->onDrawText(text, byteLength, x, y, paint);
2228}
2229void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2230 const SkPaint& paint) {
2231 this->onDrawPosText(text, byteLength, pos, paint);
2232}
2233void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2234 SkScalar constY, const SkPaint& paint) {
2235 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2236}
2237void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2238 const SkMatrix* matrix, const SkPaint& paint) {
2239 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2240}
2241
reed@android.com8a1c16f2008-12-17 15:59:43 +00002242void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2243 const SkPoint verts[], const SkPoint texs[],
2244 const SkColor colors[], SkXfermode* xmode,
2245 const uint16_t indices[], int indexCount,
2246 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002247 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002248
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249 while (iter.next()) {
2250 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002251 colors, xmode, indices, indexCount,
2252 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002253 }
reed@google.com4b226022011-01-11 18:32:13 +00002254
reed@google.com4e2b3d32011-04-07 14:18:59 +00002255 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256}
2257
dandovb3c9d1c2014-08-12 08:34:29 -07002258void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2259 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2260 if (NULL == cubics) {
2261 return;
2262 }
dandovecfff212014-08-04 10:02:00 -07002263
2264 // Since a patch is always within the convex hull of the control points, we discard it when its
2265 // bounding rectangle is completely outside the current clip.
2266 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002267 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002268 if (this->quickReject(bounds)) {
2269 return;
2270 }
2271
dandovb3c9d1c2014-08-12 08:34:29 -07002272 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2273}
2274
2275void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2276 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2277
dandovecfff212014-08-04 10:02:00 -07002278 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
2279
2280 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002281 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002282 }
2283
2284 LOOPER_END
2285}
2286
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287//////////////////////////////////////////////////////////////////////////////
2288// These methods are NOT virtual, and therefore must call back into virtual
2289// methods, rather than actually drawing themselves.
2290//////////////////////////////////////////////////////////////////////////////
2291
2292void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002293 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294 SkPaint paint;
2295
2296 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002297 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002298 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002299 }
2300 this->drawPaint(paint);
2301}
2302
reed@android.com845fdac2009-06-23 03:01:32 +00002303void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002304 SkPaint paint;
2305
2306 paint.setColor(c);
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
2313void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2314 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002315
reed@android.com8a1c16f2008-12-17 15:59:43 +00002316 pt.set(x, y);
2317 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2318}
2319
2320void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2321 SkPoint pt;
2322 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002323
reed@android.com8a1c16f2008-12-17 15:59:43 +00002324 pt.set(x, y);
2325 paint.setColor(color);
2326 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2327}
2328
2329void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2330 const SkPaint& paint) {
2331 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002332
reed@android.com8a1c16f2008-12-17 15:59:43 +00002333 pts[0].set(x0, y0);
2334 pts[1].set(x1, y1);
2335 this->drawPoints(kLines_PointMode, 2, pts, paint);
2336}
2337
2338void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2339 SkScalar right, SkScalar bottom,
2340 const SkPaint& paint) {
2341 SkRect r;
2342
2343 r.set(left, top, right, bottom);
2344 this->drawRect(r, paint);
2345}
2346
2347void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2348 const SkPaint& paint) {
2349 if (radius < 0) {
2350 radius = 0;
2351 }
2352
2353 SkRect r;
2354 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002355 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002356}
2357
2358void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2359 const SkPaint& paint) {
2360 if (rx > 0 && ry > 0) {
2361 if (paint.canComputeFastBounds()) {
2362 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002363 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002364 return;
2365 }
2366 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002367 SkRRect rrect;
2368 rrect.setRectXY(r, rx, ry);
2369 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 } else {
2371 this->drawRect(r, paint);
2372 }
2373}
2374
reed@android.com8a1c16f2008-12-17 15:59:43 +00002375void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2376 SkScalar sweepAngle, bool useCenter,
2377 const SkPaint& paint) {
2378 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2379 this->drawOval(oval, paint);
2380 } else {
2381 SkPath path;
2382 if (useCenter) {
2383 path.moveTo(oval.centerX(), oval.centerY());
2384 }
2385 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2386 if (useCenter) {
2387 path.close();
2388 }
2389 this->drawPath(path, paint);
2390 }
2391}
2392
2393void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2394 const SkPath& path, SkScalar hOffset,
2395 SkScalar vOffset, const SkPaint& paint) {
2396 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002397
reed@android.com8a1c16f2008-12-17 15:59:43 +00002398 matrix.setTranslate(hOffset, vOffset);
2399 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2400}
2401
reed@android.comf76bacf2009-05-13 14:00:33 +00002402///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002403void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002404 SkBaseDevice* device = this->getDevice();
2405 if (NULL != device) {
2406 device->EXPERIMENTAL_optimize(picture);
2407 }
2408}
reed@android.comf76bacf2009-05-13 14:00:33 +00002409
robertphillips9b14f262014-06-04 05:40:44 -07002410void SkCanvas::drawPicture(const SkPicture* picture) {
2411 if (NULL != picture) {
reedd5fa1a42014-08-09 11:08:05 -07002412 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002413 }
2414}
2415
reedd5fa1a42014-08-09 11:08:05 -07002416void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
2417 if (NULL != picture) {
2418 if (matrix && matrix->isIdentity()) {
2419 matrix = NULL;
2420 }
2421 this->onDrawPicture(picture, matrix, paint);
2422 }
2423}
robertphillips9b14f262014-06-04 05:40:44 -07002424
reedd5fa1a42014-08-09 11:08:05 -07002425void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2426 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002427 SkBaseDevice* device = this->getTopDevice();
2428 if (NULL != device) {
2429 // Canvas has to first give the device the opportunity to render
2430 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002431 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002432 return; // the device has rendered the entire picture
2433 }
2434 }
2435
reedd5fa1a42014-08-09 11:08:05 -07002436 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->width(), picture->height());
2437
robertphillips9b14f262014-06-04 05:40:44 -07002438 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002439}
2440
reed@android.com8a1c16f2008-12-17 15:59:43 +00002441///////////////////////////////////////////////////////////////////////////////
2442///////////////////////////////////////////////////////////////////////////////
2443
2444SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002445 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002446
2447 SkASSERT(canvas);
2448
2449 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2450 fDone = !fImpl->next();
2451}
2452
2453SkCanvas::LayerIter::~LayerIter() {
2454 fImpl->~SkDrawIter();
2455}
2456
2457void SkCanvas::LayerIter::next() {
2458 fDone = !fImpl->next();
2459}
2460
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002461SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462 return fImpl->getDevice();
2463}
2464
2465const SkMatrix& SkCanvas::LayerIter::matrix() const {
2466 return fImpl->getMatrix();
2467}
2468
2469const SkPaint& SkCanvas::LayerIter::paint() const {
2470 const SkPaint* paint = fImpl->getPaint();
2471 if (NULL == paint) {
2472 paint = &fDefaultPaint;
2473 }
2474 return *paint;
2475}
2476
2477const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2478int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2479int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002480
2481///////////////////////////////////////////////////////////////////////////////
2482
fmalitac3b589a2014-06-05 12:40:07 -07002483SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002484
2485///////////////////////////////////////////////////////////////////////////////
2486
2487static bool supported_for_raster_canvas(const SkImageInfo& info) {
2488 switch (info.alphaType()) {
2489 case kPremul_SkAlphaType:
2490 case kOpaque_SkAlphaType:
2491 break;
2492 default:
2493 return false;
2494 }
2495
2496 switch (info.colorType()) {
2497 case kAlpha_8_SkColorType:
2498 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002499 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002500 break;
2501 default:
2502 return false;
2503 }
2504
2505 return true;
2506}
2507
2508SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2509 if (!supported_for_raster_canvas(info)) {
2510 return NULL;
2511 }
2512
2513 SkBitmap bitmap;
2514 if (!bitmap.allocPixels(info)) {
2515 return NULL;
2516 }
2517
2518 // should this functionality be moved into allocPixels()?
2519 if (!bitmap.info().isOpaque()) {
2520 bitmap.eraseColor(0);
2521 }
2522 return SkNEW_ARGS(SkCanvas, (bitmap));
2523}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002524
2525SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2526 if (!supported_for_raster_canvas(info)) {
2527 return NULL;
2528 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002529
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002530 SkBitmap bitmap;
2531 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2532 return NULL;
2533 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002534 return SkNEW_ARGS(SkCanvas, (bitmap));
2535}
reedd5fa1a42014-08-09 11:08:05 -07002536
2537///////////////////////////////////////////////////////////////////////////////
2538
2539SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
2540 const SkPaint* paint, int width, int height)
2541 : fCanvas(canvas)
2542 , fSaveCount(canvas->getSaveCount())
2543{
2544 if (NULL != paint) {
2545 SkRect bounds = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
2546 if (matrix) {
2547 matrix->mapRect(&bounds);
2548 }
2549 canvas->saveLayer(&bounds, paint);
2550 } else if (NULL != matrix) {
2551 canvas->save();
2552 }
2553
2554 if (NULL != matrix) {
2555 canvas->concat(*matrix);
2556 }
2557}
2558
2559SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2560 fCanvas->restoreToCount(fSaveCount);
2561}