blob: 7e2609b22d133c60c3ca2ab06fc2fb2f7dc9708b [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
reed9f014712014-06-18 15:51:20 -07008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000012#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000016#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000017#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070018#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000022#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000059typedef SkTLazy<SkPaint> SkLazyPaint;
60
reed@google.com97af1a62012-08-28 12:19:02 +000061void SkCanvas::predrawNotify() {
62 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000063 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000064 }
65}
66
reed@android.com8a1c16f2008-12-17 15:59:43 +000067///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000068
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000069/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 The clip/matrix/proc are fields that reflect the top of the save/restore
71 stack. Whenever the canvas changes, it marks a dirty flag, and then before
72 these are used (assuming we're not on a layer) we rebuild these cache
73 values: they reflect the top of the save stack, but translated and clipped
74 by the device's XY offset and bitmap-bounds.
75*/
76struct DeviceCM {
77 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000078 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000079 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000081 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000082
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000083 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 : fNext(NULL) {
85 if (NULL != device) {
86 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000087 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 }
reed@google.com4b226022011-01-11 18:32:13 +000089 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000092
bungeman@google.com88edf1e2011-08-08 19:41:56 +000093 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000095 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 fDevice->unref();
97 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000098 SkDELETE(fPaint);
99 }
reed@google.com4b226022011-01-11 18:32:13 +0000100
reed@google.com045e62d2011-10-24 12:19:46 +0000101 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
102 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000103 int x = fDevice->getOrigin().x();
104 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 int width = fDevice->width();
106 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000107
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108 if ((x | y) == 0) {
109 fMatrix = &totalMatrix;
110 fClip = totalClip;
111 } else {
112 fMatrixStorage = totalMatrix;
113 fMatrixStorage.postTranslate(SkIntToScalar(-x),
114 SkIntToScalar(-y));
115 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 totalClip.translate(-x, -y, &fClip);
118 }
119
reed@google.com045e62d2011-10-24 12:19:46 +0000120 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121
122 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000125 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126 SkRegion::kDifference_Op);
127 }
reed@google.com4b226022011-01-11 18:32:13 +0000128
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000129 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
130
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131#ifdef SK_DEBUG
132 if (!fClip.isEmpty()) {
133 SkIRect deviceR;
134 deviceR.set(0, 0, width, height);
135 SkASSERT(deviceR.contains(fClip.getBounds()));
136 }
137#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000138 }
139
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000141 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142};
143
144/* This is the record we keep for each save/restore level in the stack.
145 Since a level optionally copies the matrix and/or stack, we have pointers
146 for these fields. If the value is copied for this level, the copy is
147 stored in the ...Storage field, and the pointer points to that. If the
148 value is not copied for this level, we ignore ...Storage, and just point
149 at the corresponding value in the previous level in the stack.
150*/
151class SkCanvas::MCRec {
152public:
reed1f836ee2014-07-07 07:49:34 -0700153 SkMatrix fMatrix;
154 SkRasterClip fRasterClip;
155 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000156
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157 DeviceCM* fLayer;
158 /* If there are any layers in the stack, this points to the top-most
159 one that is at or below this level in the stack (so we know what
160 bitmap/device to draw into from this level. This value is NOT
161 reference counted, since the real owner is either our fLayer field,
162 or a previous one in a lower level.)
163 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000164 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
Florin Malita5f6102d2014-06-30 10:13:28 -0400166 MCRec(const MCRec* prev) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 if (NULL != prev) {
reed1f836ee2014-07-07 07:49:34 -0700168 fMatrix = prev->fMatrix;
169 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170
171 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000172 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173
174 fTopLayer = prev->fTopLayer;
175 } else { // no prev
reed1f836ee2014-07-07 07:49:34 -0700176 fMatrix.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 fFilter = NULL;
178 fTopLayer = NULL;
179 }
180 fLayer = NULL;
181
182 // don't bother initializing fNext
183 inc_rec();
184 }
185 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000186 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187 SkDELETE(fLayer);
188 dec_rec();
189 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190};
191
192class SkDrawIter : public SkDraw {
193public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000194 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000195 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000196 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 canvas->updateDeviceCMCache();
198
reed@google.com90c07ea2012-04-13 13:50:27 +0000199 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000201 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 }
reed@google.com4b226022011-01-11 18:32:13 +0000203
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 bool next() {
205 // skip over recs with empty clips
206 if (fSkipEmptyClips) {
207 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
208 fCurrLayer = fCurrLayer->fNext;
209 }
210 }
211
reed@google.comf68c5e22012-02-24 16:38:58 +0000212 const DeviceCM* rec = fCurrLayer;
213 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214
215 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000216 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
217 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 fDevice = rec->fDevice;
219 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000221 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222
223 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000225
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 return true;
227 }
228 return false;
229 }
reed@google.com4b226022011-01-11 18:32:13 +0000230
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000231 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000232 int getX() const { return fDevice->getOrigin().x(); }
233 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 const SkMatrix& getMatrix() const { return *fMatrix; }
235 const SkRegion& getClip() const { return *fClip; }
236 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238private:
239 SkCanvas* fCanvas;
240 const DeviceCM* fCurrLayer;
241 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 SkBool8 fSkipEmptyClips;
243
244 typedef SkDraw INHERITED;
245};
246
247/////////////////////////////////////////////////////////////////////////////
248
249class AutoDrawLooper {
250public:
reed@google.com8926b162012-03-23 15:36:36 +0000251 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000252 bool skipLayerForImageFilter = false,
253 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000254 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000256 fPaint = NULL;
257 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000258 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000259 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260
reed@google.com8926b162012-03-23 15:36:36 +0000261 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
262 SkPaint tmp;
263 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000264 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
265 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000266 // we'll clear the imageFilter for the actual draws in next(), so
267 // it will only be applied during the restore().
268 fDoClearImageFilter = true;
269 }
270
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000271 if (SkDrawLooper* looper = paint.getLooper()) {
272 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
273 looper->contextSize());
274 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000275 fIsSimple = false;
276 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000277 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000278 // can we be marked as simple?
279 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000280 }
281 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000282
reed@android.com8a1c16f2008-12-17 15:59:43 +0000283 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000284 if (fDoClearImageFilter) {
285 fCanvas->internalRestore();
286 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000287 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000289
reed@google.com4e2b3d32011-04-07 14:18:59 +0000290 const SkPaint& paint() const {
291 SkASSERT(fPaint);
292 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000293 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000294
reed@google.com129ec222012-05-15 13:24:09 +0000295 bool next(SkDrawFilter::Type drawType) {
296 if (fDone) {
297 return false;
298 } else if (fIsSimple) {
299 fDone = true;
300 fPaint = &fOrigPaint;
301 return !fPaint->nothingToDraw();
302 } else {
303 return this->doNext(drawType);
304 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000305 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000306
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000308 SkLazyPaint fLazyPaint;
309 SkCanvas* fCanvas;
310 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000311 SkDrawFilter* fFilter;
312 const SkPaint* fPaint;
313 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000314 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000315 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000316 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000317 SkDrawLooper::Context* fLooperContext;
318 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000319
320 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321};
322
reed@google.com129ec222012-05-15 13:24:09 +0000323bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000324 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000325 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000326 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000327
328 SkPaint* paint = fLazyPaint.set(fOrigPaint);
329
330 if (fDoClearImageFilter) {
331 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000332 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000333
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000334 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000336 return false;
337 }
338 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000339 if (!fFilter->filter(paint, drawType)) {
340 fDone = true;
341 return false;
342 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000343 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000344 // no looper means we only draw once
345 fDone = true;
346 }
347 }
348 fPaint = paint;
349
350 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000351 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000352 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000353 }
354
355 // call this after any possible paint modifiers
356 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000357 fPaint = NULL;
358 return false;
359 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000360 return true;
361}
362
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363#include "SkColorPriv.h"
364
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365////////// macros to place around the internal draw calls //////////////////
366
reed@google.com8926b162012-03-23 15:36:36 +0000367#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000368 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000369 AutoDrawLooper looper(this, paint, true); \
370 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000371 SkDrawIter iter(this);
372
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000373#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000374 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000375 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000376 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000378
reed@google.com4e2b3d32011-04-07 14:18:59 +0000379#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380
381////////////////////////////////////////////////////////////////////////////
382
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000383SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@google.comc0784db2013-12-13 21:16:12 +0000384 fCachedLocalClipBounds.setEmpty();
385 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000386 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000387 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000388 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000389 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000390 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000391 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000392
393 fMCRec = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400394 new (fMCRec) MCRec(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000396 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000398
reed@google.com97af1a62012-08-28 12:19:02 +0000399 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000400
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000401 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402}
403
reed@google.comcde92112011-07-06 20:00:52 +0000404SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000405 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
406{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000407 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000408
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000409 this->init(NULL);
410}
411
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000412SkCanvas::SkCanvas(int width, int height)
413 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
414{
415 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000416
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000417 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000418 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000419 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
420}
421
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000422SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000423 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
424{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 inc_canvas();
426
427 this->init(device);
428}
429
430SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000431 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
432{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 inc_canvas();
434
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000435 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000436}
437
438SkCanvas::~SkCanvas() {
439 // free up the contents of our deque
440 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000441 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000442
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443 this->internalRestore(); // restore the last, since we're going away
444
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000445 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000446
reed@android.com8a1c16f2008-12-17 15:59:43 +0000447 dec_canvas();
448}
449
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450SkDrawFilter* SkCanvas::getDrawFilter() const {
451 return fMCRec->fFilter;
452}
453
454SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
455 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
456 return filter;
457}
458
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000459SkMetaData& SkCanvas::getMetaData() {
460 // metadata users are rare, so we lazily allocate it. If that changes we
461 // can decide to just make it a field in the device (rather than a ptr)
462 if (NULL == fMetaData) {
463 fMetaData = new SkMetaData;
464 }
465 return *fMetaData;
466}
467
reed@android.com8a1c16f2008-12-17 15:59:43 +0000468///////////////////////////////////////////////////////////////////////////////
469
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000470void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000471 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000472 if (device) {
473 device->flush();
474 }
475}
476
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000477SkISize SkCanvas::getTopLayerSize() const {
478 SkBaseDevice* d = this->getTopDevice();
479 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
480}
481
482SkIPoint SkCanvas::getTopLayerOrigin() const {
483 SkBaseDevice* d = this->getTopDevice();
484 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
485}
486
487SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000488 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000489 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
490}
491
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000492SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000494 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 SkASSERT(rec && rec->fLayer);
496 return rec->fLayer->fDevice;
497}
498
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000499SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000500 if (updateMatrixClip) {
501 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
502 }
reed@google.com9266fed2011-03-30 00:18:03 +0000503 return fMCRec->fTopLayer->fDevice;
504}
505
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000506SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000507 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000508 SkDeque::F2BIter iter(fMCStack);
509 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000511 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512
513 if (rootDevice == device) {
514 return device;
515 }
reed@google.com4b226022011-01-11 18:32:13 +0000516
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000518 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 }
520 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000521 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 }
523
524 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
525 rootDevice = device;
526
527 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 /* Now we update our initial region to have the bounds of the new device,
530 and then intersect all of the clips in our stack with these bounds,
531 to ensure that we can't draw outside of the device's bounds (and trash
532 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 NOTE: this is only a partial-fix, since if the new device is larger than
535 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000536 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
538 reconstruct the correct clips, so this approximation will have to do.
539 The caller really needs to restore() back to the base if they want to
540 accurately take advantage of the new device bounds.
541 */
542
reed@google.com42aea282012-03-28 16:19:15 +0000543 SkIRect bounds;
544 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000545 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000546 } else {
547 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 }
reed@google.com42aea282012-03-28 16:19:15 +0000549 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700550 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000551 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700552 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000553 }
554
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 return device;
556}
557
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000558bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
559 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
560 return false;
561 }
562
563 bool weAllocated = false;
564 if (NULL == bitmap->pixelRef()) {
565 if (!bitmap->allocPixels()) {
566 return false;
567 }
568 weAllocated = true;
569 }
570
571 SkBitmap bm(*bitmap);
572 bm.lockPixels();
573 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
574 return true;
575 }
576
577 if (weAllocated) {
578 bitmap->setPixelRef(NULL);
579 }
580 return false;
581}
reed@google.com51df9e32010-12-23 19:29:18 +0000582
bsalomon@google.comc6980972011-11-02 19:57:21 +0000583bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000584 SkIRect r = srcRect;
585 const SkISize size = this->getBaseLayerSize();
586 if (!r.intersect(0, 0, size.width(), size.height())) {
587 bitmap->reset();
588 return false;
589 }
590
591 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
592 // bitmap will already be reset.
593 return false;
594 }
595 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
596 bitmap->reset();
597 return false;
598 }
599 return true;
600}
601
602bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
603 switch (origInfo.colorType()) {
604 case kUnknown_SkColorType:
605 case kIndex_8_SkColorType:
606 return false;
607 default:
608 break;
609 }
610 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
611 return false;
612 }
613 if (0 == origInfo.width() || 0 == origInfo.height()) {
614 return false;
615 }
616
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000617 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000618 if (!device) {
619 return false;
620 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000621
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000622 const SkISize size = this->getBaseLayerSize();
623 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
624 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000625 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000626 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000627
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000628 SkImageInfo info = origInfo;
629 // the intersect may have shrunk info's logical size
630 info.fWidth = srcR.width();
631 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000632
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000633 // if x or y are negative, then we have to adjust pixels
634 if (x > 0) {
635 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000636 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000637 if (y > 0) {
638 y = 0;
639 }
640 // here x,y are either 0 or negative
641 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000642
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000643 // The device can assert that the requested area is always contained in its bounds
644 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000645}
646
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000647bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
648 if (bitmap.getTexture()) {
649 return false;
650 }
651 SkBitmap bm(bitmap);
652 bm.lockPixels();
653 if (bm.getPixels()) {
654 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
655 }
656 return false;
657}
658
659bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
660 int x, int y) {
661 switch (origInfo.colorType()) {
662 case kUnknown_SkColorType:
663 case kIndex_8_SkColorType:
664 return false;
665 default:
666 break;
667 }
668 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
669 return false;
670 }
671
672 const SkISize size = this->getBaseLayerSize();
673 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
674 if (!target.intersect(0, 0, size.width(), size.height())) {
675 return false;
676 }
677
678 SkBaseDevice* device = this->getDevice();
679 if (!device) {
680 return false;
681 }
682
683 SkImageInfo info = origInfo;
684 // the intersect may have shrunk info's logical size
685 info.fWidth = target.width();
686 info.fHeight = target.height();
687
688 // if x or y are negative, then we have to adjust pixels
689 if (x > 0) {
690 x = 0;
691 }
692 if (y > 0) {
693 y = 0;
694 }
695 // here x,y are either 0 or negative
696 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
697
reed4af35f32014-06-27 17:47:49 -0700698 // Tell our owning surface to bump its generation ID
699 this->predrawNotify();
700
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000701 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000702 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000703}
reed@google.com51df9e32010-12-23 19:29:18 +0000704
junov@google.com4370aed2012-01-18 16:21:08 +0000705SkCanvas* SkCanvas::canvasForDrawIter() {
706 return this;
707}
708
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709//////////////////////////////////////////////////////////////////////////////
710
reed@android.com8a1c16f2008-12-17 15:59:43 +0000711void SkCanvas::updateDeviceCMCache() {
712 if (fDeviceCMDirty) {
713 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700714 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000716
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000718 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000720 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000722 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000723 } while ((layer = layer->fNext) != NULL);
724 }
725 fDeviceCMDirty = false;
726 }
727}
728
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729///////////////////////////////////////////////////////////////////////////////
730
Florin Malita5f6102d2014-06-30 10:13:28 -0400731int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000733
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 MCRec* newTop = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400735 new (newTop) MCRec(fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000736 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000737
Florin Malita5f6102d2014-06-30 10:13:28 -0400738 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000739
reed@android.com8a1c16f2008-12-17 15:59:43 +0000740 return saveCount;
741}
742
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000743int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400744 this->willSave();
745 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746}
747
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000749#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000750 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000751#else
752 return true;
753#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754}
755
junov@chromium.orga907ac32012-02-24 21:54:07 +0000756bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000757 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000758 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000759 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000760 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000761 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000762 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000763
764 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700765 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000766 // Filters may grow the bounds beyond the device bounds.
767 op = SkRegion::kReplace_Op;
768 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000769 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000770 if (NULL != bounds) {
771 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000772
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 this->getTotalMatrix().mapRect(&r, *bounds);
774 r.roundOut(&ir);
775 // early exit if the layer's bounds are clipped out
776 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000777 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700778 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000779 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000780 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781 }
782 } else { // no user bounds, so just use the clip
783 ir = clipBounds;
784 }
785
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000786 if (bounds_affects_clip(flags)) {
787 fClipStack.clipDevRect(ir, op);
788 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700789 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000790 return false;
791 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000792 }
793
794 if (intersection) {
795 *intersection = ir;
796 }
797 return true;
798}
799
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000800int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
801 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
802 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
803}
804
junov@chromium.orga907ac32012-02-24 21:54:07 +0000805int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
806 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000807 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
808 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000809}
810
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000811int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
812 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000813#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000814 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000815#endif
816
junov@chromium.orga907ac32012-02-24 21:54:07 +0000817 // do this before we create the layer. We don't call the public save() since
818 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400819 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000820
821 fDeviceCMDirty = true;
822
823 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000824 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825 return count;
826 }
827
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000828 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
829 // the clipRectBounds() call above?
830 if (kNoLayer_SaveLayerStrategy == strategy) {
831 return count;
832 }
833
reed@google.comb55deeb2012-01-06 14:43:09 +0000834 // Kill the imagefilter if our device doesn't allow it
835 SkLazyPaint lazyP;
836 if (paint && paint->getImageFilter()) {
837 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000838 if (justForImageFilter) {
839 // early exit if the layer was just for the imageFilter
840 return count;
841 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000842 SkPaint* p = lazyP.set(*paint);
843 p->setImageFilter(NULL);
844 paint = p;
845 }
846 }
847
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000848 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
849 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
850 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000852 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000853 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700854 device = this->getDevice();
855 if (device) {
856 device = device->createCompatibleDevice(info);
857 }
reed@google.com76dd2772012-01-05 21:15:07 +0000858 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000859 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000860 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000861 if (NULL == device) {
862 SkDebugf("Unable to create device for layer.");
863 return count;
864 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000865
reed@google.com6f8f2922011-03-04 22:27:10 +0000866 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000867 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000868 device->unref();
869
870 layer->fNext = fMCRec->fTopLayer;
871 fMCRec->fLayer = layer;
872 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
873
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000874 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000875 return count;
876}
877
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000878int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
879 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
880}
881
reed@android.com8a1c16f2008-12-17 15:59:43 +0000882int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
883 SaveFlags flags) {
884 if (0xFF == alpha) {
885 return this->saveLayer(bounds, NULL, flags);
886 } else {
887 SkPaint tmpPaint;
888 tmpPaint.setAlpha(alpha);
889 return this->saveLayer(bounds, &tmpPaint, flags);
890 }
891}
892
893void SkCanvas::restore() {
894 // check for underflow
895 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000896 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000897 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700898 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000899 }
900}
901
902void SkCanvas::internalRestore() {
903 SkASSERT(fMCStack.count() != 0);
904
905 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000906 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000907
Florin Malita5f6102d2014-06-30 10:13:28 -0400908 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000909
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000910 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000911 DeviceCM* layer = fMCRec->fLayer; // may be null
912 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
913 fMCRec->fLayer = NULL;
914
915 // now do the normal restore()
916 fMCRec->~MCRec(); // balanced in save()
917 fMCStack.pop_back();
918 fMCRec = (MCRec*)fMCStack.back();
919
920 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
921 since if we're being recorded, we don't want to record this (the
922 recorder will have already recorded the restore).
923 */
924 if (NULL != layer) {
925 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000926 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000927 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
928 layer->fPaint);
929 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000931
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000932 SkASSERT(fSaveLayerCount > 0);
933 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934 }
935 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000936 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937}
938
939int SkCanvas::getSaveCount() const {
940 return fMCStack.count();
941}
942
943void SkCanvas::restoreToCount(int count) {
944 // sanity check
945 if (count < 1) {
946 count = 1;
947 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000948
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000949 int n = this->getSaveCount() - count;
950 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000951 this->restore();
952 }
953}
954
reed@google.com7c202932011-12-14 18:48:05 +0000955bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000956 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000957}
958
reed@google.com76f10a32014-02-05 15:32:21 +0000959SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
960 return this->onNewSurface(info);
961}
962
963SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
964 SkBaseDevice* dev = this->getDevice();
965 return dev ? dev->newSurface(info) : NULL;
966}
967
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000968SkImageInfo SkCanvas::imageInfo() const {
969 SkBaseDevice* dev = this->getDevice();
970 if (dev) {
971 return dev->imageInfo();
972 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000973 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000974 }
975}
976
977const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
978 return this->onPeekPixels(info, rowBytes);
979}
980
981const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
982 SkBaseDevice* dev = this->getDevice();
983 return dev ? dev->peekPixels(info, rowBytes) : NULL;
984}
985
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +0000986void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
987 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
988 if (pixels && origin) {
989 *origin = this->getTopDevice(false)->getOrigin();
990 }
991 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +0000992}
993
994void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
995 SkBaseDevice* dev = this->getTopDevice();
996 return dev ? dev->accessPixels(info, rowBytes) : NULL;
997}
998
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000999SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1000 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1001 if (NULL == fAddr) {
1002 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001003 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001004 return; // failure, fAddr is NULL
1005 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001006 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1007 return; // failure, fAddr is NULL
1008 }
1009 fAddr = fBitmap.getPixels();
1010 fRowBytes = fBitmap.rowBytes();
1011 }
1012 SkASSERT(fAddr); // success
1013}
1014
1015bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1016 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001017 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001018 } else {
1019 bitmap->reset();
1020 return false;
1021 }
1022}
1023
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001024void SkCanvas::onPushCull(const SkRect& cullRect) {
1025 // do nothing. Subclasses may do something
1026}
1027
1028void SkCanvas::onPopCull() {
1029 // do nothing. Subclasses may do something
1030}
1031
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001033#ifdef SK_DEBUG
1034// Ensure that cull rects are monotonically nested in device space.
1035void SkCanvas::validateCull(const SkIRect& devCull) {
1036 if (fCullStack.isEmpty()
1037 || devCull.isEmpty()
1038 || fCullStack.top().contains(devCull)) {
1039 return;
1040 }
1041
1042 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1043 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1044 fCullStack.top().x(), fCullStack.top().y(),
1045 fCullStack.top().right(), fCullStack.top().bottom()));
1046
1047#ifdef ASSERT_NESTED_CULLING
1048 SkDEBUGFAIL("Invalid cull.");
1049#endif
1050}
1051#endif
1052
1053void SkCanvas::pushCull(const SkRect& cullRect) {
1054 ++fCullCount;
1055 this->onPushCull(cullRect);
1056
1057#ifdef SK_DEBUG
1058 // Map the cull rect into device space.
1059 SkRect mappedCull;
1060 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1061
1062 // Take clipping into account.
1063 SkIRect devClip, devCull;
1064 mappedCull.roundOut(&devCull);
1065 this->getClipDeviceBounds(&devClip);
1066 if (!devCull.intersect(devClip)) {
1067 devCull.setEmpty();
1068 }
1069
1070 this->validateCull(devCull);
1071 fCullStack.push(devCull); // balanced in popCull
1072#endif
1073}
1074
1075void SkCanvas::popCull() {
1076 SkASSERT(fCullStack.count() == fCullCount);
1077
1078 if (fCullCount > 0) {
1079 --fCullCount;
1080 this->onPopCull();
1081
1082 SkDEBUGCODE(fCullStack.pop());
1083 }
1084}
1085
1086/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001088void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001090 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001091 return;
1092 }
1093
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001094 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001096 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001097 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001098
1099 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001100
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001101 SkRect storage;
1102 const SkRect* bounds = NULL;
1103 if (paint && paint->canComputeFastBounds()) {
1104 bitmap.getBounds(&storage);
1105 matrix.mapRect(&storage);
1106 bounds = &paint->computeFastBounds(storage, &storage);
1107 }
1108
1109 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001110
1111 while (iter.next()) {
1112 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1113 }
1114
1115 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116}
1117
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001118void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001119 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001120 SkPaint tmp;
1121 if (NULL == paint) {
1122 tmp.setDither(true);
1123 paint = &tmp;
1124 }
reed@google.com4b226022011-01-11 18:32:13 +00001125
reed@google.com8926b162012-03-23 15:36:36 +00001126 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001128 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001129 paint = &looper.paint();
1130 SkImageFilter* filter = paint->getImageFilter();
1131 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001132 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001133 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001134 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001135 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001136 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001137 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001138 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001139 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001140 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001141 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001142 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001143 SkPaint tmpUnfiltered(*paint);
1144 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001145 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1146 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001147 }
1148 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001149 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001150 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001152 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001153}
1154
reed@google.com8926b162012-03-23 15:36:36 +00001155void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1156 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001157 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001158 return;
1159 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001160 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001161
reed@google.com8926b162012-03-23 15:36:36 +00001162 SkPaint tmp;
1163 if (NULL == paint) {
1164 paint = &tmp;
1165 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001166
reed@google.com8926b162012-03-23 15:36:36 +00001167 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001168
reed@google.com8926b162012-03-23 15:36:36 +00001169 while (iter.next()) {
1170 paint = &looper.paint();
1171 SkImageFilter* filter = paint->getImageFilter();
1172 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1173 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001174 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001175 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001176 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001177 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001178 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001179 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001180 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001181 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001182 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001183 SkPaint tmpUnfiltered(*paint);
1184 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001185 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001186 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001187 }
1188 } else {
1189 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1190 }
1191 }
1192 LOOPER_END
1193}
1194
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001196void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001197 SkMatrix m;
1198 m.setTranslate(dx, dy);
1199 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200}
1201
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001202void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001203 SkMatrix m;
1204 m.setScale(sx, sy);
1205 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001206}
1207
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001208void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001209 SkMatrix m;
1210 m.setRotate(degrees);
1211 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212}
1213
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001214void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001215 SkMatrix m;
1216 m.setSkew(sx, sy);
1217 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001218}
1219
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001220void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001221 if (matrix.isIdentity()) {
1222 return;
1223 }
1224
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001226 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001227 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001228
1229 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001230}
1231
reed@android.com8a1c16f2008-12-17 15:59:43 +00001232void SkCanvas::setMatrix(const SkMatrix& matrix) {
1233 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001234 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001235 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001236 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237}
1238
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239void SkCanvas::resetMatrix() {
1240 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001241
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242 matrix.reset();
1243 this->setMatrix(matrix);
1244}
1245
1246//////////////////////////////////////////////////////////////////////////////
1247
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001248void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001249 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1250 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001251}
1252
1253void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001254#ifdef SK_ENABLE_CLIP_QUICKREJECT
1255 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001256 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001257 return false;
1258 }
1259
reed@google.com3b3e8952012-08-16 20:53:31 +00001260 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001261 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001262 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001263
1264 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001265 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001266 }
1267 }
1268#endif
1269
reed@google.com5c3d1472011-02-22 19:12:23 +00001270 AutoValidateClip avc(this);
1271
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001273 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001274 if (!fAllowSoftClip) {
1275 edgeStyle = kHard_ClipEdgeStyle;
1276 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277
reed1f836ee2014-07-07 07:49:34 -07001278 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001279 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001280 // the matrix. This means we don't have to a) make a path, and b) tell
1281 // the region code to scan-convert the path, only to discover that it
1282 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284
reed1f836ee2014-07-07 07:49:34 -07001285 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001286 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed1f836ee2014-07-07 07:49:34 -07001287 fMCRec->fRasterClip.op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001289 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001290 // and clip against that, since it can handle any matrix. However, to
1291 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1292 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293 SkPath path;
1294
1295 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001296 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 }
1298}
1299
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001300static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1301 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001302 // base is used to limit the size (and therefore memory allocation) of the
1303 // region that results from scan converting devPath.
1304 SkRegion base;
1305
reed@google.com819c9212011-02-23 18:56:55 +00001306 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001307 // since we are intersect, we can do better (tighter) with currRgn's
1308 // bounds, than just using the device. However, if currRgn is complex,
1309 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001310 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001311 // FIXME: we should also be able to do this when currClip->isBW(),
1312 // but relaxing the test above triggers GM asserts in
1313 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001314 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001315 } else {
reed@google.com00177082011-10-12 14:34:30 +00001316 base.setRect(currClip->getBounds());
1317 SkRasterClip clip;
1318 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001319 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001320 }
reed@google.com819c9212011-02-23 18:56:55 +00001321 } else {
reed52d9ac62014-06-30 09:05:34 -07001322 const SkISize size = canvas->getBaseLayerSize();
1323 base.setRect(0, 0, size.width(), size.height());
reed@google.com819c9212011-02-23 18:56:55 +00001324
1325 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001326 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001327 } else {
reed@google.com00177082011-10-12 14:34:30 +00001328 SkRasterClip clip;
1329 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001330 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001331 }
1332 }
1333}
1334
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001335void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001336 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001337 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001338 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1339 } else {
1340 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001341 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001342}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001343
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001344void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001345 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001346 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001347 AutoValidateClip avc(this);
1348
1349 fDeviceCMDirty = true;
1350 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001351 if (!fAllowSoftClip) {
1352 edgeStyle = kHard_ClipEdgeStyle;
1353 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001354
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001355 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001356
1357 SkPath devPath;
1358 devPath.addRRect(transformedRRect);
1359
reed1f836ee2014-07-07 07:49:34 -07001360 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001361 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001362 }
1363
1364 SkPath path;
1365 path.addRRect(rrect);
1366 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001367 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001368}
1369
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001370void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001371 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1372 SkRect r;
1373 if (!path.isInverseFillType() && path.isRect(&r)) {
1374 this->onClipRect(r, op, edgeStyle);
1375 } else {
1376 this->onClipPath(path, op, edgeStyle);
1377 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001378}
1379
1380void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001381#ifdef SK_ENABLE_CLIP_QUICKREJECT
1382 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001383 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001384 return false;
1385 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001386
reed@google.com3b3e8952012-08-16 20:53:31 +00001387 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001388 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001389 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001390
reed@google.comda17f752012-08-16 18:27:05 +00001391 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001392 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001393 }
1394 }
1395#endif
1396
reed@google.com5c3d1472011-02-22 19:12:23 +00001397 AutoValidateClip avc(this);
1398
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001400 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001401 if (!fAllowSoftClip) {
1402 edgeStyle = kHard_ClipEdgeStyle;
1403 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404
1405 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001406 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407
reed@google.comfe701122011-11-08 19:41:23 +00001408 // Check if the transfomation, or the original path itself
1409 // made us empty. Note this can also happen if we contained NaN
1410 // values. computing the bounds detects this, and will set our
1411 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1412 if (devPath.getBounds().isEmpty()) {
1413 // resetting the path will remove any NaN or other wanky values
1414 // that might upset our scan converter.
1415 devPath.reset();
1416 }
1417
reed@google.com5c3d1472011-02-22 19:12:23 +00001418 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001419 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001420
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001421 if (fAllowSimplifyClip) {
1422 devPath.reset();
1423 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1424 const SkClipStack* clipStack = getClipStack();
1425 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1426 const SkClipStack::Element* element;
1427 while ((element = iter.next())) {
1428 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001429 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001430 if (type != SkClipStack::Element::kEmpty_Type) {
1431 element->asPath(&operand);
1432 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001433 SkRegion::Op elementOp = element->getOp();
1434 if (elementOp == SkRegion::kReplace_Op) {
1435 devPath = operand;
1436 } else {
1437 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1438 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001439 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1440 // perhaps we need an API change to avoid this sort of mixed-signals about
1441 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001442 if (element->isAA()) {
1443 edgeStyle = kSoft_ClipEdgeStyle;
1444 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001445 }
1446 op = SkRegion::kReplace_Op;
1447 }
1448
reed1f836ee2014-07-07 07:49:34 -07001449 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450}
1451
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001452void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001453 bool inverseFilled) {
1454 // This is for updating the clip conservatively using only bounds
1455 // information.
1456 // Contract:
1457 // The current clip must contain the true clip. The true
1458 // clip is the clip that would have normally been computed
1459 // by calls to clipPath and clipRRect
1460 // Objective:
1461 // Keep the current clip as small as possible without
1462 // breaking the contract, using only clip bounding rectangles
1463 // (for performance).
1464
1465 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1466 // don't have to worry about getting caught in a loop. Thus anywhere
1467 // we call a virtual method, we explicitly prefix it with
1468 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001469
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001470 if (inverseFilled) {
1471 switch (op) {
1472 case SkRegion::kIntersect_Op:
1473 case SkRegion::kDifference_Op:
1474 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001475 // the clip unchanged conservatively respects the contract.
1476 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001477 case SkRegion::kUnion_Op:
1478 case SkRegion::kReplace_Op:
1479 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001480 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001481 // These ops can grow the current clip up to the extents of
1482 // the input clip, which is inverse filled, so we just set
1483 // the current clip to the device bounds.
1484 SkRect deviceBounds;
1485 SkIRect deviceIBounds;
1486 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001487 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001488
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001489 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001490 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001491 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001492 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001493 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001494 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001495 break;
1496 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001497 default:
1498 SkASSERT(0); // unhandled op?
1499 }
1500 } else {
1501 // Not inverse filled
1502 switch (op) {
1503 case SkRegion::kIntersect_Op:
1504 case SkRegion::kUnion_Op:
1505 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001506 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1507 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001508 case SkRegion::kDifference_Op:
1509 // Difference can only shrink the current clip.
1510 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001511 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001512 case SkRegion::kReverseDifference_Op:
1513 // To reverse, we swap in the bounds with a replace op.
1514 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001515 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1516 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001517 case SkRegion::kXOR_Op:
1518 // Be conservative, based on (A XOR B) always included in (A union B),
1519 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001520 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1521 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001522 default:
1523 SkASSERT(0); // unhandled op?
1524 }
1525 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001526}
1527
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001528void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001529 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001530}
1531
1532void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001533 AutoValidateClip avc(this);
1534
reed@android.com8a1c16f2008-12-17 15:59:43 +00001535 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001536 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001537
reed@google.com5c3d1472011-02-22 19:12:23 +00001538 // todo: signal fClipStack that we have a region, and therefore (I guess)
1539 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001540 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001541
reed1f836ee2014-07-07 07:49:34 -07001542 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001543}
1544
reed@google.com819c9212011-02-23 18:56:55 +00001545#ifdef SK_DEBUG
1546void SkCanvas::validateClip() const {
1547 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001548 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001549 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001550 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001551 return;
1552 }
1553
reed@google.com819c9212011-02-23 18:56:55 +00001554 SkIRect ir;
1555 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001556 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001557
robertphillips@google.com80214e22012-07-20 15:33:18 +00001558 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001559 const SkClipStack::Element* element;
1560 while ((element = iter.next()) != NULL) {
1561 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001562 case SkClipStack::Element::kRect_Type:
1563 element->getRect().round(&ir);
1564 tmpClip.op(ir, element->getOp());
1565 break;
1566 case SkClipStack::Element::kEmpty_Type:
1567 tmpClip.setEmpty();
1568 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001569 default: {
1570 SkPath path;
1571 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001572 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001573 break;
1574 }
reed@google.com819c9212011-02-23 18:56:55 +00001575 }
1576 }
reed@google.com819c9212011-02-23 18:56:55 +00001577}
1578#endif
1579
reed@google.com90c07ea2012-04-13 13:50:27 +00001580void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001581 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001582 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001583
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001584 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001585 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001586 }
1587}
1588
reed@google.com5c3d1472011-02-22 19:12:23 +00001589///////////////////////////////////////////////////////////////////////////////
1590
reed@google.com754de5f2014-02-24 19:38:20 +00001591bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001592 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001593}
1594
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001595bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001596 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001597}
1598
reed@google.com3b3e8952012-08-16 20:53:31 +00001599bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001600
reed@google.com16078632011-12-06 18:56:37 +00001601 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001602 return true;
1603
reed1f836ee2014-07-07 07:49:34 -07001604 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605 return true;
1606 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607
reed1f836ee2014-07-07 07:49:34 -07001608 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001609 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001610 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001611 SkIRect idst;
1612 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001613 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001614 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001615 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001616
reed@android.coma380ae42009-07-21 01:17:02 +00001617 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001618 // TODO: should we use | instead, or compare all 4 at once?
1619 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001620 return true;
1621 }
reed@google.comc0784db2013-12-13 21:16:12 +00001622 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001623 return true;
1624 }
1625 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627}
1628
reed@google.com3b3e8952012-08-16 20:53:31 +00001629bool SkCanvas::quickReject(const SkPath& path) const {
1630 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001631}
1632
reed@google.com3b3e8952012-08-16 20:53:31 +00001633bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001634 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001635 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636 return false;
1637 }
1638
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001639 SkMatrix inverse;
1640 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001641 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001642 if (bounds) {
1643 bounds->setEmpty();
1644 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001645 return false;
1646 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001647
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001648 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001649 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001650 // adjust it outwards in case we are antialiasing
1651 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001652
reed@google.com8f4d2302013-12-17 16:44:46 +00001653 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1654 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655 inverse.mapRect(bounds, r);
1656 }
1657 return true;
1658}
1659
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001660bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001661 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001662 if (clip.isEmpty()) {
1663 if (bounds) {
1664 bounds->setEmpty();
1665 }
1666 return false;
1667 }
1668
1669 if (NULL != bounds) {
1670 *bounds = clip.getBounds();
1671 }
1672 return true;
1673}
1674
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001676 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677}
1678
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001679#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001680SkCanvas::ClipType SkCanvas::getClipType() const {
reed1f836ee2014-07-07 07:49:34 -07001681 if (fMCRec->fRasterClip.isEmpty()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001682 return kEmpty_ClipType;
1683 }
reed1f836ee2014-07-07 07:49:34 -07001684 if (fMCRec->fRasterClip.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001685 return kRect_ClipType;
1686 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001687 return kComplex_ClipType;
1688}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001689#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001690
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001691const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001692 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001693}
1694
1695void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1696 path->reset();
1697
reed1f836ee2014-07-07 07:49:34 -07001698 const SkRegion& rgn = fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001699 if (rgn.isEmpty()) {
1700 return;
1701 }
1702 (void)rgn.getBoundaryPath(path);
1703}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001704
reed@google.com9c135db2014-03-12 18:28:35 +00001705GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1706 SkBaseDevice* dev = this->getTopDevice();
1707 return dev ? dev->accessRenderTarget() : NULL;
1708}
1709
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001710SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001711 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001712 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713}
1714
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001715GrContext* SkCanvas::getGrContext() {
1716#if SK_SUPPORT_GPU
1717 SkBaseDevice* device = this->getTopDevice();
1718 if (NULL != device) {
1719 GrRenderTarget* renderTarget = device->accessRenderTarget();
1720 if (NULL != renderTarget) {
1721 return renderTarget->getContext();
1722 }
1723 }
1724#endif
1725
1726 return NULL;
1727
1728}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001729
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001730void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1731 const SkPaint& paint) {
1732 if (outer.isEmpty()) {
1733 return;
1734 }
1735 if (inner.isEmpty()) {
1736 this->drawRRect(outer, paint);
1737 return;
1738 }
1739
1740 // We don't have this method (yet), but technically this is what we should
1741 // be able to assert...
1742 // SkASSERT(outer.contains(inner));
1743 //
1744 // For now at least check for containment of bounds
1745 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1746
1747 this->onDrawDRRect(outer, inner, paint);
1748}
1749
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750//////////////////////////////////////////////////////////////////////////////
1751// These are the virtual drawing methods
1752//////////////////////////////////////////////////////////////////////////////
1753
reed@google.com2a981812011-04-14 18:59:28 +00001754void SkCanvas::clear(SkColor color) {
1755 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001756 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001757 while (iter.next()) {
1758 iter.fDevice->clear(color);
1759 }
1760}
1761
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001762void SkCanvas::onDiscard() {
1763 if (NULL != fSurfaceBase) {
1764 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1765 }
1766}
1767
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001769 this->internalDrawPaint(paint);
1770}
1771
1772void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001773 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774
1775 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001776 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777 }
1778
reed@google.com4e2b3d32011-04-07 14:18:59 +00001779 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780}
1781
1782void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1783 const SkPaint& paint) {
1784 if ((long)count <= 0) {
1785 return;
1786 }
1787
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001788 SkRect r, storage;
1789 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001790 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001791 // special-case 2 points (common for drawing a single line)
1792 if (2 == count) {
1793 r.set(pts[0], pts[1]);
1794 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001795 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001796 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001797 bounds = &paint.computeFastStrokeBounds(r, &storage);
1798 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001799 return;
1800 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001801 }
reed@google.coma584aed2012-05-16 14:06:02 +00001802
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803 SkASSERT(pts != NULL);
1804
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001805 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001806
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001808 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809 }
reed@google.com4b226022011-01-11 18:32:13 +00001810
reed@google.com4e2b3d32011-04-07 14:18:59 +00001811 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001812}
1813
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001814void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001815 SkRect storage;
1816 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001818 bounds = &paint.computeFastBounds(r, &storage);
1819 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001820 return;
1821 }
1822 }
reed@google.com4b226022011-01-11 18:32:13 +00001823
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001824 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825
1826 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001827 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828 }
1829
reed@google.com4e2b3d32011-04-07 14:18:59 +00001830 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001831}
1832
reed@google.com4ed0fb72012-12-12 20:48:18 +00001833void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001834 SkRect storage;
1835 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001836 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001837 bounds = &paint.computeFastBounds(oval, &storage);
1838 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001839 return;
1840 }
1841 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001842
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001843 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001844
1845 while (iter.next()) {
1846 iter.fDevice->drawOval(iter, oval, looper.paint());
1847 }
1848
1849 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001850}
1851
1852void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001853 SkRect storage;
1854 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001855 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001856 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1857 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001858 return;
1859 }
1860 }
1861
1862 if (rrect.isRect()) {
1863 // call the non-virtual version
1864 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001865 return;
1866 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001867 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001868 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1869 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001870 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001871
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001872 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001873
1874 while (iter.next()) {
1875 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1876 }
1877
1878 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001879}
1880
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001881void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1882 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001883 SkRect storage;
1884 const SkRect* bounds = NULL;
1885 if (paint.canComputeFastBounds()) {
1886 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1887 if (this->quickReject(*bounds)) {
1888 return;
1889 }
1890 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001891
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001892 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001893
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001894 while (iter.next()) {
1895 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1896 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001897
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001898 LOOPER_END
1899}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001900
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001901void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001902 if (!path.isFinite()) {
1903 return;
1904 }
1905
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001906 SkRect storage;
1907 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001908 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001909 const SkRect& pathBounds = path.getBounds();
1910 bounds = &paint.computeFastBounds(pathBounds, &storage);
1911 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001912 return;
1913 }
1914 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001915
1916 const SkRect& r = path.getBounds();
1917 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001918 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001919 this->internalDrawPaint(paint);
1920 }
1921 return;
1922 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001923
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001924 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925
1926 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001927 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 }
1929
reed@google.com4e2b3d32011-04-07 14:18:59 +00001930 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001931}
1932
1933void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1934 const SkPaint* paint) {
1935 SkDEBUGCODE(bitmap.validate();)
1936
reed@google.com3d608122011-11-21 15:16:16 +00001937 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001938 SkRect bounds = {
1939 x, y,
1940 x + SkIntToScalar(bitmap.width()),
1941 y + SkIntToScalar(bitmap.height())
1942 };
1943 if (paint) {
1944 (void)paint->computeFastBounds(bounds, &bounds);
1945 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001946 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001947 return;
1948 }
1949 }
reed@google.com4b226022011-01-11 18:32:13 +00001950
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951 SkMatrix matrix;
1952 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001953 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001954}
1955
reed@google.com9987ec32011-09-07 11:57:52 +00001956// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001957void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001958 const SkRect& dst, const SkPaint* paint,
1959 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001960 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001961 return;
1962 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001963
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001964 SkRect storage;
1965 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001966 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001967 if (paint) {
1968 bounds = &paint->computeFastBounds(dst, &storage);
1969 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001970 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001971 return;
1972 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001973 }
reed@google.com3d608122011-11-21 15:16:16 +00001974
reed@google.com33535f32012-09-25 15:37:50 +00001975 SkLazyPaint lazy;
1976 if (NULL == paint) {
1977 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001978 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001979
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001980 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001981
reed@google.com33535f32012-09-25 15:37:50 +00001982 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001983 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001984 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001985
reed@google.com33535f32012-09-25 15:37:50 +00001986 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001987}
1988
reed@google.com71121732012-09-18 15:14:33 +00001989void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001990 const SkRect& dst, const SkPaint* paint,
1991 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001992 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001993 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001994}
1995
reed@android.com8a1c16f2008-12-17 15:59:43 +00001996void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1997 const SkPaint* paint) {
1998 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001999 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000}
2001
reed@google.com9987ec32011-09-07 11:57:52 +00002002void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2003 const SkIRect& center, const SkRect& dst,
2004 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002005 if (bitmap.drawsNothing()) {
2006 return;
2007 }
reed@google.com3d608122011-11-21 15:16:16 +00002008 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002009 SkRect storage;
2010 const SkRect* bounds = &dst;
2011 if (paint) {
2012 bounds = &paint->computeFastBounds(dst, &storage);
2013 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002014 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002015 return;
2016 }
2017 }
2018
reed@google.com9987ec32011-09-07 11:57:52 +00002019 const int32_t w = bitmap.width();
2020 const int32_t h = bitmap.height();
2021
2022 SkIRect c = center;
2023 // pin center to the bounds of the bitmap
2024 c.fLeft = SkMax32(0, center.fLeft);
2025 c.fTop = SkMax32(0, center.fTop);
2026 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2027 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2028
reed@google.com71121732012-09-18 15:14:33 +00002029 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002030 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002031 };
2032 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002033 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002034 };
reed@google.com9987ec32011-09-07 11:57:52 +00002035 SkScalar dstX[4] = {
2036 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2037 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2038 };
2039 SkScalar dstY[4] = {
2040 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2041 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2042 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002043
reed@google.com9987ec32011-09-07 11:57:52 +00002044 if (dstX[1] > dstX[2]) {
2045 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2046 dstX[2] = dstX[1];
2047 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002048
reed@google.com9987ec32011-09-07 11:57:52 +00002049 if (dstY[1] > dstY[2]) {
2050 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2051 dstY[2] = dstY[1];
2052 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002053
reed@google.com9987ec32011-09-07 11:57:52 +00002054 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002055 SkRect s, d;
2056
reed@google.com9987ec32011-09-07 11:57:52 +00002057 s.fTop = srcY[y];
2058 s.fBottom = srcY[y+1];
2059 d.fTop = dstY[y];
2060 d.fBottom = dstY[y+1];
2061 for (int x = 0; x < 3; x++) {
2062 s.fLeft = srcX[x];
2063 s.fRight = srcX[x+1];
2064 d.fLeft = dstX[x];
2065 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002066 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002067 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002068 }
2069 }
2070}
2071
2072void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2073 const SkRect& dst, const SkPaint* paint) {
2074 SkDEBUGCODE(bitmap.validate();)
2075
2076 // Need a device entry-point, so gpu can use a mesh
2077 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2078}
2079
reed@google.comf67e4cf2011-03-15 20:56:58 +00002080class SkDeviceFilteredPaint {
2081public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002082 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2083 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002084 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002085 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002086 newPaint->setFlags(flags.fFlags);
2087 newPaint->setHinting(flags.fHinting);
2088 fPaint = newPaint;
2089 } else {
2090 fPaint = &paint;
2091 }
2092 }
2093
reed@google.comf67e4cf2011-03-15 20:56:58 +00002094 const SkPaint& paint() const { return *fPaint; }
2095
2096private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002097 const SkPaint* fPaint;
2098 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002099};
2100
bungeman@google.com52c748b2011-08-22 21:30:43 +00002101void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2102 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002103 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002104 draw.fDevice->drawRect(draw, r, paint);
2105 } else {
2106 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002107 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002108 draw.fDevice->drawRect(draw, r, p);
2109 }
2110}
2111
2112void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2113 const char text[], size_t byteLength,
2114 SkScalar x, SkScalar y) {
2115 SkASSERT(byteLength == 0 || text != NULL);
2116
2117 // nothing to draw
2118 if (text == NULL || byteLength == 0 ||
2119 draw.fClip->isEmpty() ||
2120 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2121 return;
2122 }
2123
2124 SkScalar width = 0;
2125 SkPoint start;
2126
2127 start.set(0, 0); // to avoid warning
2128 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2129 SkPaint::kStrikeThruText_Flag)) {
2130 width = paint.measureText(text, byteLength);
2131
2132 SkScalar offsetX = 0;
2133 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2134 offsetX = SkScalarHalf(width);
2135 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2136 offsetX = width;
2137 }
2138 start.set(x - offsetX, y);
2139 }
2140
2141 if (0 == width) {
2142 return;
2143 }
2144
2145 uint32_t flags = paint.getFlags();
2146
2147 if (flags & (SkPaint::kUnderlineText_Flag |
2148 SkPaint::kStrikeThruText_Flag)) {
2149 SkScalar textSize = paint.getTextSize();
2150 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2151 SkRect r;
2152
2153 r.fLeft = start.fX;
2154 r.fRight = start.fX + width;
2155
2156 if (flags & SkPaint::kUnderlineText_Flag) {
2157 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2158 start.fY);
2159 r.fTop = offset;
2160 r.fBottom = offset + height;
2161 DrawRect(draw, paint, r, textSize);
2162 }
2163 if (flags & SkPaint::kStrikeThruText_Flag) {
2164 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2165 start.fY);
2166 r.fTop = offset;
2167 r.fBottom = offset + height;
2168 DrawRect(draw, paint, r, textSize);
2169 }
2170 }
2171}
2172
reed@google.come0d9ce82014-04-23 04:00:17 +00002173void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2174 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002175 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176
2177 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002178 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002179 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002180 DrawTextDecorations(iter, dfp.paint(),
2181 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182 }
2183
reed@google.com4e2b3d32011-04-07 14:18:59 +00002184 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185}
2186
reed@google.come0d9ce82014-04-23 04:00:17 +00002187void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2188 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002189 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002190
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002192 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002194 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002196
reed@google.com4e2b3d32011-04-07 14:18:59 +00002197 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002198}
2199
reed@google.come0d9ce82014-04-23 04:00:17 +00002200void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2201 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002202 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002203
reed@android.com8a1c16f2008-12-17 15:59:43 +00002204 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002205 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002207 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002209
reed@google.com4e2b3d32011-04-07 14:18:59 +00002210 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002211}
2212
reed@google.come0d9ce82014-04-23 04:00:17 +00002213void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2214 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002215 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002216
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217 while (iter.next()) {
2218 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002219 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002220 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002221
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002222 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002223}
2224
reed@google.come0d9ce82014-04-23 04:00:17 +00002225// These will become non-virtual, so they always call the (virtual) onDraw... method
2226void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2227 const SkPaint& paint) {
2228 this->onDrawText(text, byteLength, x, y, paint);
2229}
2230void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2231 const SkPaint& paint) {
2232 this->onDrawPosText(text, byteLength, pos, paint);
2233}
2234void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2235 SkScalar constY, const SkPaint& paint) {
2236 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2237}
2238void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2239 const SkMatrix* matrix, const SkPaint& paint) {
2240 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2241}
2242
reed@android.com8a1c16f2008-12-17 15:59:43 +00002243void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2244 const SkPoint verts[], const SkPoint texs[],
2245 const SkColor colors[], SkXfermode* xmode,
2246 const uint16_t indices[], int indexCount,
2247 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002248 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002249
reed@android.com8a1c16f2008-12-17 15:59:43 +00002250 while (iter.next()) {
2251 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002252 colors, xmode, indices, indexCount,
2253 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254 }
reed@google.com4b226022011-01-11 18:32:13 +00002255
reed@google.com4e2b3d32011-04-07 14:18:59 +00002256 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002257}
2258
dandovb3c9d1c2014-08-12 08:34:29 -07002259void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2260 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2261 if (NULL == cubics) {
2262 return;
2263 }
mtklein6cfa73a2014-08-13 13:33:49 -07002264
dandovecfff212014-08-04 10:02:00 -07002265 // Since a patch is always within the convex hull of the control points, we discard it when its
2266 // bounding rectangle is completely outside the current clip.
2267 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002268 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002269 if (this->quickReject(bounds)) {
2270 return;
2271 }
mtklein6cfa73a2014-08-13 13:33:49 -07002272
dandovb3c9d1c2014-08-12 08:34:29 -07002273 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2274}
2275
2276void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2277 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2278
dandovecfff212014-08-04 10:02:00 -07002279 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002280
dandovecfff212014-08-04 10:02:00 -07002281 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002282 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002283 }
mtklein6cfa73a2014-08-13 13:33:49 -07002284
dandovecfff212014-08-04 10:02:00 -07002285 LOOPER_END
2286}
2287
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288//////////////////////////////////////////////////////////////////////////////
2289// These methods are NOT virtual, and therefore must call back into virtual
2290// methods, rather than actually drawing themselves.
2291//////////////////////////////////////////////////////////////////////////////
2292
2293void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002294 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295 SkPaint paint;
2296
2297 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002298 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002299 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002300 }
2301 this->drawPaint(paint);
2302}
2303
reed@android.com845fdac2009-06-23 03:01:32 +00002304void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305 SkPaint paint;
2306
2307 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002308 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002309 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002310 }
2311 this->drawPaint(paint);
2312}
2313
2314void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2315 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002316
reed@android.com8a1c16f2008-12-17 15:59:43 +00002317 pt.set(x, y);
2318 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2319}
2320
2321void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2322 SkPoint pt;
2323 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002324
reed@android.com8a1c16f2008-12-17 15:59:43 +00002325 pt.set(x, y);
2326 paint.setColor(color);
2327 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2328}
2329
2330void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2331 const SkPaint& paint) {
2332 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002333
reed@android.com8a1c16f2008-12-17 15:59:43 +00002334 pts[0].set(x0, y0);
2335 pts[1].set(x1, y1);
2336 this->drawPoints(kLines_PointMode, 2, pts, paint);
2337}
2338
2339void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2340 SkScalar right, SkScalar bottom,
2341 const SkPaint& paint) {
2342 SkRect r;
2343
2344 r.set(left, top, right, bottom);
2345 this->drawRect(r, paint);
2346}
2347
2348void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2349 const SkPaint& paint) {
2350 if (radius < 0) {
2351 radius = 0;
2352 }
2353
2354 SkRect r;
2355 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002356 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357}
2358
2359void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2360 const SkPaint& paint) {
2361 if (rx > 0 && ry > 0) {
2362 if (paint.canComputeFastBounds()) {
2363 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002364 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365 return;
2366 }
2367 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002368 SkRRect rrect;
2369 rrect.setRectXY(r, rx, ry);
2370 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371 } else {
2372 this->drawRect(r, paint);
2373 }
2374}
2375
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2377 SkScalar sweepAngle, bool useCenter,
2378 const SkPaint& paint) {
2379 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2380 this->drawOval(oval, paint);
2381 } else {
2382 SkPath path;
2383 if (useCenter) {
2384 path.moveTo(oval.centerX(), oval.centerY());
2385 }
2386 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2387 if (useCenter) {
2388 path.close();
2389 }
2390 this->drawPath(path, paint);
2391 }
2392}
2393
2394void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2395 const SkPath& path, SkScalar hOffset,
2396 SkScalar vOffset, const SkPaint& paint) {
2397 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002398
reed@android.com8a1c16f2008-12-17 15:59:43 +00002399 matrix.setTranslate(hOffset, vOffset);
2400 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2401}
2402
reed@android.comf76bacf2009-05-13 14:00:33 +00002403///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002404void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002405 SkBaseDevice* device = this->getDevice();
2406 if (NULL != device) {
2407 device->EXPERIMENTAL_optimize(picture);
2408 }
2409}
reed@android.comf76bacf2009-05-13 14:00:33 +00002410
robertphillips9b14f262014-06-04 05:40:44 -07002411void SkCanvas::drawPicture(const SkPicture* picture) {
2412 if (NULL != picture) {
reedd5fa1a42014-08-09 11:08:05 -07002413 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002414 }
2415}
2416
reedd5fa1a42014-08-09 11:08:05 -07002417void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
2418 if (NULL != picture) {
2419 if (matrix && matrix->isIdentity()) {
2420 matrix = NULL;
2421 }
2422 this->onDrawPicture(picture, matrix, paint);
2423 }
2424}
robertphillips9b14f262014-06-04 05:40:44 -07002425
reedd5fa1a42014-08-09 11:08:05 -07002426void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2427 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002428 SkBaseDevice* device = this->getTopDevice();
2429 if (NULL != device) {
2430 // Canvas has to first give the device the opportunity to render
2431 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002432 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002433 return; // the device has rendered the entire picture
2434 }
2435 }
2436
reedd5fa1a42014-08-09 11:08:05 -07002437 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->width(), picture->height());
2438
robertphillips9b14f262014-06-04 05:40:44 -07002439 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440}
2441
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442///////////////////////////////////////////////////////////////////////////////
2443///////////////////////////////////////////////////////////////////////////////
2444
2445SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002446 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002447
2448 SkASSERT(canvas);
2449
2450 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2451 fDone = !fImpl->next();
2452}
2453
2454SkCanvas::LayerIter::~LayerIter() {
2455 fImpl->~SkDrawIter();
2456}
2457
2458void SkCanvas::LayerIter::next() {
2459 fDone = !fImpl->next();
2460}
2461
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002462SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002463 return fImpl->getDevice();
2464}
2465
2466const SkMatrix& SkCanvas::LayerIter::matrix() const {
2467 return fImpl->getMatrix();
2468}
2469
2470const SkPaint& SkCanvas::LayerIter::paint() const {
2471 const SkPaint* paint = fImpl->getPaint();
2472 if (NULL == paint) {
2473 paint = &fDefaultPaint;
2474 }
2475 return *paint;
2476}
2477
2478const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2479int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2480int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002481
2482///////////////////////////////////////////////////////////////////////////////
2483
fmalitac3b589a2014-06-05 12:40:07 -07002484SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002485
2486///////////////////////////////////////////////////////////////////////////////
2487
2488static bool supported_for_raster_canvas(const SkImageInfo& info) {
2489 switch (info.alphaType()) {
2490 case kPremul_SkAlphaType:
2491 case kOpaque_SkAlphaType:
2492 break;
2493 default:
2494 return false;
2495 }
2496
2497 switch (info.colorType()) {
2498 case kAlpha_8_SkColorType:
2499 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002500 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002501 break;
2502 default:
2503 return false;
2504 }
2505
2506 return true;
2507}
2508
2509SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2510 if (!supported_for_raster_canvas(info)) {
2511 return NULL;
2512 }
2513
2514 SkBitmap bitmap;
2515 if (!bitmap.allocPixels(info)) {
2516 return NULL;
2517 }
2518
2519 // should this functionality be moved into allocPixels()?
2520 if (!bitmap.info().isOpaque()) {
2521 bitmap.eraseColor(0);
2522 }
2523 return SkNEW_ARGS(SkCanvas, (bitmap));
2524}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002525
2526SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2527 if (!supported_for_raster_canvas(info)) {
2528 return NULL;
2529 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002530
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002531 SkBitmap bitmap;
2532 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2533 return NULL;
2534 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002535 return SkNEW_ARGS(SkCanvas, (bitmap));
2536}
reedd5fa1a42014-08-09 11:08:05 -07002537
2538///////////////////////////////////////////////////////////////////////////////
2539
2540SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
2541 const SkPaint* paint, int width, int height)
2542 : fCanvas(canvas)
2543 , fSaveCount(canvas->getSaveCount())
2544{
2545 if (NULL != paint) {
2546 SkRect bounds = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
2547 if (matrix) {
2548 matrix->mapRect(&bounds);
2549 }
2550 canvas->saveLayer(&bounds, paint);
2551 } else if (NULL != matrix) {
2552 canvas->save();
2553 }
mtklein6cfa73a2014-08-13 13:33:49 -07002554
reedd5fa1a42014-08-09 11:08:05 -07002555 if (NULL != matrix) {
2556 canvas->concat(*matrix);
2557 }
2558}
2559
2560SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2561 fCanvas->restoreToCount(fSaveCount);
2562}