blob: 2b08a94c85e603a434d35dece267c3ab96bb0498 [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 +00001684const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001685 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001686}
1687
1688void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1689 path->reset();
1690
reed1f836ee2014-07-07 07:49:34 -07001691 const SkRegion& rgn = fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001692 if (rgn.isEmpty()) {
1693 return;
1694 }
1695 (void)rgn.getBoundaryPath(path);
1696}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697
reed@google.com9c135db2014-03-12 18:28:35 +00001698GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1699 SkBaseDevice* dev = this->getTopDevice();
1700 return dev ? dev->accessRenderTarget() : NULL;
1701}
1702
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001703SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001704 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001705 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706}
1707
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001708GrContext* SkCanvas::getGrContext() {
1709#if SK_SUPPORT_GPU
1710 SkBaseDevice* device = this->getTopDevice();
1711 if (NULL != device) {
1712 GrRenderTarget* renderTarget = device->accessRenderTarget();
1713 if (NULL != renderTarget) {
1714 return renderTarget->getContext();
1715 }
1716 }
1717#endif
1718
1719 return NULL;
1720
1721}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001722
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001723void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1724 const SkPaint& paint) {
1725 if (outer.isEmpty()) {
1726 return;
1727 }
1728 if (inner.isEmpty()) {
1729 this->drawRRect(outer, paint);
1730 return;
1731 }
1732
1733 // We don't have this method (yet), but technically this is what we should
1734 // be able to assert...
1735 // SkASSERT(outer.contains(inner));
1736 //
1737 // For now at least check for containment of bounds
1738 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1739
1740 this->onDrawDRRect(outer, inner, paint);
1741}
1742
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743//////////////////////////////////////////////////////////////////////////////
1744// These are the virtual drawing methods
1745//////////////////////////////////////////////////////////////////////////////
1746
reed@google.com2a981812011-04-14 18:59:28 +00001747void SkCanvas::clear(SkColor color) {
1748 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001749 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001750 while (iter.next()) {
1751 iter.fDevice->clear(color);
1752 }
1753}
1754
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001755void SkCanvas::onDiscard() {
1756 if (NULL != fSurfaceBase) {
1757 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1758 }
1759}
1760
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001762 this->internalDrawPaint(paint);
1763}
1764
1765void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001766 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767
1768 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001769 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001770 }
1771
reed@google.com4e2b3d32011-04-07 14:18:59 +00001772 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773}
1774
1775void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1776 const SkPaint& paint) {
1777 if ((long)count <= 0) {
1778 return;
1779 }
1780
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001781 SkRect r, storage;
1782 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001783 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001784 // special-case 2 points (common for drawing a single line)
1785 if (2 == count) {
1786 r.set(pts[0], pts[1]);
1787 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001788 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001789 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001790 bounds = &paint.computeFastStrokeBounds(r, &storage);
1791 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001792 return;
1793 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001794 }
reed@google.coma584aed2012-05-16 14:06:02 +00001795
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796 SkASSERT(pts != NULL);
1797
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001798 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001799
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001801 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001802 }
reed@google.com4b226022011-01-11 18:32:13 +00001803
reed@google.com4e2b3d32011-04-07 14:18:59 +00001804 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805}
1806
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001807void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001808 SkRect storage;
1809 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001811 bounds = &paint.computeFastBounds(r, &storage);
1812 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001813 return;
1814 }
1815 }
reed@google.com4b226022011-01-11 18:32:13 +00001816
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001817 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818
1819 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001820 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821 }
1822
reed@google.com4e2b3d32011-04-07 14:18:59 +00001823 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824}
1825
reed@google.com4ed0fb72012-12-12 20:48:18 +00001826void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001827 SkRect storage;
1828 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001829 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001830 bounds = &paint.computeFastBounds(oval, &storage);
1831 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001832 return;
1833 }
1834 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001835
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001836 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001837
1838 while (iter.next()) {
1839 iter.fDevice->drawOval(iter, oval, looper.paint());
1840 }
1841
1842 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001843}
1844
1845void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001846 SkRect storage;
1847 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001848 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001849 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1850 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001851 return;
1852 }
1853 }
1854
1855 if (rrect.isRect()) {
1856 // call the non-virtual version
1857 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001858 return;
1859 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001860 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001861 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1862 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001863 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001864
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001865 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001866
1867 while (iter.next()) {
1868 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1869 }
1870
1871 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001872}
1873
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001874void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1875 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001876 SkRect storage;
1877 const SkRect* bounds = NULL;
1878 if (paint.canComputeFastBounds()) {
1879 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1880 if (this->quickReject(*bounds)) {
1881 return;
1882 }
1883 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001884
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001885 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001886
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001887 while (iter.next()) {
1888 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1889 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001890
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001891 LOOPER_END
1892}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001893
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001894void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001895 if (!path.isFinite()) {
1896 return;
1897 }
1898
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001899 SkRect storage;
1900 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001901 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001902 const SkRect& pathBounds = path.getBounds();
1903 bounds = &paint.computeFastBounds(pathBounds, &storage);
1904 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905 return;
1906 }
1907 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001908
1909 const SkRect& r = path.getBounds();
1910 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001911 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001912 this->internalDrawPaint(paint);
1913 }
1914 return;
1915 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001916
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001917 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001918
1919 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001920 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921 }
1922
reed@google.com4e2b3d32011-04-07 14:18:59 +00001923 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001924}
1925
1926void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1927 const SkPaint* paint) {
1928 SkDEBUGCODE(bitmap.validate();)
1929
reed@google.com3d608122011-11-21 15:16:16 +00001930 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001931 SkRect bounds = {
1932 x, y,
1933 x + SkIntToScalar(bitmap.width()),
1934 y + SkIntToScalar(bitmap.height())
1935 };
1936 if (paint) {
1937 (void)paint->computeFastBounds(bounds, &bounds);
1938 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001939 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940 return;
1941 }
1942 }
reed@google.com4b226022011-01-11 18:32:13 +00001943
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944 SkMatrix matrix;
1945 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001946 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947}
1948
reed@google.com9987ec32011-09-07 11:57:52 +00001949// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001950void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001951 const SkRect& dst, const SkPaint* paint,
1952 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001953 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001954 return;
1955 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001956
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001957 SkRect storage;
1958 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001959 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001960 if (paint) {
1961 bounds = &paint->computeFastBounds(dst, &storage);
1962 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001963 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001964 return;
1965 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966 }
reed@google.com3d608122011-11-21 15:16:16 +00001967
reed@google.com33535f32012-09-25 15:37:50 +00001968 SkLazyPaint lazy;
1969 if (NULL == paint) {
1970 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001971 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001972
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001973 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001974
reed@google.com33535f32012-09-25 15:37:50 +00001975 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001976 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001977 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001978
reed@google.com33535f32012-09-25 15:37:50 +00001979 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001980}
1981
reed@google.com71121732012-09-18 15:14:33 +00001982void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001983 const SkRect& dst, const SkPaint* paint,
1984 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001985 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001986 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001987}
1988
reed@android.com8a1c16f2008-12-17 15:59:43 +00001989void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1990 const SkPaint* paint) {
1991 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001992 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001993}
1994
reed@google.com9987ec32011-09-07 11:57:52 +00001995void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1996 const SkIRect& center, const SkRect& dst,
1997 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001998 if (bitmap.drawsNothing()) {
1999 return;
2000 }
reed@google.com3d608122011-11-21 15:16:16 +00002001 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002002 SkRect storage;
2003 const SkRect* bounds = &dst;
2004 if (paint) {
2005 bounds = &paint->computeFastBounds(dst, &storage);
2006 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002007 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002008 return;
2009 }
2010 }
2011
reed@google.com9987ec32011-09-07 11:57:52 +00002012 const int32_t w = bitmap.width();
2013 const int32_t h = bitmap.height();
2014
2015 SkIRect c = center;
2016 // pin center to the bounds of the bitmap
2017 c.fLeft = SkMax32(0, center.fLeft);
2018 c.fTop = SkMax32(0, center.fTop);
2019 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2020 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2021
reed@google.com71121732012-09-18 15:14:33 +00002022 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002023 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002024 };
2025 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002026 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002027 };
reed@google.com9987ec32011-09-07 11:57:52 +00002028 SkScalar dstX[4] = {
2029 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2030 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2031 };
2032 SkScalar dstY[4] = {
2033 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2034 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2035 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002036
reed@google.com9987ec32011-09-07 11:57:52 +00002037 if (dstX[1] > dstX[2]) {
2038 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2039 dstX[2] = dstX[1];
2040 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002041
reed@google.com9987ec32011-09-07 11:57:52 +00002042 if (dstY[1] > dstY[2]) {
2043 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2044 dstY[2] = dstY[1];
2045 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002046
reed@google.com9987ec32011-09-07 11:57:52 +00002047 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002048 SkRect s, d;
2049
reed@google.com9987ec32011-09-07 11:57:52 +00002050 s.fTop = srcY[y];
2051 s.fBottom = srcY[y+1];
2052 d.fTop = dstY[y];
2053 d.fBottom = dstY[y+1];
2054 for (int x = 0; x < 3; x++) {
2055 s.fLeft = srcX[x];
2056 s.fRight = srcX[x+1];
2057 d.fLeft = dstX[x];
2058 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002059 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002060 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002061 }
2062 }
2063}
2064
2065void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2066 const SkRect& dst, const SkPaint* paint) {
2067 SkDEBUGCODE(bitmap.validate();)
2068
2069 // Need a device entry-point, so gpu can use a mesh
2070 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2071}
2072
reed@google.comf67e4cf2011-03-15 20:56:58 +00002073class SkDeviceFilteredPaint {
2074public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002075 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2076 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002077 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002078 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002079 newPaint->setFlags(flags.fFlags);
2080 newPaint->setHinting(flags.fHinting);
2081 fPaint = newPaint;
2082 } else {
2083 fPaint = &paint;
2084 }
2085 }
2086
reed@google.comf67e4cf2011-03-15 20:56:58 +00002087 const SkPaint& paint() const { return *fPaint; }
2088
2089private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002090 const SkPaint* fPaint;
2091 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002092};
2093
bungeman@google.com52c748b2011-08-22 21:30:43 +00002094void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2095 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002096 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002097 draw.fDevice->drawRect(draw, r, paint);
2098 } else {
2099 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002100 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002101 draw.fDevice->drawRect(draw, r, p);
2102 }
2103}
2104
2105void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2106 const char text[], size_t byteLength,
2107 SkScalar x, SkScalar y) {
2108 SkASSERT(byteLength == 0 || text != NULL);
2109
2110 // nothing to draw
2111 if (text == NULL || byteLength == 0 ||
2112 draw.fClip->isEmpty() ||
2113 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2114 return;
2115 }
2116
2117 SkScalar width = 0;
2118 SkPoint start;
2119
2120 start.set(0, 0); // to avoid warning
2121 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2122 SkPaint::kStrikeThruText_Flag)) {
2123 width = paint.measureText(text, byteLength);
2124
2125 SkScalar offsetX = 0;
2126 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2127 offsetX = SkScalarHalf(width);
2128 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2129 offsetX = width;
2130 }
2131 start.set(x - offsetX, y);
2132 }
2133
2134 if (0 == width) {
2135 return;
2136 }
2137
2138 uint32_t flags = paint.getFlags();
2139
2140 if (flags & (SkPaint::kUnderlineText_Flag |
2141 SkPaint::kStrikeThruText_Flag)) {
2142 SkScalar textSize = paint.getTextSize();
2143 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2144 SkRect r;
2145
2146 r.fLeft = start.fX;
2147 r.fRight = start.fX + width;
2148
2149 if (flags & SkPaint::kUnderlineText_Flag) {
2150 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2151 start.fY);
2152 r.fTop = offset;
2153 r.fBottom = offset + height;
2154 DrawRect(draw, paint, r, textSize);
2155 }
2156 if (flags & SkPaint::kStrikeThruText_Flag) {
2157 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2158 start.fY);
2159 r.fTop = offset;
2160 r.fBottom = offset + height;
2161 DrawRect(draw, paint, r, textSize);
2162 }
2163 }
2164}
2165
reed@google.come0d9ce82014-04-23 04:00:17 +00002166void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2167 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002168 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002169
2170 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002171 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002172 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002173 DrawTextDecorations(iter, dfp.paint(),
2174 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002175 }
2176
reed@google.com4e2b3d32011-04-07 14:18:59 +00002177 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178}
2179
reed@google.come0d9ce82014-04-23 04:00:17 +00002180void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2181 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002182 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002183
reed@android.com8a1c16f2008-12-17 15:59:43 +00002184 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002185 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002187 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002189
reed@google.com4e2b3d32011-04-07 14:18:59 +00002190 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191}
2192
reed@google.come0d9ce82014-04-23 04:00:17 +00002193void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2194 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002195 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002196
reed@android.com8a1c16f2008-12-17 15:59:43 +00002197 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002198 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002199 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002200 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002202
reed@google.com4e2b3d32011-04-07 14:18:59 +00002203 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002204}
2205
reed@google.come0d9ce82014-04-23 04:00:17 +00002206void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2207 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002208 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002209
reed@android.com8a1c16f2008-12-17 15:59:43 +00002210 while (iter.next()) {
2211 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002212 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002213 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002214
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002215 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002216}
2217
fmalita00d5c2c2014-08-21 08:53:26 -07002218void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2219 const SkPaint& paint) {
fmalitaaa1b9122014-08-28 14:32:24 -07002220 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002221
fmalitaaa1b9122014-08-28 14:32:24 -07002222 while (iter.next()) {
2223 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2224 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002225 }
2226
fmalitaaa1b9122014-08-28 14:32:24 -07002227 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002228}
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}
fmalita00d5c2c2014-08-21 08:53:26 -07002247void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2248 const SkPaint& paint) {
2249 if (NULL != blob) {
2250 this->onDrawTextBlob(blob, x, y, paint);
2251 }
2252}
reed@google.come0d9ce82014-04-23 04:00:17 +00002253
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2255 const SkPoint verts[], const SkPoint texs[],
2256 const SkColor colors[], SkXfermode* xmode,
2257 const uint16_t indices[], int indexCount,
2258 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002259 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002260
reed@android.com8a1c16f2008-12-17 15:59:43 +00002261 while (iter.next()) {
2262 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002263 colors, xmode, indices, indexCount,
2264 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002265 }
reed@google.com4b226022011-01-11 18:32:13 +00002266
reed@google.com4e2b3d32011-04-07 14:18:59 +00002267 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268}
2269
dandovb3c9d1c2014-08-12 08:34:29 -07002270void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2271 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2272 if (NULL == cubics) {
2273 return;
2274 }
mtklein6cfa73a2014-08-13 13:33:49 -07002275
dandovecfff212014-08-04 10:02:00 -07002276 // Since a patch is always within the convex hull of the control points, we discard it when its
2277 // bounding rectangle is completely outside the current clip.
2278 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002279 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002280 if (this->quickReject(bounds)) {
2281 return;
2282 }
mtklein6cfa73a2014-08-13 13:33:49 -07002283
dandovb3c9d1c2014-08-12 08:34:29 -07002284 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2285}
2286
2287void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2288 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2289
dandovecfff212014-08-04 10:02:00 -07002290 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002291
dandovecfff212014-08-04 10:02:00 -07002292 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002293 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002294 }
mtklein6cfa73a2014-08-13 13:33:49 -07002295
dandovecfff212014-08-04 10:02:00 -07002296 LOOPER_END
2297}
2298
reed@android.com8a1c16f2008-12-17 15:59:43 +00002299//////////////////////////////////////////////////////////////////////////////
2300// These methods are NOT virtual, and therefore must call back into virtual
2301// methods, rather than actually drawing themselves.
2302//////////////////////////////////////////////////////////////////////////////
2303
2304void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002305 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306 SkPaint paint;
2307
2308 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002309 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002310 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002311 }
2312 this->drawPaint(paint);
2313}
2314
reed@android.com845fdac2009-06-23 03:01:32 +00002315void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002316 SkPaint paint;
2317
2318 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002319 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002320 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002321 }
2322 this->drawPaint(paint);
2323}
2324
2325void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2326 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002327
reed@android.com8a1c16f2008-12-17 15:59:43 +00002328 pt.set(x, y);
2329 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2330}
2331
2332void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2333 SkPoint pt;
2334 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002335
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 pt.set(x, y);
2337 paint.setColor(color);
2338 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2339}
2340
2341void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2342 const SkPaint& paint) {
2343 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002344
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345 pts[0].set(x0, y0);
2346 pts[1].set(x1, y1);
2347 this->drawPoints(kLines_PointMode, 2, pts, paint);
2348}
2349
2350void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2351 SkScalar right, SkScalar bottom,
2352 const SkPaint& paint) {
2353 SkRect r;
2354
2355 r.set(left, top, right, bottom);
2356 this->drawRect(r, paint);
2357}
2358
2359void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2360 const SkPaint& paint) {
2361 if (radius < 0) {
2362 radius = 0;
2363 }
2364
2365 SkRect r;
2366 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002367 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002368}
2369
2370void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2371 const SkPaint& paint) {
2372 if (rx > 0 && ry > 0) {
2373 if (paint.canComputeFastBounds()) {
2374 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002375 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376 return;
2377 }
2378 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002379 SkRRect rrect;
2380 rrect.setRectXY(r, rx, ry);
2381 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002382 } else {
2383 this->drawRect(r, paint);
2384 }
2385}
2386
reed@android.com8a1c16f2008-12-17 15:59:43 +00002387void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2388 SkScalar sweepAngle, bool useCenter,
2389 const SkPaint& paint) {
2390 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2391 this->drawOval(oval, paint);
2392 } else {
2393 SkPath path;
2394 if (useCenter) {
2395 path.moveTo(oval.centerX(), oval.centerY());
2396 }
2397 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2398 if (useCenter) {
2399 path.close();
2400 }
2401 this->drawPath(path, paint);
2402 }
2403}
2404
2405void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2406 const SkPath& path, SkScalar hOffset,
2407 SkScalar vOffset, const SkPaint& paint) {
2408 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002409
reed@android.com8a1c16f2008-12-17 15:59:43 +00002410 matrix.setTranslate(hOffset, vOffset);
2411 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2412}
2413
reed@android.comf76bacf2009-05-13 14:00:33 +00002414///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002415void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002416 SkBaseDevice* device = this->getDevice();
2417 if (NULL != device) {
2418 device->EXPERIMENTAL_optimize(picture);
2419 }
2420}
reed@android.comf76bacf2009-05-13 14:00:33 +00002421
robertphillips9b14f262014-06-04 05:40:44 -07002422void SkCanvas::drawPicture(const SkPicture* picture) {
2423 if (NULL != picture) {
reedd5fa1a42014-08-09 11:08:05 -07002424 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002425 }
2426}
2427
reedd5fa1a42014-08-09 11:08:05 -07002428void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
2429 if (NULL != picture) {
2430 if (matrix && matrix->isIdentity()) {
2431 matrix = NULL;
2432 }
2433 this->onDrawPicture(picture, matrix, paint);
2434 }
2435}
robertphillips9b14f262014-06-04 05:40:44 -07002436
reedd5fa1a42014-08-09 11:08:05 -07002437void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2438 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002439 SkBaseDevice* device = this->getTopDevice();
2440 if (NULL != device) {
2441 // Canvas has to first give the device the opportunity to render
2442 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002443 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002444 return; // the device has rendered the entire picture
2445 }
2446 }
2447
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002448 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002449
robertphillips9b14f262014-06-04 05:40:44 -07002450 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451}
2452
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453///////////////////////////////////////////////////////////////////////////////
2454///////////////////////////////////////////////////////////////////////////////
2455
2456SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002457 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002458
2459 SkASSERT(canvas);
2460
2461 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2462 fDone = !fImpl->next();
2463}
2464
2465SkCanvas::LayerIter::~LayerIter() {
2466 fImpl->~SkDrawIter();
2467}
2468
2469void SkCanvas::LayerIter::next() {
2470 fDone = !fImpl->next();
2471}
2472
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002473SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002474 return fImpl->getDevice();
2475}
2476
2477const SkMatrix& SkCanvas::LayerIter::matrix() const {
2478 return fImpl->getMatrix();
2479}
2480
2481const SkPaint& SkCanvas::LayerIter::paint() const {
2482 const SkPaint* paint = fImpl->getPaint();
2483 if (NULL == paint) {
2484 paint = &fDefaultPaint;
2485 }
2486 return *paint;
2487}
2488
2489const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2490int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2491int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002492
2493///////////////////////////////////////////////////////////////////////////////
2494
fmalitac3b589a2014-06-05 12:40:07 -07002495SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002496
2497///////////////////////////////////////////////////////////////////////////////
2498
2499static bool supported_for_raster_canvas(const SkImageInfo& info) {
2500 switch (info.alphaType()) {
2501 case kPremul_SkAlphaType:
2502 case kOpaque_SkAlphaType:
2503 break;
2504 default:
2505 return false;
2506 }
2507
2508 switch (info.colorType()) {
2509 case kAlpha_8_SkColorType:
2510 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002511 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002512 break;
2513 default:
2514 return false;
2515 }
2516
2517 return true;
2518}
2519
2520SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2521 if (!supported_for_raster_canvas(info)) {
2522 return NULL;
2523 }
2524
2525 SkBitmap bitmap;
2526 if (!bitmap.allocPixels(info)) {
2527 return NULL;
2528 }
2529
2530 // should this functionality be moved into allocPixels()?
2531 if (!bitmap.info().isOpaque()) {
2532 bitmap.eraseColor(0);
2533 }
2534 return SkNEW_ARGS(SkCanvas, (bitmap));
2535}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002536
2537SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2538 if (!supported_for_raster_canvas(info)) {
2539 return NULL;
2540 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002541
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002542 SkBitmap bitmap;
2543 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2544 return NULL;
2545 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002546 return SkNEW_ARGS(SkCanvas, (bitmap));
2547}
reedd5fa1a42014-08-09 11:08:05 -07002548
2549///////////////////////////////////////////////////////////////////////////////
2550
2551SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002552 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002553 : fCanvas(canvas)
2554 , fSaveCount(canvas->getSaveCount())
2555{
2556 if (NULL != paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002557 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002558 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002559 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002560 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002561 canvas->saveLayer(&newBounds, paint);
reedd5fa1a42014-08-09 11:08:05 -07002562 } else if (NULL != matrix) {
2563 canvas->save();
2564 }
mtklein6cfa73a2014-08-13 13:33:49 -07002565
reedd5fa1a42014-08-09 11:08:05 -07002566 if (NULL != matrix) {
2567 canvas->concat(*matrix);
2568 }
2569}
2570
2571SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2572 fCanvas->restoreToCount(fSaveCount);
2573}