blob: bab1edca0b562617d10ccdba9096964a28b640cb [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;
reedf92c8662014-08-18 08:02:43 -0700388 fDeviceCMDirty = true;
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
reedf92c8662014-08-18 08:02:43 -0700401 if (device) {
402 device->onAttachToCanvas(this);
403 fMCRec->fLayer->fDevice = SkRef(device);
404 fMCRec->fRasterClip.setRect(SkIRect::MakeWH(device->width(), device->height()));
405 }
406 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000407}
408
reed@google.comcde92112011-07-06 20:00:52 +0000409SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000410 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
411{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000412 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000413
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000414 this->init(NULL);
415}
416
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000417SkCanvas::SkCanvas(int width, int height)
418 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
419{
420 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000421
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000422 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000423 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000424 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
425}
426
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000427SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000428 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
429{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000430 inc_canvas();
431
432 this->init(device);
433}
434
435SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000436 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
437{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438 inc_canvas();
439
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000440 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441}
442
443SkCanvas::~SkCanvas() {
444 // free up the contents of our deque
445 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000446 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448 this->internalRestore(); // restore the last, since we're going away
449
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000450 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000451
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 dec_canvas();
453}
454
reed@android.com8a1c16f2008-12-17 15:59:43 +0000455SkDrawFilter* SkCanvas::getDrawFilter() const {
456 return fMCRec->fFilter;
457}
458
459SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
460 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
461 return filter;
462}
463
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000464SkMetaData& SkCanvas::getMetaData() {
465 // metadata users are rare, so we lazily allocate it. If that changes we
466 // can decide to just make it a field in the device (rather than a ptr)
467 if (NULL == fMetaData) {
468 fMetaData = new SkMetaData;
469 }
470 return *fMetaData;
471}
472
reed@android.com8a1c16f2008-12-17 15:59:43 +0000473///////////////////////////////////////////////////////////////////////////////
474
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000475void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000476 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000477 if (device) {
478 device->flush();
479 }
480}
481
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000482SkISize SkCanvas::getTopLayerSize() const {
483 SkBaseDevice* d = this->getTopDevice();
484 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
485}
486
487SkIPoint SkCanvas::getTopLayerOrigin() const {
488 SkBaseDevice* d = this->getTopDevice();
489 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
490}
491
492SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000493 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000494 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
495}
496
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000497SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000499 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500 SkASSERT(rec && rec->fLayer);
501 return rec->fLayer->fDevice;
502}
503
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000504SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000505 if (updateMatrixClip) {
506 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
507 }
reed@google.com9266fed2011-03-30 00:18:03 +0000508 return fMCRec->fTopLayer->fDevice;
509}
510
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000511SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000513 SkDeque::F2BIter iter(fMCStack);
514 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000516 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517
518 if (rootDevice == device) {
519 return device;
520 }
reed@google.com4b226022011-01-11 18:32:13 +0000521
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000523 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 }
525 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000526 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 }
528
529 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
530 rootDevice = device;
531
532 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 /* Now we update our initial region to have the bounds of the new device,
535 and then intersect all of the clips in our stack with these bounds,
536 to ensure that we can't draw outside of the device's bounds (and trash
537 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000538
reed@android.com8a1c16f2008-12-17 15:59:43 +0000539 NOTE: this is only a partial-fix, since if the new device is larger than
540 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000541 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000542 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
543 reconstruct the correct clips, so this approximation will have to do.
544 The caller really needs to restore() back to the base if they want to
545 accurately take advantage of the new device bounds.
546 */
547
reed@google.com42aea282012-03-28 16:19:15 +0000548 SkIRect bounds;
549 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000551 } else {
552 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 }
reed@google.com42aea282012-03-28 16:19:15 +0000554 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700555 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000556 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700557 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000558 }
559
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 return device;
561}
562
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000563bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
564 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
565 return false;
566 }
567
568 bool weAllocated = false;
569 if (NULL == bitmap->pixelRef()) {
570 if (!bitmap->allocPixels()) {
571 return false;
572 }
573 weAllocated = true;
574 }
575
576 SkBitmap bm(*bitmap);
577 bm.lockPixels();
578 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
579 return true;
580 }
581
582 if (weAllocated) {
583 bitmap->setPixelRef(NULL);
584 }
585 return false;
586}
reed@google.com51df9e32010-12-23 19:29:18 +0000587
bsalomon@google.comc6980972011-11-02 19:57:21 +0000588bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000589 SkIRect r = srcRect;
590 const SkISize size = this->getBaseLayerSize();
591 if (!r.intersect(0, 0, size.width(), size.height())) {
592 bitmap->reset();
593 return false;
594 }
595
596 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
597 // bitmap will already be reset.
598 return false;
599 }
600 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
601 bitmap->reset();
602 return false;
603 }
604 return true;
605}
606
607bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
608 switch (origInfo.colorType()) {
609 case kUnknown_SkColorType:
610 case kIndex_8_SkColorType:
611 return false;
612 default:
613 break;
614 }
615 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
616 return false;
617 }
618 if (0 == origInfo.width() || 0 == origInfo.height()) {
619 return false;
620 }
621
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000622 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000623 if (!device) {
624 return false;
625 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000626
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000627 const SkISize size = this->getBaseLayerSize();
628 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
629 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000630 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000631 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000632
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000633 SkImageInfo info = origInfo;
634 // the intersect may have shrunk info's logical size
635 info.fWidth = srcR.width();
636 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000637
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000638 // if x or y are negative, then we have to adjust pixels
639 if (x > 0) {
640 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000641 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000642 if (y > 0) {
643 y = 0;
644 }
645 // here x,y are either 0 or negative
646 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000647
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000648 // The device can assert that the requested area is always contained in its bounds
649 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000650}
651
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000652bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
653 if (bitmap.getTexture()) {
654 return false;
655 }
656 SkBitmap bm(bitmap);
657 bm.lockPixels();
658 if (bm.getPixels()) {
659 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
660 }
661 return false;
662}
663
664bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
665 int x, int y) {
666 switch (origInfo.colorType()) {
667 case kUnknown_SkColorType:
668 case kIndex_8_SkColorType:
669 return false;
670 default:
671 break;
672 }
673 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
674 return false;
675 }
676
677 const SkISize size = this->getBaseLayerSize();
678 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
679 if (!target.intersect(0, 0, size.width(), size.height())) {
680 return false;
681 }
682
683 SkBaseDevice* device = this->getDevice();
684 if (!device) {
685 return false;
686 }
687
688 SkImageInfo info = origInfo;
689 // the intersect may have shrunk info's logical size
690 info.fWidth = target.width();
691 info.fHeight = target.height();
692
693 // if x or y are negative, then we have to adjust pixels
694 if (x > 0) {
695 x = 0;
696 }
697 if (y > 0) {
698 y = 0;
699 }
700 // here x,y are either 0 or negative
701 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
702
reed4af35f32014-06-27 17:47:49 -0700703 // Tell our owning surface to bump its generation ID
704 this->predrawNotify();
705
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000706 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000707 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000708}
reed@google.com51df9e32010-12-23 19:29:18 +0000709
junov@google.com4370aed2012-01-18 16:21:08 +0000710SkCanvas* SkCanvas::canvasForDrawIter() {
711 return this;
712}
713
reed@android.com8a1c16f2008-12-17 15:59:43 +0000714//////////////////////////////////////////////////////////////////////////////
715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716void SkCanvas::updateDeviceCMCache() {
717 if (fDeviceCMDirty) {
718 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700719 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000723 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000724 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000725 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000727 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728 } while ((layer = layer->fNext) != NULL);
729 }
730 fDeviceCMDirty = false;
731 }
732}
733
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734///////////////////////////////////////////////////////////////////////////////
735
Florin Malita5f6102d2014-06-30 10:13:28 -0400736int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000737 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000738
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 MCRec* newTop = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400740 new (newTop) MCRec(fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000741 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000742
Florin Malita5f6102d2014-06-30 10:13:28 -0400743 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000744
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 return saveCount;
746}
747
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000748int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400749 this->willSave();
750 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751}
752
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000754#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000755 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000756#else
757 return true;
758#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000759}
760
junov@chromium.orga907ac32012-02-24 21:54:07 +0000761bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000762 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000763 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000764 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000765 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000766 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000767 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000768
769 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700770 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000771 // Filters may grow the bounds beyond the device bounds.
772 op = SkRegion::kReplace_Op;
773 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000774 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 if (NULL != bounds) {
776 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000777
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778 this->getTotalMatrix().mapRect(&r, *bounds);
779 r.roundOut(&ir);
780 // early exit if the layer's bounds are clipped out
781 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000782 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700783 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000784 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000785 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 }
787 } else { // no user bounds, so just use the clip
788 ir = clipBounds;
789 }
790
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000791 if (bounds_affects_clip(flags)) {
792 fClipStack.clipDevRect(ir, op);
793 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700794 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000795 return false;
796 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000797 }
798
799 if (intersection) {
800 *intersection = ir;
801 }
802 return true;
803}
804
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000805int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
806 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
807 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
808}
809
junov@chromium.orga907ac32012-02-24 21:54:07 +0000810int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
811 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000812 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
813 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000814}
815
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000816int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
817 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000818#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000819 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000820#endif
821
junov@chromium.orga907ac32012-02-24 21:54:07 +0000822 // do this before we create the layer. We don't call the public save() since
823 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400824 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000825
826 fDeviceCMDirty = true;
827
828 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000829 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 return count;
831 }
832
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000833 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
834 // the clipRectBounds() call above?
835 if (kNoLayer_SaveLayerStrategy == strategy) {
836 return count;
837 }
838
reed@google.comb55deeb2012-01-06 14:43:09 +0000839 // Kill the imagefilter if our device doesn't allow it
840 SkLazyPaint lazyP;
841 if (paint && paint->getImageFilter()) {
842 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000843 if (justForImageFilter) {
844 // early exit if the layer was just for the imageFilter
845 return count;
846 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000847 SkPaint* p = lazyP.set(*paint);
848 p->setImageFilter(NULL);
849 paint = p;
850 }
851 }
852
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000853 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
854 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
855 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000857 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000858 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700859 device = this->getDevice();
860 if (device) {
861 device = device->createCompatibleDevice(info);
862 }
reed@google.com76dd2772012-01-05 21:15:07 +0000863 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000864 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000865 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000866 if (NULL == device) {
867 SkDebugf("Unable to create device for layer.");
868 return count;
869 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000870
reed@google.com6f8f2922011-03-04 22:27:10 +0000871 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000872 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 device->unref();
874
875 layer->fNext = fMCRec->fTopLayer;
876 fMCRec->fLayer = layer;
877 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
878
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000879 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880 return count;
881}
882
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000883int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
884 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
885}
886
reed@android.com8a1c16f2008-12-17 15:59:43 +0000887int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
888 SaveFlags flags) {
889 if (0xFF == alpha) {
890 return this->saveLayer(bounds, NULL, flags);
891 } else {
892 SkPaint tmpPaint;
893 tmpPaint.setAlpha(alpha);
894 return this->saveLayer(bounds, &tmpPaint, flags);
895 }
896}
897
898void SkCanvas::restore() {
899 // check for underflow
900 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000901 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000902 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700903 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 }
905}
906
907void SkCanvas::internalRestore() {
908 SkASSERT(fMCStack.count() != 0);
909
910 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000911 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912
Florin Malita5f6102d2014-06-30 10:13:28 -0400913 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000914
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000915 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 DeviceCM* layer = fMCRec->fLayer; // may be null
917 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
918 fMCRec->fLayer = NULL;
919
920 // now do the normal restore()
921 fMCRec->~MCRec(); // balanced in save()
922 fMCStack.pop_back();
923 fMCRec = (MCRec*)fMCStack.back();
924
925 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
926 since if we're being recorded, we don't want to record this (the
927 recorder will have already recorded the restore).
928 */
929 if (NULL != layer) {
930 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000931 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000932 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
933 layer->fPaint);
934 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000936
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000937 SkASSERT(fSaveLayerCount > 0);
938 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000939 }
940 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000941 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000942}
943
944int SkCanvas::getSaveCount() const {
945 return fMCStack.count();
946}
947
948void SkCanvas::restoreToCount(int count) {
949 // sanity check
950 if (count < 1) {
951 count = 1;
952 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000953
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000954 int n = this->getSaveCount() - count;
955 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 this->restore();
957 }
958}
959
reed@google.com7c202932011-12-14 18:48:05 +0000960bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000961 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000962}
963
reed@google.com76f10a32014-02-05 15:32:21 +0000964SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
965 return this->onNewSurface(info);
966}
967
968SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
969 SkBaseDevice* dev = this->getDevice();
970 return dev ? dev->newSurface(info) : NULL;
971}
972
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000973SkImageInfo SkCanvas::imageInfo() const {
974 SkBaseDevice* dev = this->getDevice();
975 if (dev) {
976 return dev->imageInfo();
977 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000978 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000979 }
980}
981
982const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
983 return this->onPeekPixels(info, rowBytes);
984}
985
986const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
987 SkBaseDevice* dev = this->getDevice();
988 return dev ? dev->peekPixels(info, rowBytes) : NULL;
989}
990
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +0000991void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
992 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
993 if (pixels && origin) {
994 *origin = this->getTopDevice(false)->getOrigin();
995 }
996 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +0000997}
998
999void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1000 SkBaseDevice* dev = this->getTopDevice();
1001 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1002}
1003
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001004SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1005 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1006 if (NULL == fAddr) {
1007 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001008 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001009 return; // failure, fAddr is NULL
1010 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001011 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1012 return; // failure, fAddr is NULL
1013 }
1014 fAddr = fBitmap.getPixels();
1015 fRowBytes = fBitmap.rowBytes();
1016 }
1017 SkASSERT(fAddr); // success
1018}
1019
1020bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1021 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001022 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001023 } else {
1024 bitmap->reset();
1025 return false;
1026 }
1027}
1028
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001029void SkCanvas::onPushCull(const SkRect& cullRect) {
1030 // do nothing. Subclasses may do something
1031}
1032
1033void SkCanvas::onPopCull() {
1034 // do nothing. Subclasses may do something
1035}
1036
reed@android.com8a1c16f2008-12-17 15:59:43 +00001037/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001038#ifdef SK_DEBUG
1039// Ensure that cull rects are monotonically nested in device space.
1040void SkCanvas::validateCull(const SkIRect& devCull) {
1041 if (fCullStack.isEmpty()
1042 || devCull.isEmpty()
1043 || fCullStack.top().contains(devCull)) {
1044 return;
1045 }
1046
1047 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1048 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1049 fCullStack.top().x(), fCullStack.top().y(),
1050 fCullStack.top().right(), fCullStack.top().bottom()));
1051
1052#ifdef ASSERT_NESTED_CULLING
1053 SkDEBUGFAIL("Invalid cull.");
1054#endif
1055}
1056#endif
1057
1058void SkCanvas::pushCull(const SkRect& cullRect) {
1059 ++fCullCount;
1060 this->onPushCull(cullRect);
1061
1062#ifdef SK_DEBUG
1063 // Map the cull rect into device space.
1064 SkRect mappedCull;
1065 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1066
1067 // Take clipping into account.
1068 SkIRect devClip, devCull;
1069 mappedCull.roundOut(&devCull);
1070 this->getClipDeviceBounds(&devClip);
1071 if (!devCull.intersect(devClip)) {
1072 devCull.setEmpty();
1073 }
1074
1075 this->validateCull(devCull);
1076 fCullStack.push(devCull); // balanced in popCull
1077#endif
1078}
1079
1080void SkCanvas::popCull() {
1081 SkASSERT(fCullStack.count() == fCullCount);
1082
1083 if (fCullCount > 0) {
1084 --fCullCount;
1085 this->onPopCull();
1086
1087 SkDEBUGCODE(fCullStack.pop());
1088 }
1089}
1090
1091/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001093void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001095 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 return;
1097 }
1098
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001099 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001101 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001102 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001103
1104 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001105
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001106 SkRect storage;
1107 const SkRect* bounds = NULL;
1108 if (paint && paint->canComputeFastBounds()) {
1109 bitmap.getBounds(&storage);
1110 matrix.mapRect(&storage);
1111 bounds = &paint->computeFastBounds(storage, &storage);
1112 }
1113
1114 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001115
1116 while (iter.next()) {
1117 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1118 }
1119
1120 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001121}
1122
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001123void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001124 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 SkPaint tmp;
1126 if (NULL == paint) {
1127 tmp.setDither(true);
1128 paint = &tmp;
1129 }
reed@google.com4b226022011-01-11 18:32:13 +00001130
reed@google.com8926b162012-03-23 15:36:36 +00001131 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001132 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001133 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001134 paint = &looper.paint();
1135 SkImageFilter* filter = paint->getImageFilter();
1136 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001137 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001138 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001139 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001140 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001141 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001142 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001143 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001144 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001145 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001146 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001147 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001148 SkPaint tmpUnfiltered(*paint);
1149 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001150 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1151 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001152 }
1153 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001154 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001155 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001156 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001157 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158}
1159
reed@google.com8926b162012-03-23 15:36:36 +00001160void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1161 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001162 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001163 return;
1164 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001165 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001166
reed@google.com8926b162012-03-23 15:36:36 +00001167 SkPaint tmp;
1168 if (NULL == paint) {
1169 paint = &tmp;
1170 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001171
reed@google.com8926b162012-03-23 15:36:36 +00001172 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001173
reed@google.com8926b162012-03-23 15:36:36 +00001174 while (iter.next()) {
1175 paint = &looper.paint();
1176 SkImageFilter* filter = paint->getImageFilter();
1177 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1178 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001179 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001180 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001181 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001182 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001183 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001184 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001185 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001186 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001187 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001188 SkPaint tmpUnfiltered(*paint);
1189 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001190 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001191 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001192 }
1193 } else {
1194 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1195 }
1196 }
1197 LOOPER_END
1198}
1199
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001201void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001202 SkMatrix m;
1203 m.setTranslate(dx, dy);
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::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001208 SkMatrix m;
1209 m.setScale(sx, sy);
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::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001214 SkMatrix m;
1215 m.setRotate(degrees);
1216 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001217}
1218
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001219void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001220 SkMatrix m;
1221 m.setSkew(sx, sy);
1222 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001223}
1224
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001225void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001226 if (matrix.isIdentity()) {
1227 return;
1228 }
1229
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001231 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001232 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001233
1234 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001235}
1236
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237void SkCanvas::setMatrix(const SkMatrix& matrix) {
1238 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001239 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001240 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001241 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242}
1243
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244void SkCanvas::resetMatrix() {
1245 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001246
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247 matrix.reset();
1248 this->setMatrix(matrix);
1249}
1250
1251//////////////////////////////////////////////////////////////////////////////
1252
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001253void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001254 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1255 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001256}
1257
1258void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001259#ifdef SK_ENABLE_CLIP_QUICKREJECT
1260 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001261 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001262 return false;
1263 }
1264
reed@google.com3b3e8952012-08-16 20:53:31 +00001265 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001266 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001267 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001268
1269 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001270 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001271 }
1272 }
1273#endif
1274
reed@google.com5c3d1472011-02-22 19:12:23 +00001275 AutoValidateClip avc(this);
1276
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001278 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001279 if (!fAllowSoftClip) {
1280 edgeStyle = kHard_ClipEdgeStyle;
1281 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282
reed1f836ee2014-07-07 07:49:34 -07001283 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001284 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001285 // the matrix. This means we don't have to a) make a path, and b) tell
1286 // the region code to scan-convert the path, only to discover that it
1287 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289
reed1f836ee2014-07-07 07:49:34 -07001290 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001291 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed1f836ee2014-07-07 07:49:34 -07001292 fMCRec->fRasterClip.op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001294 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001295 // and clip against that, since it can handle any matrix. However, to
1296 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1297 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298 SkPath path;
1299
1300 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001301 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302 }
1303}
1304
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001305static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1306 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001307 // base is used to limit the size (and therefore memory allocation) of the
1308 // region that results from scan converting devPath.
1309 SkRegion base;
1310
reed@google.com819c9212011-02-23 18:56:55 +00001311 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001312 // since we are intersect, we can do better (tighter) with currRgn's
1313 // bounds, than just using the device. However, if currRgn is complex,
1314 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001315 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001316 // FIXME: we should also be able to do this when currClip->isBW(),
1317 // but relaxing the test above triggers GM asserts in
1318 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001319 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001320 } else {
reed@google.com00177082011-10-12 14:34:30 +00001321 base.setRect(currClip->getBounds());
1322 SkRasterClip clip;
1323 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001324 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001325 }
reed@google.com819c9212011-02-23 18:56:55 +00001326 } else {
reed52d9ac62014-06-30 09:05:34 -07001327 const SkISize size = canvas->getBaseLayerSize();
1328 base.setRect(0, 0, size.width(), size.height());
reed@google.com819c9212011-02-23 18:56:55 +00001329
1330 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001331 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001332 } else {
reed@google.com00177082011-10-12 14:34:30 +00001333 SkRasterClip clip;
1334 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001335 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001336 }
1337 }
1338}
1339
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001340void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001341 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001342 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001343 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1344 } else {
1345 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001346 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001347}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001348
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001349void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001350 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001351 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001352 AutoValidateClip avc(this);
1353
1354 fDeviceCMDirty = true;
1355 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001356 if (!fAllowSoftClip) {
1357 edgeStyle = kHard_ClipEdgeStyle;
1358 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001359
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001360 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001361
1362 SkPath devPath;
1363 devPath.addRRect(transformedRRect);
1364
reed1f836ee2014-07-07 07:49:34 -07001365 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001366 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001367 }
1368
1369 SkPath path;
1370 path.addRRect(rrect);
1371 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001372 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001373}
1374
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001375void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001376 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1377 SkRect r;
1378 if (!path.isInverseFillType() && path.isRect(&r)) {
1379 this->onClipRect(r, op, edgeStyle);
1380 } else {
1381 this->onClipPath(path, op, edgeStyle);
1382 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001383}
1384
1385void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001386#ifdef SK_ENABLE_CLIP_QUICKREJECT
1387 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001388 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001389 return false;
1390 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001391
reed@google.com3b3e8952012-08-16 20:53:31 +00001392 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001393 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001394 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001395
reed@google.comda17f752012-08-16 18:27:05 +00001396 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001397 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001398 }
1399 }
1400#endif
1401
reed@google.com5c3d1472011-02-22 19:12:23 +00001402 AutoValidateClip avc(this);
1403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001405 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001406 if (!fAllowSoftClip) {
1407 edgeStyle = kHard_ClipEdgeStyle;
1408 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409
1410 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001411 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412
reed@google.comfe701122011-11-08 19:41:23 +00001413 // Check if the transfomation, or the original path itself
1414 // made us empty. Note this can also happen if we contained NaN
1415 // values. computing the bounds detects this, and will set our
1416 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1417 if (devPath.getBounds().isEmpty()) {
1418 // resetting the path will remove any NaN or other wanky values
1419 // that might upset our scan converter.
1420 devPath.reset();
1421 }
1422
reed@google.com5c3d1472011-02-22 19:12:23 +00001423 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001424 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001425
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001426 if (fAllowSimplifyClip) {
1427 devPath.reset();
1428 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1429 const SkClipStack* clipStack = getClipStack();
1430 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1431 const SkClipStack::Element* element;
1432 while ((element = iter.next())) {
1433 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001434 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001435 if (type != SkClipStack::Element::kEmpty_Type) {
1436 element->asPath(&operand);
1437 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001438 SkRegion::Op elementOp = element->getOp();
1439 if (elementOp == SkRegion::kReplace_Op) {
1440 devPath = operand;
1441 } else {
1442 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1443 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001444 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1445 // perhaps we need an API change to avoid this sort of mixed-signals about
1446 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001447 if (element->isAA()) {
1448 edgeStyle = kSoft_ClipEdgeStyle;
1449 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001450 }
1451 op = SkRegion::kReplace_Op;
1452 }
1453
reed1f836ee2014-07-07 07:49:34 -07001454 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455}
1456
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001457void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001458 bool inverseFilled) {
1459 // This is for updating the clip conservatively using only bounds
1460 // information.
1461 // Contract:
1462 // The current clip must contain the true clip. The true
1463 // clip is the clip that would have normally been computed
1464 // by calls to clipPath and clipRRect
1465 // Objective:
1466 // Keep the current clip as small as possible without
1467 // breaking the contract, using only clip bounding rectangles
1468 // (for performance).
1469
1470 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1471 // don't have to worry about getting caught in a loop. Thus anywhere
1472 // we call a virtual method, we explicitly prefix it with
1473 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001474
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001475 if (inverseFilled) {
1476 switch (op) {
1477 case SkRegion::kIntersect_Op:
1478 case SkRegion::kDifference_Op:
1479 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001480 // the clip unchanged conservatively respects the contract.
1481 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001482 case SkRegion::kUnion_Op:
1483 case SkRegion::kReplace_Op:
1484 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001485 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001486 // These ops can grow the current clip up to the extents of
1487 // the input clip, which is inverse filled, so we just set
1488 // the current clip to the device bounds.
1489 SkRect deviceBounds;
1490 SkIRect deviceIBounds;
1491 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001492 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001493
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001494 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001495 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001496 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001497 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001498 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001499 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001500 break;
1501 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001502 default:
1503 SkASSERT(0); // unhandled op?
1504 }
1505 } else {
1506 // Not inverse filled
1507 switch (op) {
1508 case SkRegion::kIntersect_Op:
1509 case SkRegion::kUnion_Op:
1510 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001511 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1512 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001513 case SkRegion::kDifference_Op:
1514 // Difference can only shrink the current clip.
1515 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001516 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001517 case SkRegion::kReverseDifference_Op:
1518 // To reverse, we swap in the bounds with a replace op.
1519 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001520 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1521 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001522 case SkRegion::kXOR_Op:
1523 // Be conservative, based on (A XOR B) always included in (A union B),
1524 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001525 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1526 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001527 default:
1528 SkASSERT(0); // unhandled op?
1529 }
1530 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001531}
1532
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001533void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001534 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001535}
1536
1537void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001538 AutoValidateClip avc(this);
1539
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001541 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001542
reed@google.com5c3d1472011-02-22 19:12:23 +00001543 // todo: signal fClipStack that we have a region, and therefore (I guess)
1544 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001545 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001546
reed1f836ee2014-07-07 07:49:34 -07001547 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001548}
1549
reed@google.com819c9212011-02-23 18:56:55 +00001550#ifdef SK_DEBUG
1551void SkCanvas::validateClip() const {
1552 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001553 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001554 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001555 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001556 return;
1557 }
1558
reed@google.com819c9212011-02-23 18:56:55 +00001559 SkIRect ir;
1560 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001561 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001562
robertphillips@google.com80214e22012-07-20 15:33:18 +00001563 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001564 const SkClipStack::Element* element;
1565 while ((element = iter.next()) != NULL) {
1566 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001567 case SkClipStack::Element::kRect_Type:
1568 element->getRect().round(&ir);
1569 tmpClip.op(ir, element->getOp());
1570 break;
1571 case SkClipStack::Element::kEmpty_Type:
1572 tmpClip.setEmpty();
1573 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001574 default: {
1575 SkPath path;
1576 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001577 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001578 break;
1579 }
reed@google.com819c9212011-02-23 18:56:55 +00001580 }
1581 }
reed@google.com819c9212011-02-23 18:56:55 +00001582}
1583#endif
1584
reed@google.com90c07ea2012-04-13 13:50:27 +00001585void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001586 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001587 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001588
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001589 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001590 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001591 }
1592}
1593
reed@google.com5c3d1472011-02-22 19:12:23 +00001594///////////////////////////////////////////////////////////////////////////////
1595
reed@google.com754de5f2014-02-24 19:38:20 +00001596bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001597 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001598}
1599
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001600bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001601 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001602}
1603
reed@google.com3b3e8952012-08-16 20:53:31 +00001604bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001605
reed@google.com16078632011-12-06 18:56:37 +00001606 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001607 return true;
1608
reed1f836ee2014-07-07 07:49:34 -07001609 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610 return true;
1611 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612
reed1f836ee2014-07-07 07:49:34 -07001613 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001614 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001615 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001616 SkIRect idst;
1617 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001618 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001619 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001620 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001621
reed@android.coma380ae42009-07-21 01:17:02 +00001622 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001623 // TODO: should we use | instead, or compare all 4 at once?
1624 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001625 return true;
1626 }
reed@google.comc0784db2013-12-13 21:16:12 +00001627 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001628 return true;
1629 }
1630 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632}
1633
reed@google.com3b3e8952012-08-16 20:53:31 +00001634bool SkCanvas::quickReject(const SkPath& path) const {
1635 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636}
1637
reed@google.com3b3e8952012-08-16 20:53:31 +00001638bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001639 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641 return false;
1642 }
1643
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001644 SkMatrix inverse;
1645 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001646 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001647 if (bounds) {
1648 bounds->setEmpty();
1649 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001650 return false;
1651 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001653 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001654 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001655 // adjust it outwards in case we are antialiasing
1656 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001657
reed@google.com8f4d2302013-12-17 16:44:46 +00001658 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1659 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001660 inverse.mapRect(bounds, r);
1661 }
1662 return true;
1663}
1664
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001665bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001666 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001667 if (clip.isEmpty()) {
1668 if (bounds) {
1669 bounds->setEmpty();
1670 }
1671 return false;
1672 }
1673
1674 if (NULL != bounds) {
1675 *bounds = clip.getBounds();
1676 }
1677 return true;
1678}
1679
reed@android.com8a1c16f2008-12-17 15:59:43 +00001680const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001681 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001682}
1683
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001684#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001685SkCanvas::ClipType SkCanvas::getClipType() const {
reed1f836ee2014-07-07 07:49:34 -07001686 if (fMCRec->fRasterClip.isEmpty()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001687 return kEmpty_ClipType;
1688 }
reed1f836ee2014-07-07 07:49:34 -07001689 if (fMCRec->fRasterClip.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001690 return kRect_ClipType;
1691 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001692 return kComplex_ClipType;
1693}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001694#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001695
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001696const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001697 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001698}
1699
1700void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1701 path->reset();
1702
reed1f836ee2014-07-07 07:49:34 -07001703 const SkRegion& rgn = fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001704 if (rgn.isEmpty()) {
1705 return;
1706 }
1707 (void)rgn.getBoundaryPath(path);
1708}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001709
reed@google.com9c135db2014-03-12 18:28:35 +00001710GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1711 SkBaseDevice* dev = this->getTopDevice();
1712 return dev ? dev->accessRenderTarget() : NULL;
1713}
1714
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001715SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001716 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001717 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718}
1719
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001720GrContext* SkCanvas::getGrContext() {
1721#if SK_SUPPORT_GPU
1722 SkBaseDevice* device = this->getTopDevice();
1723 if (NULL != device) {
1724 GrRenderTarget* renderTarget = device->accessRenderTarget();
1725 if (NULL != renderTarget) {
1726 return renderTarget->getContext();
1727 }
1728 }
1729#endif
1730
1731 return NULL;
1732
1733}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001734
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001735void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1736 const SkPaint& paint) {
1737 if (outer.isEmpty()) {
1738 return;
1739 }
1740 if (inner.isEmpty()) {
1741 this->drawRRect(outer, paint);
1742 return;
1743 }
1744
1745 // We don't have this method (yet), but technically this is what we should
1746 // be able to assert...
1747 // SkASSERT(outer.contains(inner));
1748 //
1749 // For now at least check for containment of bounds
1750 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1751
1752 this->onDrawDRRect(outer, inner, paint);
1753}
1754
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755//////////////////////////////////////////////////////////////////////////////
1756// These are the virtual drawing methods
1757//////////////////////////////////////////////////////////////////////////////
1758
reed@google.com2a981812011-04-14 18:59:28 +00001759void SkCanvas::clear(SkColor color) {
1760 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001761 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001762 while (iter.next()) {
1763 iter.fDevice->clear(color);
1764 }
1765}
1766
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001767void SkCanvas::onDiscard() {
1768 if (NULL != fSurfaceBase) {
1769 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1770 }
1771}
1772
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001774 this->internalDrawPaint(paint);
1775}
1776
1777void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001778 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001779
1780 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001781 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001782 }
1783
reed@google.com4e2b3d32011-04-07 14:18:59 +00001784 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785}
1786
1787void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1788 const SkPaint& paint) {
1789 if ((long)count <= 0) {
1790 return;
1791 }
1792
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001793 SkRect r, storage;
1794 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001795 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001796 // special-case 2 points (common for drawing a single line)
1797 if (2 == count) {
1798 r.set(pts[0], pts[1]);
1799 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001800 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001801 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001802 bounds = &paint.computeFastStrokeBounds(r, &storage);
1803 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001804 return;
1805 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001806 }
reed@google.coma584aed2012-05-16 14:06:02 +00001807
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808 SkASSERT(pts != NULL);
1809
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001810 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001811
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001813 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 }
reed@google.com4b226022011-01-11 18:32:13 +00001815
reed@google.com4e2b3d32011-04-07 14:18:59 +00001816 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817}
1818
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001819void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001820 SkRect storage;
1821 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001823 bounds = &paint.computeFastBounds(r, &storage);
1824 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825 return;
1826 }
1827 }
reed@google.com4b226022011-01-11 18:32:13 +00001828
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001829 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001830
1831 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001832 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001833 }
1834
reed@google.com4e2b3d32011-04-07 14:18:59 +00001835 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001836}
1837
reed@google.com4ed0fb72012-12-12 20:48:18 +00001838void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001839 SkRect storage;
1840 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001841 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001842 bounds = &paint.computeFastBounds(oval, &storage);
1843 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001844 return;
1845 }
1846 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001847
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001848 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001849
1850 while (iter.next()) {
1851 iter.fDevice->drawOval(iter, oval, looper.paint());
1852 }
1853
1854 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001855}
1856
1857void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001858 SkRect storage;
1859 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001860 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001861 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1862 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001863 return;
1864 }
1865 }
1866
1867 if (rrect.isRect()) {
1868 // call the non-virtual version
1869 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001870 return;
1871 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001872 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001873 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1874 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001875 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001876
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001877 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001878
1879 while (iter.next()) {
1880 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1881 }
1882
1883 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001884}
1885
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001886void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1887 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001888 SkRect storage;
1889 const SkRect* bounds = NULL;
1890 if (paint.canComputeFastBounds()) {
1891 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1892 if (this->quickReject(*bounds)) {
1893 return;
1894 }
1895 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001896
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001897 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001898
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001899 while (iter.next()) {
1900 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1901 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001902
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001903 LOOPER_END
1904}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001905
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001906void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001907 if (!path.isFinite()) {
1908 return;
1909 }
1910
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001911 SkRect storage;
1912 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001913 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001914 const SkRect& pathBounds = path.getBounds();
1915 bounds = &paint.computeFastBounds(pathBounds, &storage);
1916 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917 return;
1918 }
1919 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001920
1921 const SkRect& r = path.getBounds();
1922 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001923 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001924 this->internalDrawPaint(paint);
1925 }
1926 return;
1927 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001929 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001930
1931 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001932 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933 }
1934
reed@google.com4e2b3d32011-04-07 14:18:59 +00001935 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001936}
1937
1938void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1939 const SkPaint* paint) {
1940 SkDEBUGCODE(bitmap.validate();)
1941
reed@google.com3d608122011-11-21 15:16:16 +00001942 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001943 SkRect bounds = {
1944 x, y,
1945 x + SkIntToScalar(bitmap.width()),
1946 y + SkIntToScalar(bitmap.height())
1947 };
1948 if (paint) {
1949 (void)paint->computeFastBounds(bounds, &bounds);
1950 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001951 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001952 return;
1953 }
1954 }
reed@google.com4b226022011-01-11 18:32:13 +00001955
reed@android.com8a1c16f2008-12-17 15:59:43 +00001956 SkMatrix matrix;
1957 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001958 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959}
1960
reed@google.com9987ec32011-09-07 11:57:52 +00001961// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001962void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001963 const SkRect& dst, const SkPaint* paint,
1964 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001965 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966 return;
1967 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001968
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001969 SkRect storage;
1970 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001971 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001972 if (paint) {
1973 bounds = &paint->computeFastBounds(dst, &storage);
1974 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001975 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001976 return;
1977 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 }
reed@google.com3d608122011-11-21 15:16:16 +00001979
reed@google.com33535f32012-09-25 15:37:50 +00001980 SkLazyPaint lazy;
1981 if (NULL == paint) {
1982 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001983 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001984
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001985 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001986
reed@google.com33535f32012-09-25 15:37:50 +00001987 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001988 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001989 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001990
reed@google.com33535f32012-09-25 15:37:50 +00001991 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001992}
1993
reed@google.com71121732012-09-18 15:14:33 +00001994void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001995 const SkRect& dst, const SkPaint* paint,
1996 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001997 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001998 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001999}
2000
reed@android.com8a1c16f2008-12-17 15:59:43 +00002001void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
2002 const SkPaint* paint) {
2003 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002004 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002005}
2006
reed@google.com9987ec32011-09-07 11:57:52 +00002007void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2008 const SkIRect& center, const SkRect& dst,
2009 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002010 if (bitmap.drawsNothing()) {
2011 return;
2012 }
reed@google.com3d608122011-11-21 15:16:16 +00002013 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002014 SkRect storage;
2015 const SkRect* bounds = &dst;
2016 if (paint) {
2017 bounds = &paint->computeFastBounds(dst, &storage);
2018 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002019 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002020 return;
2021 }
2022 }
2023
reed@google.com9987ec32011-09-07 11:57:52 +00002024 const int32_t w = bitmap.width();
2025 const int32_t h = bitmap.height();
2026
2027 SkIRect c = center;
2028 // pin center to the bounds of the bitmap
2029 c.fLeft = SkMax32(0, center.fLeft);
2030 c.fTop = SkMax32(0, center.fTop);
2031 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2032 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2033
reed@google.com71121732012-09-18 15:14:33 +00002034 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002035 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002036 };
2037 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002038 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002039 };
reed@google.com9987ec32011-09-07 11:57:52 +00002040 SkScalar dstX[4] = {
2041 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2042 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2043 };
2044 SkScalar dstY[4] = {
2045 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2046 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2047 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002048
reed@google.com9987ec32011-09-07 11:57:52 +00002049 if (dstX[1] > dstX[2]) {
2050 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2051 dstX[2] = dstX[1];
2052 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002053
reed@google.com9987ec32011-09-07 11:57:52 +00002054 if (dstY[1] > dstY[2]) {
2055 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2056 dstY[2] = dstY[1];
2057 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002058
reed@google.com9987ec32011-09-07 11:57:52 +00002059 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002060 SkRect s, d;
2061
reed@google.com9987ec32011-09-07 11:57:52 +00002062 s.fTop = srcY[y];
2063 s.fBottom = srcY[y+1];
2064 d.fTop = dstY[y];
2065 d.fBottom = dstY[y+1];
2066 for (int x = 0; x < 3; x++) {
2067 s.fLeft = srcX[x];
2068 s.fRight = srcX[x+1];
2069 d.fLeft = dstX[x];
2070 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002071 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002072 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002073 }
2074 }
2075}
2076
2077void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2078 const SkRect& dst, const SkPaint* paint) {
2079 SkDEBUGCODE(bitmap.validate();)
2080
2081 // Need a device entry-point, so gpu can use a mesh
2082 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2083}
2084
reed@google.comf67e4cf2011-03-15 20:56:58 +00002085class SkDeviceFilteredPaint {
2086public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002087 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2088 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002089 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002090 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002091 newPaint->setFlags(flags.fFlags);
2092 newPaint->setHinting(flags.fHinting);
2093 fPaint = newPaint;
2094 } else {
2095 fPaint = &paint;
2096 }
2097 }
2098
reed@google.comf67e4cf2011-03-15 20:56:58 +00002099 const SkPaint& paint() const { return *fPaint; }
2100
2101private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002102 const SkPaint* fPaint;
2103 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002104};
2105
bungeman@google.com52c748b2011-08-22 21:30:43 +00002106void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2107 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002108 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002109 draw.fDevice->drawRect(draw, r, paint);
2110 } else {
2111 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002112 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002113 draw.fDevice->drawRect(draw, r, p);
2114 }
2115}
2116
2117void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2118 const char text[], size_t byteLength,
2119 SkScalar x, SkScalar y) {
2120 SkASSERT(byteLength == 0 || text != NULL);
2121
2122 // nothing to draw
2123 if (text == NULL || byteLength == 0 ||
2124 draw.fClip->isEmpty() ||
2125 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2126 return;
2127 }
2128
2129 SkScalar width = 0;
2130 SkPoint start;
2131
2132 start.set(0, 0); // to avoid warning
2133 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2134 SkPaint::kStrikeThruText_Flag)) {
2135 width = paint.measureText(text, byteLength);
2136
2137 SkScalar offsetX = 0;
2138 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2139 offsetX = SkScalarHalf(width);
2140 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2141 offsetX = width;
2142 }
2143 start.set(x - offsetX, y);
2144 }
2145
2146 if (0 == width) {
2147 return;
2148 }
2149
2150 uint32_t flags = paint.getFlags();
2151
2152 if (flags & (SkPaint::kUnderlineText_Flag |
2153 SkPaint::kStrikeThruText_Flag)) {
2154 SkScalar textSize = paint.getTextSize();
2155 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2156 SkRect r;
2157
2158 r.fLeft = start.fX;
2159 r.fRight = start.fX + width;
2160
2161 if (flags & SkPaint::kUnderlineText_Flag) {
2162 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2163 start.fY);
2164 r.fTop = offset;
2165 r.fBottom = offset + height;
2166 DrawRect(draw, paint, r, textSize);
2167 }
2168 if (flags & SkPaint::kStrikeThruText_Flag) {
2169 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2170 start.fY);
2171 r.fTop = offset;
2172 r.fBottom = offset + height;
2173 DrawRect(draw, paint, r, textSize);
2174 }
2175 }
2176}
2177
reed@google.come0d9ce82014-04-23 04:00:17 +00002178void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2179 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002180 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181
2182 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002183 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002184 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002185 DrawTextDecorations(iter, dfp.paint(),
2186 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187 }
2188
reed@google.com4e2b3d32011-04-07 14:18:59 +00002189 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190}
2191
reed@google.come0d9ce82014-04-23 04:00:17 +00002192void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2193 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002194 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002195
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002197 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002198 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002199 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002200 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002201
reed@google.com4e2b3d32011-04-07 14:18:59 +00002202 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203}
2204
reed@google.come0d9ce82014-04-23 04:00:17 +00002205void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2206 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002207 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002208
reed@android.com8a1c16f2008-12-17 15:59:43 +00002209 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002210 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002212 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002213 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002214
reed@google.com4e2b3d32011-04-07 14:18:59 +00002215 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216}
2217
reed@google.come0d9ce82014-04-23 04:00:17 +00002218void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2219 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002220 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002221
reed@android.com8a1c16f2008-12-17 15:59:43 +00002222 while (iter.next()) {
2223 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002224 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002226
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002227 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002228}
2229
reed@google.come0d9ce82014-04-23 04:00:17 +00002230// These will become non-virtual, so they always call the (virtual) onDraw... method
2231void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2232 const SkPaint& paint) {
2233 this->onDrawText(text, byteLength, x, y, paint);
2234}
2235void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2236 const SkPaint& paint) {
2237 this->onDrawPosText(text, byteLength, pos, paint);
2238}
2239void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2240 SkScalar constY, const SkPaint& paint) {
2241 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2242}
2243void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2244 const SkMatrix* matrix, const SkPaint& paint) {
2245 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2246}
2247
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2249 const SkPoint verts[], const SkPoint texs[],
2250 const SkColor colors[], SkXfermode* xmode,
2251 const uint16_t indices[], int indexCount,
2252 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002253 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002254
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255 while (iter.next()) {
2256 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002257 colors, xmode, indices, indexCount,
2258 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002259 }
reed@google.com4b226022011-01-11 18:32:13 +00002260
reed@google.com4e2b3d32011-04-07 14:18:59 +00002261 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262}
2263
dandovb3c9d1c2014-08-12 08:34:29 -07002264void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2265 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2266 if (NULL == cubics) {
2267 return;
2268 }
mtklein6cfa73a2014-08-13 13:33:49 -07002269
dandovecfff212014-08-04 10:02:00 -07002270 // Since a patch is always within the convex hull of the control points, we discard it when its
2271 // bounding rectangle is completely outside the current clip.
2272 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002273 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002274 if (this->quickReject(bounds)) {
2275 return;
2276 }
mtklein6cfa73a2014-08-13 13:33:49 -07002277
dandovb3c9d1c2014-08-12 08:34:29 -07002278 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2279}
2280
2281void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2282 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2283
dandovecfff212014-08-04 10:02:00 -07002284 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002285
dandovecfff212014-08-04 10:02:00 -07002286 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002287 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002288 }
mtklein6cfa73a2014-08-13 13:33:49 -07002289
dandovecfff212014-08-04 10:02:00 -07002290 LOOPER_END
2291}
2292
reed@android.com8a1c16f2008-12-17 15:59:43 +00002293//////////////////////////////////////////////////////////////////////////////
2294// These methods are NOT virtual, and therefore must call back into virtual
2295// methods, rather than actually drawing themselves.
2296//////////////////////////////////////////////////////////////////////////////
2297
2298void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002299 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002300 SkPaint paint;
2301
2302 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002303 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002304 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305 }
2306 this->drawPaint(paint);
2307}
2308
reed@android.com845fdac2009-06-23 03:01:32 +00002309void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002310 SkPaint paint;
2311
2312 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002313 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002314 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002315 }
2316 this->drawPaint(paint);
2317}
2318
2319void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2320 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002321
reed@android.com8a1c16f2008-12-17 15:59:43 +00002322 pt.set(x, y);
2323 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2324}
2325
2326void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2327 SkPoint pt;
2328 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002329
reed@android.com8a1c16f2008-12-17 15:59:43 +00002330 pt.set(x, y);
2331 paint.setColor(color);
2332 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2333}
2334
2335void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2336 const SkPaint& paint) {
2337 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002338
reed@android.com8a1c16f2008-12-17 15:59:43 +00002339 pts[0].set(x0, y0);
2340 pts[1].set(x1, y1);
2341 this->drawPoints(kLines_PointMode, 2, pts, paint);
2342}
2343
2344void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2345 SkScalar right, SkScalar bottom,
2346 const SkPaint& paint) {
2347 SkRect r;
2348
2349 r.set(left, top, right, bottom);
2350 this->drawRect(r, paint);
2351}
2352
2353void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2354 const SkPaint& paint) {
2355 if (radius < 0) {
2356 radius = 0;
2357 }
2358
2359 SkRect r;
2360 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002361 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362}
2363
2364void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2365 const SkPaint& paint) {
2366 if (rx > 0 && ry > 0) {
2367 if (paint.canComputeFastBounds()) {
2368 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002369 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002370 return;
2371 }
2372 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002373 SkRRect rrect;
2374 rrect.setRectXY(r, rx, ry);
2375 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376 } else {
2377 this->drawRect(r, paint);
2378 }
2379}
2380
reed@android.com8a1c16f2008-12-17 15:59:43 +00002381void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2382 SkScalar sweepAngle, bool useCenter,
2383 const SkPaint& paint) {
2384 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2385 this->drawOval(oval, paint);
2386 } else {
2387 SkPath path;
2388 if (useCenter) {
2389 path.moveTo(oval.centerX(), oval.centerY());
2390 }
2391 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2392 if (useCenter) {
2393 path.close();
2394 }
2395 this->drawPath(path, paint);
2396 }
2397}
2398
2399void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2400 const SkPath& path, SkScalar hOffset,
2401 SkScalar vOffset, const SkPaint& paint) {
2402 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002403
reed@android.com8a1c16f2008-12-17 15:59:43 +00002404 matrix.setTranslate(hOffset, vOffset);
2405 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2406}
2407
reed@android.comf76bacf2009-05-13 14:00:33 +00002408///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002409void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002410 SkBaseDevice* device = this->getDevice();
2411 if (NULL != device) {
2412 device->EXPERIMENTAL_optimize(picture);
2413 }
2414}
reed@android.comf76bacf2009-05-13 14:00:33 +00002415
robertphillips9b14f262014-06-04 05:40:44 -07002416void SkCanvas::drawPicture(const SkPicture* picture) {
2417 if (NULL != picture) {
reedd5fa1a42014-08-09 11:08:05 -07002418 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002419 }
2420}
2421
reedd5fa1a42014-08-09 11:08:05 -07002422void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
2423 if (NULL != picture) {
2424 if (matrix && matrix->isIdentity()) {
2425 matrix = NULL;
2426 }
2427 this->onDrawPicture(picture, matrix, paint);
2428 }
2429}
robertphillips9b14f262014-06-04 05:40:44 -07002430
reedd5fa1a42014-08-09 11:08:05 -07002431void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2432 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002433 SkBaseDevice* device = this->getTopDevice();
2434 if (NULL != device) {
2435 // Canvas has to first give the device the opportunity to render
2436 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002437 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002438 return; // the device has rendered the entire picture
2439 }
2440 }
2441
reedd5fa1a42014-08-09 11:08:05 -07002442 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->width(), picture->height());
2443
robertphillips9b14f262014-06-04 05:40:44 -07002444 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002445}
2446
reed@android.com8a1c16f2008-12-17 15:59:43 +00002447///////////////////////////////////////////////////////////////////////////////
2448///////////////////////////////////////////////////////////////////////////////
2449
2450SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002451 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002452
2453 SkASSERT(canvas);
2454
2455 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2456 fDone = !fImpl->next();
2457}
2458
2459SkCanvas::LayerIter::~LayerIter() {
2460 fImpl->~SkDrawIter();
2461}
2462
2463void SkCanvas::LayerIter::next() {
2464 fDone = !fImpl->next();
2465}
2466
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002467SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002468 return fImpl->getDevice();
2469}
2470
2471const SkMatrix& SkCanvas::LayerIter::matrix() const {
2472 return fImpl->getMatrix();
2473}
2474
2475const SkPaint& SkCanvas::LayerIter::paint() const {
2476 const SkPaint* paint = fImpl->getPaint();
2477 if (NULL == paint) {
2478 paint = &fDefaultPaint;
2479 }
2480 return *paint;
2481}
2482
2483const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2484int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2485int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002486
2487///////////////////////////////////////////////////////////////////////////////
2488
fmalitac3b589a2014-06-05 12:40:07 -07002489SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002490
2491///////////////////////////////////////////////////////////////////////////////
2492
2493static bool supported_for_raster_canvas(const SkImageInfo& info) {
2494 switch (info.alphaType()) {
2495 case kPremul_SkAlphaType:
2496 case kOpaque_SkAlphaType:
2497 break;
2498 default:
2499 return false;
2500 }
2501
2502 switch (info.colorType()) {
2503 case kAlpha_8_SkColorType:
2504 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002505 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002506 break;
2507 default:
2508 return false;
2509 }
2510
2511 return true;
2512}
2513
2514SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2515 if (!supported_for_raster_canvas(info)) {
2516 return NULL;
2517 }
2518
2519 SkBitmap bitmap;
2520 if (!bitmap.allocPixels(info)) {
2521 return NULL;
2522 }
2523
2524 // should this functionality be moved into allocPixels()?
2525 if (!bitmap.info().isOpaque()) {
2526 bitmap.eraseColor(0);
2527 }
2528 return SkNEW_ARGS(SkCanvas, (bitmap));
2529}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002530
2531SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2532 if (!supported_for_raster_canvas(info)) {
2533 return NULL;
2534 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002535
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002536 SkBitmap bitmap;
2537 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2538 return NULL;
2539 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002540 return SkNEW_ARGS(SkCanvas, (bitmap));
2541}
reedd5fa1a42014-08-09 11:08:05 -07002542
2543///////////////////////////////////////////////////////////////////////////////
2544
2545SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
2546 const SkPaint* paint, int width, int height)
2547 : fCanvas(canvas)
2548 , fSaveCount(canvas->getSaveCount())
2549{
2550 if (NULL != paint) {
2551 SkRect bounds = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
2552 if (matrix) {
2553 matrix->mapRect(&bounds);
2554 }
2555 canvas->saveLayer(&bounds, paint);
2556 } else if (NULL != matrix) {
2557 canvas->save();
2558 }
mtklein6cfa73a2014-08-13 13:33:49 -07002559
reedd5fa1a42014-08-09 11:08:05 -07002560 if (NULL != matrix) {
2561 canvas->concat(*matrix);
2562 }
2563}
2564
2565SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2566 fCanvas->restoreToCount(fSaveCount);
2567}