blob: 3320d18fc039749ec054ab380595a84c47468c0a [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"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000010#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000011#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkDraw.h"
13#include "SkDrawFilter.h"
14#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000015#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000016#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000018#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000019#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000020#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000021#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000023#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000024#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000027#if SK_SUPPORT_GPU
28#include "GrRenderTarget.h"
29#endif
30
reed@google.comda17f752012-08-16 18:27:05 +000031// experimental for faster tiled drawing...
32//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000033
reed@android.com8a1c16f2008-12-17 15:59:43 +000034//#define SK_TRACE_SAVERESTORE
35
36#ifdef SK_TRACE_SAVERESTORE
37 static int gLayerCounter;
38 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
39 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
40
41 static int gRecCounter;
42 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
43 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
44
45 static int gCanvasCounter;
46 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
47 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
48#else
49 #define inc_layer()
50 #define dec_layer()
51 #define inc_rec()
52 #define dec_rec()
53 #define inc_canvas()
54 #define dec_canvas()
55#endif
56
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000057typedef SkTLazy<SkPaint> SkLazyPaint;
58
reed@google.com97af1a62012-08-28 12:19:02 +000059void SkCanvas::predrawNotify() {
60 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000061 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000062 }
63}
64
reed@android.com8a1c16f2008-12-17 15:59:43 +000065///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000066
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000067/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 The clip/matrix/proc are fields that reflect the top of the save/restore
69 stack. Whenever the canvas changes, it marks a dirty flag, and then before
70 these are used (assuming we're not on a layer) we rebuild these cache
71 values: they reflect the top of the save stack, but translated and clipped
72 by the device's XY offset and bitmap-bounds.
73*/
74struct DeviceCM {
75 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000076 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000077 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000078 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000079 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000080
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000081 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000082 : fNext(NULL) {
83 if (NULL != device) {
84 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000085 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 }
reed@google.com4b226022011-01-11 18:32:13 +000087 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000088 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000089 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000090
bungeman@google.com88edf1e2011-08-08 19:41:56 +000091 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000093 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 fDevice->unref();
95 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000096 SkDELETE(fPaint);
97 }
reed@google.com4b226022011-01-11 18:32:13 +000098
reed@google.com045e62d2011-10-24 12:19:46 +000099 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
100 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000101 int x = fDevice->getOrigin().x();
102 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 int width = fDevice->width();
104 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000105
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 if ((x | y) == 0) {
107 fMatrix = &totalMatrix;
108 fClip = totalClip;
109 } else {
110 fMatrixStorage = totalMatrix;
111 fMatrixStorage.postTranslate(SkIntToScalar(-x),
112 SkIntToScalar(-y));
113 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000114
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 totalClip.translate(-x, -y, &fClip);
116 }
117
reed@google.com045e62d2011-10-24 12:19:46 +0000118 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000119
120 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000121
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000123 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 SkRegion::kDifference_Op);
125 }
reed@google.com4b226022011-01-11 18:32:13 +0000126
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000127 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
128
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129#ifdef SK_DEBUG
130 if (!fClip.isEmpty()) {
131 SkIRect deviceR;
132 deviceR.set(0, 0, width, height);
133 SkASSERT(deviceR.contains(fClip.getBounds()));
134 }
135#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000136 }
137
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000139 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140};
141
142/* This is the record we keep for each save/restore level in the stack.
143 Since a level optionally copies the matrix and/or stack, we have pointers
144 for these fields. If the value is copied for this level, the copy is
145 stored in the ...Storage field, and the pointer points to that. If the
146 value is not copied for this level, we ignore ...Storage, and just point
147 at the corresponding value in the previous level in the stack.
148*/
149class SkCanvas::MCRec {
150public:
reed1f836ee2014-07-07 07:49:34 -0700151 SkMatrix fMatrix;
152 SkRasterClip fRasterClip;
153 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000154
reed@android.com8a1c16f2008-12-17 15:59:43 +0000155 DeviceCM* fLayer;
156 /* If there are any layers in the stack, this points to the top-most
157 one that is at or below this level in the stack (so we know what
158 bitmap/device to draw into from this level. This value is NOT
159 reference counted, since the real owner is either our fLayer field,
160 or a previous one in a lower level.)
161 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000162 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163
Florin Malita5f6102d2014-06-30 10:13:28 -0400164 MCRec(const MCRec* prev) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165 if (NULL != prev) {
reed1f836ee2014-07-07 07:49:34 -0700166 fMatrix = prev->fMatrix;
167 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
169 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000170 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171
172 fTopLayer = prev->fTopLayer;
173 } else { // no prev
reed1f836ee2014-07-07 07:49:34 -0700174 fMatrix.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 fFilter = NULL;
176 fTopLayer = NULL;
177 }
178 fLayer = NULL;
179
180 // don't bother initializing fNext
181 inc_rec();
182 }
183 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000184 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 SkDELETE(fLayer);
186 dec_rec();
187 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188};
189
190class SkDrawIter : public SkDraw {
191public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000192 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000193 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000194 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195 canvas->updateDeviceCMCache();
196
reed@google.com90c07ea2012-04-13 13:50:27 +0000197 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000199 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000200 }
reed@google.com4b226022011-01-11 18:32:13 +0000201
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 bool next() {
203 // skip over recs with empty clips
204 if (fSkipEmptyClips) {
205 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
206 fCurrLayer = fCurrLayer->fNext;
207 }
208 }
209
reed@google.comf68c5e22012-02-24 16:38:58 +0000210 const DeviceCM* rec = fCurrLayer;
211 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212
213 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000214 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
215 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 fDevice = rec->fDevice;
217 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000219 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220
221 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000223
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 return true;
225 }
226 return false;
227 }
reed@google.com4b226022011-01-11 18:32:13 +0000228
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000229 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000230 int getX() const { return fDevice->getOrigin().x(); }
231 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 const SkMatrix& getMatrix() const { return *fMatrix; }
233 const SkRegion& getClip() const { return *fClip; }
234 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000235
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236private:
237 SkCanvas* fCanvas;
238 const DeviceCM* fCurrLayer;
239 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 SkBool8 fSkipEmptyClips;
241
242 typedef SkDraw INHERITED;
243};
244
245/////////////////////////////////////////////////////////////////////////////
246
247class AutoDrawLooper {
248public:
reed@google.com8926b162012-03-23 15:36:36 +0000249 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000250 bool skipLayerForImageFilter = false,
251 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000252 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000254 fPaint = NULL;
255 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000256 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000257 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258
reed@google.com8926b162012-03-23 15:36:36 +0000259 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
260 SkPaint tmp;
261 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000262 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
263 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000264 // we'll clear the imageFilter for the actual draws in next(), so
265 // it will only be applied during the restore().
266 fDoClearImageFilter = true;
267 }
268
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000269 if (SkDrawLooper* looper = paint.getLooper()) {
270 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
271 looper->contextSize());
272 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000273 fIsSimple = false;
274 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000275 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000276 // can we be marked as simple?
277 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000278 }
279 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000280
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000282 if (fDoClearImageFilter) {
283 fCanvas->internalRestore();
284 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000285 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000287
reed@google.com4e2b3d32011-04-07 14:18:59 +0000288 const SkPaint& paint() const {
289 SkASSERT(fPaint);
290 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000292
reed@google.com129ec222012-05-15 13:24:09 +0000293 bool next(SkDrawFilter::Type drawType) {
294 if (fDone) {
295 return false;
296 } else if (fIsSimple) {
297 fDone = true;
298 fPaint = &fOrigPaint;
299 return !fPaint->nothingToDraw();
300 } else {
301 return this->doNext(drawType);
302 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000303 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000304
reed@android.com8a1c16f2008-12-17 15:59:43 +0000305private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000306 SkLazyPaint fLazyPaint;
307 SkCanvas* fCanvas;
308 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000309 SkDrawFilter* fFilter;
310 const SkPaint* fPaint;
311 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000312 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000313 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000314 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000315 SkDrawLooper::Context* fLooperContext;
316 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000317
318 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319};
320
reed@google.com129ec222012-05-15 13:24:09 +0000321bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000322 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000323 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000324 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000325
326 SkPaint* paint = fLazyPaint.set(fOrigPaint);
327
328 if (fDoClearImageFilter) {
329 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000330 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000331
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000332 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000333 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000334 return false;
335 }
336 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000337 if (!fFilter->filter(paint, drawType)) {
338 fDone = true;
339 return false;
340 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000341 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000342 // no looper means we only draw once
343 fDone = true;
344 }
345 }
346 fPaint = paint;
347
348 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000349 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000350 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000351 }
352
353 // call this after any possible paint modifiers
354 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000355 fPaint = NULL;
356 return false;
357 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358 return true;
359}
360
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361#include "SkColorPriv.h"
362
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363////////// macros to place around the internal draw calls //////////////////
364
reed@google.com8926b162012-03-23 15:36:36 +0000365#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000366 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000367 AutoDrawLooper looper(this, paint, true); \
368 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000369 SkDrawIter iter(this);
370
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000371#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000372 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000373 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000374 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000376
reed@google.com4e2b3d32011-04-07 14:18:59 +0000377#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378
379////////////////////////////////////////////////////////////////////////////
380
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000381SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@google.comc0784db2013-12-13 21:16:12 +0000382 fCachedLocalClipBounds.setEmpty();
383 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000384 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000385 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000386 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000387 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000388 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000389 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390
391 fMCRec = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400392 new (fMCRec) MCRec(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000393
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000394 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396
reed@google.com97af1a62012-08-28 12:19:02 +0000397 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000398
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000399 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400}
401
reed@google.comcde92112011-07-06 20:00:52 +0000402SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000403 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
404{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000405 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000406
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000407 this->init(NULL);
408}
409
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000410SkCanvas::SkCanvas(int width, int height)
411 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
412{
413 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000414
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000415 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000416 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000417 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
418}
419
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000420SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000421 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
422{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 inc_canvas();
424
425 this->init(device);
426}
427
428SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000429 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
430{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431 inc_canvas();
432
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000433 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434}
435
436SkCanvas::~SkCanvas() {
437 // free up the contents of our deque
438 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000439 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441 this->internalRestore(); // restore the last, since we're going away
442
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000443 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000444
reed@android.com8a1c16f2008-12-17 15:59:43 +0000445 dec_canvas();
446}
447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448SkDrawFilter* SkCanvas::getDrawFilter() const {
449 return fMCRec->fFilter;
450}
451
452SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
453 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
454 return filter;
455}
456
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000457SkMetaData& SkCanvas::getMetaData() {
458 // metadata users are rare, so we lazily allocate it. If that changes we
459 // can decide to just make it a field in the device (rather than a ptr)
460 if (NULL == fMetaData) {
461 fMetaData = new SkMetaData;
462 }
463 return *fMetaData;
464}
465
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466///////////////////////////////////////////////////////////////////////////////
467
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000468void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000469 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000470 if (device) {
471 device->flush();
472 }
473}
474
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000475SkISize SkCanvas::getTopLayerSize() const {
476 SkBaseDevice* d = this->getTopDevice();
477 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
478}
479
480SkIPoint SkCanvas::getTopLayerOrigin() const {
481 SkBaseDevice* d = this->getTopDevice();
482 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
483}
484
485SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000486 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000487 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
488}
489
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000490SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000492 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000493 SkASSERT(rec && rec->fLayer);
494 return rec->fLayer->fDevice;
495}
496
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000497SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000498 if (updateMatrixClip) {
499 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
500 }
reed@google.com9266fed2011-03-30 00:18:03 +0000501 return fMCRec->fTopLayer->fDevice;
502}
503
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000504SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000505 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000506 SkDeque::F2BIter iter(fMCStack);
507 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000508 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000509 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510
511 if (rootDevice == device) {
512 return device;
513 }
reed@google.com4b226022011-01-11 18:32:13 +0000514
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000516 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 }
518 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000519 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 }
521
522 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
523 rootDevice = device;
524
525 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000526
reed@android.com8a1c16f2008-12-17 15:59:43 +0000527 /* Now we update our initial region to have the bounds of the new device,
528 and then intersect all of the clips in our stack with these bounds,
529 to ensure that we can't draw outside of the device's bounds (and trash
530 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532 NOTE: this is only a partial-fix, since if the new device is larger than
533 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000534 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
536 reconstruct the correct clips, so this approximation will have to do.
537 The caller really needs to restore() back to the base if they want to
538 accurately take advantage of the new device bounds.
539 */
540
reed@google.com42aea282012-03-28 16:19:15 +0000541 SkIRect bounds;
542 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000543 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000544 } else {
545 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000546 }
reed@google.com42aea282012-03-28 16:19:15 +0000547 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700548 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000549 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700550 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000551 }
552
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553 return device;
554}
555
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000556bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
557 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
558 return false;
559 }
560
561 bool weAllocated = false;
562 if (NULL == bitmap->pixelRef()) {
563 if (!bitmap->allocPixels()) {
564 return false;
565 }
566 weAllocated = true;
567 }
568
569 SkBitmap bm(*bitmap);
570 bm.lockPixels();
571 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
572 return true;
573 }
574
575 if (weAllocated) {
576 bitmap->setPixelRef(NULL);
577 }
578 return false;
579}
reed@google.com51df9e32010-12-23 19:29:18 +0000580
bsalomon@google.comc6980972011-11-02 19:57:21 +0000581bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000582 SkIRect r = srcRect;
583 const SkISize size = this->getBaseLayerSize();
584 if (!r.intersect(0, 0, size.width(), size.height())) {
585 bitmap->reset();
586 return false;
587 }
588
589 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
590 // bitmap will already be reset.
591 return false;
592 }
593 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
594 bitmap->reset();
595 return false;
596 }
597 return true;
598}
599
600bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
601 switch (origInfo.colorType()) {
602 case kUnknown_SkColorType:
603 case kIndex_8_SkColorType:
604 return false;
605 default:
606 break;
607 }
608 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
609 return false;
610 }
611 if (0 == origInfo.width() || 0 == origInfo.height()) {
612 return false;
613 }
614
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000615 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000616 if (!device) {
617 return false;
618 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000619
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000620 const SkISize size = this->getBaseLayerSize();
621 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
622 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000623 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000624 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000625
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000626 SkImageInfo info = origInfo;
627 // the intersect may have shrunk info's logical size
628 info.fWidth = srcR.width();
629 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000630
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000631 // if x or y are negative, then we have to adjust pixels
632 if (x > 0) {
633 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000634 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000635 if (y > 0) {
636 y = 0;
637 }
638 // here x,y are either 0 or negative
639 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000640
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000641 // The device can assert that the requested area is always contained in its bounds
642 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000643}
644
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000645bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
646 if (bitmap.getTexture()) {
647 return false;
648 }
649 SkBitmap bm(bitmap);
650 bm.lockPixels();
651 if (bm.getPixels()) {
652 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
653 }
654 return false;
655}
656
657bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
658 int x, int y) {
659 switch (origInfo.colorType()) {
660 case kUnknown_SkColorType:
661 case kIndex_8_SkColorType:
662 return false;
663 default:
664 break;
665 }
666 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
667 return false;
668 }
669
670 const SkISize size = this->getBaseLayerSize();
671 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
672 if (!target.intersect(0, 0, size.width(), size.height())) {
673 return false;
674 }
675
676 SkBaseDevice* device = this->getDevice();
677 if (!device) {
678 return false;
679 }
680
681 SkImageInfo info = origInfo;
682 // the intersect may have shrunk info's logical size
683 info.fWidth = target.width();
684 info.fHeight = target.height();
685
686 // if x or y are negative, then we have to adjust pixels
687 if (x > 0) {
688 x = 0;
689 }
690 if (y > 0) {
691 y = 0;
692 }
693 // here x,y are either 0 or negative
694 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
695
reed4af35f32014-06-27 17:47:49 -0700696 // Tell our owning surface to bump its generation ID
697 this->predrawNotify();
698
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000699 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000700 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000701}
reed@google.com51df9e32010-12-23 19:29:18 +0000702
junov@google.com4370aed2012-01-18 16:21:08 +0000703SkCanvas* SkCanvas::canvasForDrawIter() {
704 return this;
705}
706
reed@android.com8a1c16f2008-12-17 15:59:43 +0000707//////////////////////////////////////////////////////////////////////////////
708
reed@android.com8a1c16f2008-12-17 15:59:43 +0000709void SkCanvas::updateDeviceCMCache() {
710 if (fDeviceCMDirty) {
711 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700712 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000714
reed@android.com8a1c16f2008-12-17 15:59:43 +0000715 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000716 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000717 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000718 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000719 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000720 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000721 } while ((layer = layer->fNext) != NULL);
722 }
723 fDeviceCMDirty = false;
724 }
725}
726
reed@android.com8a1c16f2008-12-17 15:59:43 +0000727///////////////////////////////////////////////////////////////////////////////
728
Florin Malita5f6102d2014-06-30 10:13:28 -0400729int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000730 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000731
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 MCRec* newTop = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400733 new (newTop) MCRec(fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000735
Florin Malita5f6102d2014-06-30 10:13:28 -0400736 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000737
reed@android.com8a1c16f2008-12-17 15:59:43 +0000738 return saveCount;
739}
740
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000741int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400742 this->willSave();
743 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744}
745
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000747#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000749#else
750 return true;
751#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752}
753
junov@chromium.orga907ac32012-02-24 21:54:07 +0000754bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000755 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000756 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000757 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000758 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000759 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000760 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000761
762 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700763 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000764 // Filters may grow the bounds beyond the device bounds.
765 op = SkRegion::kReplace_Op;
766 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000767 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000768 if (NULL != bounds) {
769 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000770
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 this->getTotalMatrix().mapRect(&r, *bounds);
772 r.roundOut(&ir);
773 // early exit if the layer's bounds are clipped out
774 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000775 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700776 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000777 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000778 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779 }
780 } else { // no user bounds, so just use the clip
781 ir = clipBounds;
782 }
783
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000784 if (bounds_affects_clip(flags)) {
785 fClipStack.clipDevRect(ir, op);
786 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700787 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000788 return false;
789 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000790 }
791
792 if (intersection) {
793 *intersection = ir;
794 }
795 return true;
796}
797
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000798int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
799 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
800 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
801}
802
junov@chromium.orga907ac32012-02-24 21:54:07 +0000803int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
804 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000805 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
806 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000807}
808
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000809int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
810 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000811#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000812 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000813#endif
814
junov@chromium.orga907ac32012-02-24 21:54:07 +0000815 // do this before we create the layer. We don't call the public save() since
816 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400817 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000818
819 fDeviceCMDirty = true;
820
821 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000822 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000823 return count;
824 }
825
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000826 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
827 // the clipRectBounds() call above?
828 if (kNoLayer_SaveLayerStrategy == strategy) {
829 return count;
830 }
831
reed@google.comb55deeb2012-01-06 14:43:09 +0000832 // Kill the imagefilter if our device doesn't allow it
833 SkLazyPaint lazyP;
834 if (paint && paint->getImageFilter()) {
835 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000836 if (justForImageFilter) {
837 // early exit if the layer was just for the imageFilter
838 return count;
839 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000840 SkPaint* p = lazyP.set(*paint);
841 p->setImageFilter(NULL);
842 paint = p;
843 }
844 }
845
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000846 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
847 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
848 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000850 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000851 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700852 device = this->getDevice();
853 if (device) {
854 device = device->createCompatibleDevice(info);
855 }
reed@google.com76dd2772012-01-05 21:15:07 +0000856 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000857 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000858 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000859 if (NULL == device) {
860 SkDebugf("Unable to create device for layer.");
861 return count;
862 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000863
reed@google.com6f8f2922011-03-04 22:27:10 +0000864 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000865 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000866 device->unref();
867
868 layer->fNext = fMCRec->fTopLayer;
869 fMCRec->fLayer = layer;
870 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
871
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000872 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000873 return count;
874}
875
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000876int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
877 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
878}
879
reed@android.com8a1c16f2008-12-17 15:59:43 +0000880int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
881 SaveFlags flags) {
882 if (0xFF == alpha) {
883 return this->saveLayer(bounds, NULL, flags);
884 } else {
885 SkPaint tmpPaint;
886 tmpPaint.setAlpha(alpha);
887 return this->saveLayer(bounds, &tmpPaint, flags);
888 }
889}
890
891void SkCanvas::restore() {
892 // check for underflow
893 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000894 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000895 this->internalRestore();
896 }
897}
898
899void SkCanvas::internalRestore() {
900 SkASSERT(fMCStack.count() != 0);
901
902 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000903 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904
Florin Malita5f6102d2014-06-30 10:13:28 -0400905 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000906
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000907 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000908 DeviceCM* layer = fMCRec->fLayer; // may be null
909 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
910 fMCRec->fLayer = NULL;
911
912 // now do the normal restore()
913 fMCRec->~MCRec(); // balanced in save()
914 fMCStack.pop_back();
915 fMCRec = (MCRec*)fMCStack.back();
916
917 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
918 since if we're being recorded, we don't want to record this (the
919 recorder will have already recorded the restore).
920 */
921 if (NULL != layer) {
922 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000923 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000924 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
925 layer->fPaint);
926 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000927 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000928
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000929 SkASSERT(fSaveLayerCount > 0);
930 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931 }
932 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000933 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000934}
935
936int SkCanvas::getSaveCount() const {
937 return fMCStack.count();
938}
939
940void SkCanvas::restoreToCount(int count) {
941 // sanity check
942 if (count < 1) {
943 count = 1;
944 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000945
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000946 int n = this->getSaveCount() - count;
947 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000948 this->restore();
949 }
950}
951
reed@google.com7c202932011-12-14 18:48:05 +0000952bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000953 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000954}
955
reed@google.com76f10a32014-02-05 15:32:21 +0000956SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
957 return this->onNewSurface(info);
958}
959
960SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
961 SkBaseDevice* dev = this->getDevice();
962 return dev ? dev->newSurface(info) : NULL;
963}
964
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000965SkImageInfo SkCanvas::imageInfo() const {
966 SkBaseDevice* dev = this->getDevice();
967 if (dev) {
968 return dev->imageInfo();
969 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000970 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000971 }
972}
973
974const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
975 return this->onPeekPixels(info, rowBytes);
976}
977
978const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
979 SkBaseDevice* dev = this->getDevice();
980 return dev ? dev->peekPixels(info, rowBytes) : NULL;
981}
982
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +0000983void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
984 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
985 if (pixels && origin) {
986 *origin = this->getTopDevice(false)->getOrigin();
987 }
988 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +0000989}
990
991void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
992 SkBaseDevice* dev = this->getTopDevice();
993 return dev ? dev->accessPixels(info, rowBytes) : NULL;
994}
995
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000996SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
997 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
998 if (NULL == fAddr) {
999 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001000 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001001 return; // failure, fAddr is NULL
1002 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001003 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1004 return; // failure, fAddr is NULL
1005 }
1006 fAddr = fBitmap.getPixels();
1007 fRowBytes = fBitmap.rowBytes();
1008 }
1009 SkASSERT(fAddr); // success
1010}
1011
1012bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1013 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001014 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001015 } else {
1016 bitmap->reset();
1017 return false;
1018 }
1019}
1020
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001021void SkCanvas::onPushCull(const SkRect& cullRect) {
1022 // do nothing. Subclasses may do something
1023}
1024
1025void SkCanvas::onPopCull() {
1026 // do nothing. Subclasses may do something
1027}
1028
reed@android.com8a1c16f2008-12-17 15:59:43 +00001029/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001030#ifdef SK_DEBUG
1031// Ensure that cull rects are monotonically nested in device space.
1032void SkCanvas::validateCull(const SkIRect& devCull) {
1033 if (fCullStack.isEmpty()
1034 || devCull.isEmpty()
1035 || fCullStack.top().contains(devCull)) {
1036 return;
1037 }
1038
1039 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1040 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1041 fCullStack.top().x(), fCullStack.top().y(),
1042 fCullStack.top().right(), fCullStack.top().bottom()));
1043
1044#ifdef ASSERT_NESTED_CULLING
1045 SkDEBUGFAIL("Invalid cull.");
1046#endif
1047}
1048#endif
1049
1050void SkCanvas::pushCull(const SkRect& cullRect) {
1051 ++fCullCount;
1052 this->onPushCull(cullRect);
1053
1054#ifdef SK_DEBUG
1055 // Map the cull rect into device space.
1056 SkRect mappedCull;
1057 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1058
1059 // Take clipping into account.
1060 SkIRect devClip, devCull;
1061 mappedCull.roundOut(&devCull);
1062 this->getClipDeviceBounds(&devClip);
1063 if (!devCull.intersect(devClip)) {
1064 devCull.setEmpty();
1065 }
1066
1067 this->validateCull(devCull);
1068 fCullStack.push(devCull); // balanced in popCull
1069#endif
1070}
1071
1072void SkCanvas::popCull() {
1073 SkASSERT(fCullStack.count() == fCullCount);
1074
1075 if (fCullCount > 0) {
1076 --fCullCount;
1077 this->onPopCull();
1078
1079 SkDEBUGCODE(fCullStack.pop());
1080 }
1081}
1082
1083/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001085void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001086 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001087 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 return;
1089 }
1090
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001091 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001093 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001094 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001095
1096 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001097
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001098 SkRect storage;
1099 const SkRect* bounds = NULL;
1100 if (paint && paint->canComputeFastBounds()) {
1101 bitmap.getBounds(&storage);
1102 matrix.mapRect(&storage);
1103 bounds = &paint->computeFastBounds(storage, &storage);
1104 }
1105
1106 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001107
1108 while (iter.next()) {
1109 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1110 }
1111
1112 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001113}
1114
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001115void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001116 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117 SkPaint tmp;
1118 if (NULL == paint) {
1119 tmp.setDither(true);
1120 paint = &tmp;
1121 }
reed@google.com4b226022011-01-11 18:32:13 +00001122
reed@google.com8926b162012-03-23 15:36:36 +00001123 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001124 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001125 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001126 paint = &looper.paint();
1127 SkImageFilter* filter = paint->getImageFilter();
1128 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001129 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001130 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001131 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001132 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001133 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001134 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001135 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001136 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblanco55b6d8b2014-07-30 11:26:46 -07001137 SkAutoTUnref<SkImageFilter::UniqueIDCache> cache(dstDev->getImageFilterCache());
1138 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001139 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001140 SkPaint tmpUnfiltered(*paint);
1141 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001142 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1143 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001144 }
1145 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001146 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001147 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001148 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001149 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001150}
1151
reed@google.com8926b162012-03-23 15:36:36 +00001152void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1153 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001154 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001155 return;
1156 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001157 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001158
reed@google.com8926b162012-03-23 15:36:36 +00001159 SkPaint tmp;
1160 if (NULL == paint) {
1161 paint = &tmp;
1162 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001163
reed@google.com8926b162012-03-23 15:36:36 +00001164 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001165
reed@google.com8926b162012-03-23 15:36:36 +00001166 while (iter.next()) {
1167 paint = &looper.paint();
1168 SkImageFilter* filter = paint->getImageFilter();
1169 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1170 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001171 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001172 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001173 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001174 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001175 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001176 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblanco55b6d8b2014-07-30 11:26:46 -07001177 SkAutoTUnref<SkImageFilter::UniqueIDCache> cache(iter.fDevice->getImageFilterCache());
1178 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001179 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001180 SkPaint tmpUnfiltered(*paint);
1181 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001182 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001183 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001184 }
1185 } else {
1186 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1187 }
1188 }
1189 LOOPER_END
1190}
1191
reed@android.com8a1c16f2008-12-17 15:59:43 +00001192/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001193void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001194 SkMatrix m;
1195 m.setTranslate(dx, dy);
1196 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001197}
1198
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001199void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001200 SkMatrix m;
1201 m.setScale(sx, sy);
1202 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001203}
1204
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001205void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001206 SkMatrix m;
1207 m.setRotate(degrees);
1208 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001209}
1210
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001211void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001212 SkMatrix m;
1213 m.setSkew(sx, sy);
1214 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001215}
1216
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001217void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001218 if (matrix.isIdentity()) {
1219 return;
1220 }
1221
reed@android.com8a1c16f2008-12-17 15:59:43 +00001222 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001223 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001224 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001225
1226 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001227}
1228
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229void SkCanvas::setMatrix(const SkMatrix& matrix) {
1230 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001231 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001232 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001233 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001234}
1235
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236void SkCanvas::resetMatrix() {
1237 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001238
reed@android.com8a1c16f2008-12-17 15:59:43 +00001239 matrix.reset();
1240 this->setMatrix(matrix);
1241}
1242
1243//////////////////////////////////////////////////////////////////////////////
1244
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001245void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001246 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1247 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001248}
1249
1250void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001251#ifdef SK_ENABLE_CLIP_QUICKREJECT
1252 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001253 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001254 return false;
1255 }
1256
reed@google.com3b3e8952012-08-16 20:53:31 +00001257 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001258 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001259 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001260
1261 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001262 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001263 }
1264 }
1265#endif
1266
reed@google.com5c3d1472011-02-22 19:12:23 +00001267 AutoValidateClip avc(this);
1268
reed@android.com8a1c16f2008-12-17 15:59:43 +00001269 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001270 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001271 if (!fAllowSoftClip) {
1272 edgeStyle = kHard_ClipEdgeStyle;
1273 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274
reed1f836ee2014-07-07 07:49:34 -07001275 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001276 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001277 // the matrix. This means we don't have to a) make a path, and b) tell
1278 // the region code to scan-convert the path, only to discover that it
1279 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281
reed1f836ee2014-07-07 07:49:34 -07001282 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001283 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed1f836ee2014-07-07 07:49:34 -07001284 fMCRec->fRasterClip.op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001286 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001287 // and clip against that, since it can handle any matrix. However, to
1288 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1289 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290 SkPath path;
1291
1292 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001293 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001294 }
1295}
1296
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001297static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1298 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001299 // base is used to limit the size (and therefore memory allocation) of the
1300 // region that results from scan converting devPath.
1301 SkRegion base;
1302
reed@google.com819c9212011-02-23 18:56:55 +00001303 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001304 // since we are intersect, we can do better (tighter) with currRgn's
1305 // bounds, than just using the device. However, if currRgn is complex,
1306 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001307 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001308 // FIXME: we should also be able to do this when currClip->isBW(),
1309 // but relaxing the test above triggers GM asserts in
1310 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001311 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001312 } else {
reed@google.com00177082011-10-12 14:34:30 +00001313 base.setRect(currClip->getBounds());
1314 SkRasterClip clip;
1315 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001316 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001317 }
reed@google.com819c9212011-02-23 18:56:55 +00001318 } else {
reed52d9ac62014-06-30 09:05:34 -07001319 const SkISize size = canvas->getBaseLayerSize();
1320 base.setRect(0, 0, size.width(), size.height());
reed@google.com819c9212011-02-23 18:56:55 +00001321
1322 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001323 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001324 } else {
reed@google.com00177082011-10-12 14:34:30 +00001325 SkRasterClip clip;
1326 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001327 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001328 }
1329 }
1330}
1331
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001332void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001333 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001334 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001335 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1336 } else {
1337 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001338 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001339}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001340
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001341void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001342 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001343 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001344 AutoValidateClip avc(this);
1345
1346 fDeviceCMDirty = true;
1347 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001348 if (!fAllowSoftClip) {
1349 edgeStyle = kHard_ClipEdgeStyle;
1350 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001351
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001352 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001353
1354 SkPath devPath;
1355 devPath.addRRect(transformedRRect);
1356
reed1f836ee2014-07-07 07:49:34 -07001357 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001358 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001359 }
1360
1361 SkPath path;
1362 path.addRRect(rrect);
1363 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001364 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001365}
1366
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001367void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001368 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1369 SkRect r;
1370 if (!path.isInverseFillType() && path.isRect(&r)) {
1371 this->onClipRect(r, op, edgeStyle);
1372 } else {
1373 this->onClipPath(path, op, edgeStyle);
1374 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001375}
1376
1377void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001378#ifdef SK_ENABLE_CLIP_QUICKREJECT
1379 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001380 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001381 return false;
1382 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001383
reed@google.com3b3e8952012-08-16 20:53:31 +00001384 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001385 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001386 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001387
reed@google.comda17f752012-08-16 18:27:05 +00001388 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001389 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001390 }
1391 }
1392#endif
1393
reed@google.com5c3d1472011-02-22 19:12:23 +00001394 AutoValidateClip avc(this);
1395
reed@android.com8a1c16f2008-12-17 15:59:43 +00001396 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001397 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398 if (!fAllowSoftClip) {
1399 edgeStyle = kHard_ClipEdgeStyle;
1400 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001401
1402 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001403 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404
reed@google.comfe701122011-11-08 19:41:23 +00001405 // Check if the transfomation, or the original path itself
1406 // made us empty. Note this can also happen if we contained NaN
1407 // values. computing the bounds detects this, and will set our
1408 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1409 if (devPath.getBounds().isEmpty()) {
1410 // resetting the path will remove any NaN or other wanky values
1411 // that might upset our scan converter.
1412 devPath.reset();
1413 }
1414
reed@google.com5c3d1472011-02-22 19:12:23 +00001415 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001416 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001417
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001418 if (fAllowSimplifyClip) {
1419 devPath.reset();
1420 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1421 const SkClipStack* clipStack = getClipStack();
1422 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1423 const SkClipStack::Element* element;
1424 while ((element = iter.next())) {
1425 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001426 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001427 if (type != SkClipStack::Element::kEmpty_Type) {
1428 element->asPath(&operand);
1429 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001430 SkRegion::Op elementOp = element->getOp();
1431 if (elementOp == SkRegion::kReplace_Op) {
1432 devPath = operand;
1433 } else {
1434 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1435 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001436 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1437 // perhaps we need an API change to avoid this sort of mixed-signals about
1438 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001439 if (element->isAA()) {
1440 edgeStyle = kSoft_ClipEdgeStyle;
1441 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001442 }
1443 op = SkRegion::kReplace_Op;
1444 }
1445
reed1f836ee2014-07-07 07:49:34 -07001446 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447}
1448
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001449void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001450 bool inverseFilled) {
1451 // This is for updating the clip conservatively using only bounds
1452 // information.
1453 // Contract:
1454 // The current clip must contain the true clip. The true
1455 // clip is the clip that would have normally been computed
1456 // by calls to clipPath and clipRRect
1457 // Objective:
1458 // Keep the current clip as small as possible without
1459 // breaking the contract, using only clip bounding rectangles
1460 // (for performance).
1461
1462 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1463 // don't have to worry about getting caught in a loop. Thus anywhere
1464 // we call a virtual method, we explicitly prefix it with
1465 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001466
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001467 if (inverseFilled) {
1468 switch (op) {
1469 case SkRegion::kIntersect_Op:
1470 case SkRegion::kDifference_Op:
1471 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001472 // the clip unchanged conservatively respects the contract.
1473 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001474 case SkRegion::kUnion_Op:
1475 case SkRegion::kReplace_Op:
1476 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001477 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001478 // These ops can grow the current clip up to the extents of
1479 // the input clip, which is inverse filled, so we just set
1480 // the current clip to the device bounds.
1481 SkRect deviceBounds;
1482 SkIRect deviceIBounds;
1483 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001484 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001485
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001486 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001487 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001488 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001489 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001490 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001491 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001492 break;
1493 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001494 default:
1495 SkASSERT(0); // unhandled op?
1496 }
1497 } else {
1498 // Not inverse filled
1499 switch (op) {
1500 case SkRegion::kIntersect_Op:
1501 case SkRegion::kUnion_Op:
1502 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001503 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1504 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001505 case SkRegion::kDifference_Op:
1506 // Difference can only shrink the current clip.
1507 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001509 case SkRegion::kReverseDifference_Op:
1510 // To reverse, we swap in the bounds with a replace op.
1511 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001512 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1513 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001514 case SkRegion::kXOR_Op:
1515 // Be conservative, based on (A XOR B) always included in (A union B),
1516 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001517 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1518 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001519 default:
1520 SkASSERT(0); // unhandled op?
1521 }
1522 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001523}
1524
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001525void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001527}
1528
1529void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001530 AutoValidateClip avc(this);
1531
reed@android.com8a1c16f2008-12-17 15:59:43 +00001532 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001533 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534
reed@google.com5c3d1472011-02-22 19:12:23 +00001535 // todo: signal fClipStack that we have a region, and therefore (I guess)
1536 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001537 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001538
reed1f836ee2014-07-07 07:49:34 -07001539 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540}
1541
reed@google.com819c9212011-02-23 18:56:55 +00001542#ifdef SK_DEBUG
1543void SkCanvas::validateClip() const {
1544 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001545 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001546 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001547 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001548 return;
1549 }
1550
reed@google.com819c9212011-02-23 18:56:55 +00001551 SkIRect ir;
1552 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001553 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001554
robertphillips@google.com80214e22012-07-20 15:33:18 +00001555 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001556 const SkClipStack::Element* element;
1557 while ((element = iter.next()) != NULL) {
1558 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001559 case SkClipStack::Element::kRect_Type:
1560 element->getRect().round(&ir);
1561 tmpClip.op(ir, element->getOp());
1562 break;
1563 case SkClipStack::Element::kEmpty_Type:
1564 tmpClip.setEmpty();
1565 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001566 default: {
1567 SkPath path;
1568 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001569 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001570 break;
1571 }
reed@google.com819c9212011-02-23 18:56:55 +00001572 }
1573 }
reed@google.com819c9212011-02-23 18:56:55 +00001574}
1575#endif
1576
reed@google.com90c07ea2012-04-13 13:50:27 +00001577void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001578 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001579 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001580
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001581 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001582 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001583 }
1584}
1585
reed@google.com5c3d1472011-02-22 19:12:23 +00001586///////////////////////////////////////////////////////////////////////////////
1587
reed@google.com754de5f2014-02-24 19:38:20 +00001588bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001589 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001590}
1591
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001592bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001593 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001594}
1595
reed@google.com3b3e8952012-08-16 20:53:31 +00001596bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001597
reed@google.com16078632011-12-06 18:56:37 +00001598 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001599 return true;
1600
reed1f836ee2014-07-07 07:49:34 -07001601 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001602 return true;
1603 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604
reed1f836ee2014-07-07 07:49:34 -07001605 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001606 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001607 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001608 SkIRect idst;
1609 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001610 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001611 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001612 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001613
reed@android.coma380ae42009-07-21 01:17:02 +00001614 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001615 // TODO: should we use | instead, or compare all 4 at once?
1616 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001617 return true;
1618 }
reed@google.comc0784db2013-12-13 21:16:12 +00001619 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001620 return true;
1621 }
1622 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001623 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624}
1625
reed@google.com3b3e8952012-08-16 20:53:31 +00001626bool SkCanvas::quickReject(const SkPath& path) const {
1627 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628}
1629
reed@google.com3b3e8952012-08-16 20:53:31 +00001630bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001631 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001632 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633 return false;
1634 }
1635
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001636 SkMatrix inverse;
1637 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001638 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001639 if (bounds) {
1640 bounds->setEmpty();
1641 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001642 return false;
1643 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001645 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001646 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001647 // adjust it outwards in case we are antialiasing
1648 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001649
reed@google.com8f4d2302013-12-17 16:44:46 +00001650 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1651 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652 inverse.mapRect(bounds, r);
1653 }
1654 return true;
1655}
1656
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001657bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001658 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001659 if (clip.isEmpty()) {
1660 if (bounds) {
1661 bounds->setEmpty();
1662 }
1663 return false;
1664 }
1665
1666 if (NULL != bounds) {
1667 *bounds = clip.getBounds();
1668 }
1669 return true;
1670}
1671
reed@android.com8a1c16f2008-12-17 15:59:43 +00001672const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001673 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001674}
1675
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001676#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001677SkCanvas::ClipType SkCanvas::getClipType() const {
reed1f836ee2014-07-07 07:49:34 -07001678 if (fMCRec->fRasterClip.isEmpty()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001679 return kEmpty_ClipType;
1680 }
reed1f836ee2014-07-07 07:49:34 -07001681 if (fMCRec->fRasterClip.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001682 return kRect_ClipType;
1683 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001684 return kComplex_ClipType;
1685}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001686#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001687
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001688const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001689 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001690}
1691
1692void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1693 path->reset();
1694
reed1f836ee2014-07-07 07:49:34 -07001695 const SkRegion& rgn = fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001696 if (rgn.isEmpty()) {
1697 return;
1698 }
1699 (void)rgn.getBoundaryPath(path);
1700}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001701
reed@google.com9c135db2014-03-12 18:28:35 +00001702GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1703 SkBaseDevice* dev = this->getTopDevice();
1704 return dev ? dev->accessRenderTarget() : NULL;
1705}
1706
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001707SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001708 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001709 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001710}
1711
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001712GrContext* SkCanvas::getGrContext() {
1713#if SK_SUPPORT_GPU
1714 SkBaseDevice* device = this->getTopDevice();
1715 if (NULL != device) {
1716 GrRenderTarget* renderTarget = device->accessRenderTarget();
1717 if (NULL != renderTarget) {
1718 return renderTarget->getContext();
1719 }
1720 }
1721#endif
1722
1723 return NULL;
1724
1725}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001726
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001727void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1728 const SkPaint& paint) {
1729 if (outer.isEmpty()) {
1730 return;
1731 }
1732 if (inner.isEmpty()) {
1733 this->drawRRect(outer, paint);
1734 return;
1735 }
1736
1737 // We don't have this method (yet), but technically this is what we should
1738 // be able to assert...
1739 // SkASSERT(outer.contains(inner));
1740 //
1741 // For now at least check for containment of bounds
1742 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1743
1744 this->onDrawDRRect(outer, inner, paint);
1745}
1746
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747//////////////////////////////////////////////////////////////////////////////
1748// These are the virtual drawing methods
1749//////////////////////////////////////////////////////////////////////////////
1750
reed@google.com2a981812011-04-14 18:59:28 +00001751void SkCanvas::clear(SkColor color) {
1752 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001753 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001754 while (iter.next()) {
1755 iter.fDevice->clear(color);
1756 }
1757}
1758
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001759void SkCanvas::onDiscard() {
1760 if (NULL != fSurfaceBase) {
1761 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1762 }
1763}
1764
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001766 this->internalDrawPaint(paint);
1767}
1768
1769void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001770 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001771
1772 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001773 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 }
1775
reed@google.com4e2b3d32011-04-07 14:18:59 +00001776 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777}
1778
1779void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1780 const SkPaint& paint) {
1781 if ((long)count <= 0) {
1782 return;
1783 }
1784
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001785 SkRect r, storage;
1786 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001787 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001788 // special-case 2 points (common for drawing a single line)
1789 if (2 == count) {
1790 r.set(pts[0], pts[1]);
1791 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001792 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001793 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001794 bounds = &paint.computeFastStrokeBounds(r, &storage);
1795 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001796 return;
1797 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001798 }
reed@google.coma584aed2012-05-16 14:06:02 +00001799
reed@android.com8a1c16f2008-12-17 15:59:43 +00001800 SkASSERT(pts != NULL);
1801
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001802 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001803
reed@android.com8a1c16f2008-12-17 15:59:43 +00001804 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001805 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001806 }
reed@google.com4b226022011-01-11 18:32:13 +00001807
reed@google.com4e2b3d32011-04-07 14:18:59 +00001808 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001809}
1810
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001811void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001812 SkRect storage;
1813 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001815 bounds = &paint.computeFastBounds(r, &storage);
1816 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001817 return;
1818 }
1819 }
reed@google.com4b226022011-01-11 18:32:13 +00001820
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001821 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001822
1823 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001824 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001825 }
1826
reed@google.com4e2b3d32011-04-07 14:18:59 +00001827 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001828}
1829
reed@google.com4ed0fb72012-12-12 20:48:18 +00001830void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001831 SkRect storage;
1832 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001833 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001834 bounds = &paint.computeFastBounds(oval, &storage);
1835 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001836 return;
1837 }
1838 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001839
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001840 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001841
1842 while (iter.next()) {
1843 iter.fDevice->drawOval(iter, oval, looper.paint());
1844 }
1845
1846 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001847}
1848
1849void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001850 SkRect storage;
1851 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001852 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001853 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1854 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001855 return;
1856 }
1857 }
1858
1859 if (rrect.isRect()) {
1860 // call the non-virtual version
1861 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001862 return;
1863 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001864 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001865 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1866 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001867 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001868
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001869 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001870
1871 while (iter.next()) {
1872 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1873 }
1874
1875 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001876}
1877
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001878void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1879 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001880 SkRect storage;
1881 const SkRect* bounds = NULL;
1882 if (paint.canComputeFastBounds()) {
1883 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1884 if (this->quickReject(*bounds)) {
1885 return;
1886 }
1887 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001888
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001889 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001890
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001891 while (iter.next()) {
1892 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1893 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001894
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001895 LOOPER_END
1896}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001897
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001898void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001899 if (!path.isFinite()) {
1900 return;
1901 }
1902
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001903 SkRect storage;
1904 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001905 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001906 const SkRect& pathBounds = path.getBounds();
1907 bounds = &paint.computeFastBounds(pathBounds, &storage);
1908 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 return;
1910 }
1911 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001912
1913 const SkRect& r = path.getBounds();
1914 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001915 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001916 this->internalDrawPaint(paint);
1917 }
1918 return;
1919 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001921 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001922
1923 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001924 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001925 }
1926
reed@google.com4e2b3d32011-04-07 14:18:59 +00001927 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928}
1929
1930void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1931 const SkPaint* paint) {
1932 SkDEBUGCODE(bitmap.validate();)
1933
reed@google.com3d608122011-11-21 15:16:16 +00001934 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001935 SkRect bounds = {
1936 x, y,
1937 x + SkIntToScalar(bitmap.width()),
1938 y + SkIntToScalar(bitmap.height())
1939 };
1940 if (paint) {
1941 (void)paint->computeFastBounds(bounds, &bounds);
1942 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001943 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944 return;
1945 }
1946 }
reed@google.com4b226022011-01-11 18:32:13 +00001947
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948 SkMatrix matrix;
1949 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001950 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951}
1952
reed@google.com9987ec32011-09-07 11:57:52 +00001953// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001954void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001955 const SkRect& dst, const SkPaint* paint,
1956 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001957 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001958 return;
1959 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001960
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001961 SkRect storage;
1962 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001963 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001964 if (paint) {
1965 bounds = &paint->computeFastBounds(dst, &storage);
1966 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001967 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001968 return;
1969 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001970 }
reed@google.com3d608122011-11-21 15:16:16 +00001971
reed@google.com33535f32012-09-25 15:37:50 +00001972 SkLazyPaint lazy;
1973 if (NULL == paint) {
1974 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001975 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001976
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001977 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001978
reed@google.com33535f32012-09-25 15:37:50 +00001979 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001980 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001981 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001982
reed@google.com33535f32012-09-25 15:37:50 +00001983 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001984}
1985
reed@google.com71121732012-09-18 15:14:33 +00001986void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001987 const SkRect& dst, const SkPaint* paint,
1988 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001989 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001990 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001991}
1992
reed@android.com8a1c16f2008-12-17 15:59:43 +00001993void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1994 const SkPaint* paint) {
1995 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001996 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001997}
1998
reed@google.com9987ec32011-09-07 11:57:52 +00001999void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2000 const SkIRect& center, const SkRect& dst,
2001 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002002 if (bitmap.drawsNothing()) {
2003 return;
2004 }
reed@google.com3d608122011-11-21 15:16:16 +00002005 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002006 SkRect storage;
2007 const SkRect* bounds = &dst;
2008 if (paint) {
2009 bounds = &paint->computeFastBounds(dst, &storage);
2010 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002011 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002012 return;
2013 }
2014 }
2015
reed@google.com9987ec32011-09-07 11:57:52 +00002016 const int32_t w = bitmap.width();
2017 const int32_t h = bitmap.height();
2018
2019 SkIRect c = center;
2020 // pin center to the bounds of the bitmap
2021 c.fLeft = SkMax32(0, center.fLeft);
2022 c.fTop = SkMax32(0, center.fTop);
2023 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2024 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2025
reed@google.com71121732012-09-18 15:14:33 +00002026 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002027 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002028 };
2029 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002030 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002031 };
reed@google.com9987ec32011-09-07 11:57:52 +00002032 SkScalar dstX[4] = {
2033 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2034 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2035 };
2036 SkScalar dstY[4] = {
2037 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2038 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2039 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002040
reed@google.com9987ec32011-09-07 11:57:52 +00002041 if (dstX[1] > dstX[2]) {
2042 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2043 dstX[2] = dstX[1];
2044 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002045
reed@google.com9987ec32011-09-07 11:57:52 +00002046 if (dstY[1] > dstY[2]) {
2047 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2048 dstY[2] = dstY[1];
2049 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002050
reed@google.com9987ec32011-09-07 11:57:52 +00002051 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002052 SkRect s, d;
2053
reed@google.com9987ec32011-09-07 11:57:52 +00002054 s.fTop = srcY[y];
2055 s.fBottom = srcY[y+1];
2056 d.fTop = dstY[y];
2057 d.fBottom = dstY[y+1];
2058 for (int x = 0; x < 3; x++) {
2059 s.fLeft = srcX[x];
2060 s.fRight = srcX[x+1];
2061 d.fLeft = dstX[x];
2062 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002063 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002064 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002065 }
2066 }
2067}
2068
2069void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2070 const SkRect& dst, const SkPaint* paint) {
2071 SkDEBUGCODE(bitmap.validate();)
2072
2073 // Need a device entry-point, so gpu can use a mesh
2074 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2075}
2076
reed@google.comf67e4cf2011-03-15 20:56:58 +00002077class SkDeviceFilteredPaint {
2078public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002079 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2080 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002081 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002082 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002083 newPaint->setFlags(flags.fFlags);
2084 newPaint->setHinting(flags.fHinting);
2085 fPaint = newPaint;
2086 } else {
2087 fPaint = &paint;
2088 }
2089 }
2090
reed@google.comf67e4cf2011-03-15 20:56:58 +00002091 const SkPaint& paint() const { return *fPaint; }
2092
2093private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002094 const SkPaint* fPaint;
2095 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002096};
2097
bungeman@google.com52c748b2011-08-22 21:30:43 +00002098void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2099 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002100 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002101 draw.fDevice->drawRect(draw, r, paint);
2102 } else {
2103 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002104 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002105 draw.fDevice->drawRect(draw, r, p);
2106 }
2107}
2108
2109void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2110 const char text[], size_t byteLength,
2111 SkScalar x, SkScalar y) {
2112 SkASSERT(byteLength == 0 || text != NULL);
2113
2114 // nothing to draw
2115 if (text == NULL || byteLength == 0 ||
2116 draw.fClip->isEmpty() ||
2117 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2118 return;
2119 }
2120
2121 SkScalar width = 0;
2122 SkPoint start;
2123
2124 start.set(0, 0); // to avoid warning
2125 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2126 SkPaint::kStrikeThruText_Flag)) {
2127 width = paint.measureText(text, byteLength);
2128
2129 SkScalar offsetX = 0;
2130 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2131 offsetX = SkScalarHalf(width);
2132 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2133 offsetX = width;
2134 }
2135 start.set(x - offsetX, y);
2136 }
2137
2138 if (0 == width) {
2139 return;
2140 }
2141
2142 uint32_t flags = paint.getFlags();
2143
2144 if (flags & (SkPaint::kUnderlineText_Flag |
2145 SkPaint::kStrikeThruText_Flag)) {
2146 SkScalar textSize = paint.getTextSize();
2147 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2148 SkRect r;
2149
2150 r.fLeft = start.fX;
2151 r.fRight = start.fX + width;
2152
2153 if (flags & SkPaint::kUnderlineText_Flag) {
2154 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2155 start.fY);
2156 r.fTop = offset;
2157 r.fBottom = offset + height;
2158 DrawRect(draw, paint, r, textSize);
2159 }
2160 if (flags & SkPaint::kStrikeThruText_Flag) {
2161 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2162 start.fY);
2163 r.fTop = offset;
2164 r.fBottom = offset + height;
2165 DrawRect(draw, paint, r, textSize);
2166 }
2167 }
2168}
2169
reed@google.come0d9ce82014-04-23 04:00:17 +00002170void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2171 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002172 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173
2174 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002175 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002176 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002177 DrawTextDecorations(iter, dfp.paint(),
2178 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002179 }
2180
reed@google.com4e2b3d32011-04-07 14:18:59 +00002181 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002182}
2183
reed@google.come0d9ce82014-04-23 04:00:17 +00002184void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2185 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002186 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002187
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002189 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002191 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002193
reed@google.com4e2b3d32011-04-07 14:18:59 +00002194 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002195}
2196
reed@google.come0d9ce82014-04-23 04:00:17 +00002197void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2198 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002199 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002200
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002202 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002203 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002204 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002205 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002206
reed@google.com4e2b3d32011-04-07 14:18:59 +00002207 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002208}
2209
reed@google.come0d9ce82014-04-23 04:00:17 +00002210void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2211 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002212 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002213
reed@android.com8a1c16f2008-12-17 15:59:43 +00002214 while (iter.next()) {
2215 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002216 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002217 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002218
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002219 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002220}
2221
reed@google.come0d9ce82014-04-23 04:00:17 +00002222// These will become non-virtual, so they always call the (virtual) onDraw... method
2223void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2224 const SkPaint& paint) {
2225 this->onDrawText(text, byteLength, x, y, paint);
2226}
2227void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2228 const SkPaint& paint) {
2229 this->onDrawPosText(text, byteLength, pos, paint);
2230}
2231void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2232 SkScalar constY, const SkPaint& paint) {
2233 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2234}
2235void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2236 const SkMatrix* matrix, const SkPaint& paint) {
2237 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2238}
2239
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2241 const SkPoint verts[], const SkPoint texs[],
2242 const SkColor colors[], SkXfermode* xmode,
2243 const uint16_t indices[], int indexCount,
2244 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002245 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002246
reed@android.com8a1c16f2008-12-17 15:59:43 +00002247 while (iter.next()) {
2248 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002249 colors, xmode, indices, indexCount,
2250 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002251 }
reed@google.com4b226022011-01-11 18:32:13 +00002252
reed@google.com4e2b3d32011-04-07 14:18:59 +00002253 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254}
2255
dandovecfff212014-08-04 10:02:00 -07002256void SkCanvas::drawPatch(const SkPatch& patch, const SkPaint& paint) {
2257
2258 // Since a patch is always within the convex hull of the control points, we discard it when its
2259 // bounding rectangle is completely outside the current clip.
2260 SkRect bounds;
2261 bounds.set(patch.getControlPoints(), 12);
2262 if (this->quickReject(bounds)) {
2263 return;
2264 }
2265
2266 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
2267
2268 while (iter.next()) {
2269 iter.fDevice->drawPatch(iter, patch, paint);
2270 }
2271
2272 LOOPER_END
2273}
2274
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275//////////////////////////////////////////////////////////////////////////////
2276// These methods are NOT virtual, and therefore must call back into virtual
2277// methods, rather than actually drawing themselves.
2278//////////////////////////////////////////////////////////////////////////////
2279
2280void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002281 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282 SkPaint paint;
2283
2284 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002285 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002286 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287 }
2288 this->drawPaint(paint);
2289}
2290
reed@android.com845fdac2009-06-23 03:01:32 +00002291void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292 SkPaint paint;
2293
2294 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002295 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002296 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297 }
2298 this->drawPaint(paint);
2299}
2300
2301void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2302 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002303
reed@android.com8a1c16f2008-12-17 15:59:43 +00002304 pt.set(x, y);
2305 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2306}
2307
2308void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2309 SkPoint pt;
2310 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002311
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312 pt.set(x, y);
2313 paint.setColor(color);
2314 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2315}
2316
2317void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2318 const SkPaint& paint) {
2319 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002320
reed@android.com8a1c16f2008-12-17 15:59:43 +00002321 pts[0].set(x0, y0);
2322 pts[1].set(x1, y1);
2323 this->drawPoints(kLines_PointMode, 2, pts, paint);
2324}
2325
2326void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2327 SkScalar right, SkScalar bottom,
2328 const SkPaint& paint) {
2329 SkRect r;
2330
2331 r.set(left, top, right, bottom);
2332 this->drawRect(r, paint);
2333}
2334
2335void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2336 const SkPaint& paint) {
2337 if (radius < 0) {
2338 radius = 0;
2339 }
2340
2341 SkRect r;
2342 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002343 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344}
2345
2346void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2347 const SkPaint& paint) {
2348 if (rx > 0 && ry > 0) {
2349 if (paint.canComputeFastBounds()) {
2350 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002351 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352 return;
2353 }
2354 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002355 SkRRect rrect;
2356 rrect.setRectXY(r, rx, ry);
2357 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002358 } else {
2359 this->drawRect(r, paint);
2360 }
2361}
2362
reed@android.com8a1c16f2008-12-17 15:59:43 +00002363void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2364 SkScalar sweepAngle, bool useCenter,
2365 const SkPaint& paint) {
2366 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2367 this->drawOval(oval, paint);
2368 } else {
2369 SkPath path;
2370 if (useCenter) {
2371 path.moveTo(oval.centerX(), oval.centerY());
2372 }
2373 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2374 if (useCenter) {
2375 path.close();
2376 }
2377 this->drawPath(path, paint);
2378 }
2379}
2380
2381void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2382 const SkPath& path, SkScalar hOffset,
2383 SkScalar vOffset, const SkPaint& paint) {
2384 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002385
reed@android.com8a1c16f2008-12-17 15:59:43 +00002386 matrix.setTranslate(hOffset, vOffset);
2387 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2388}
2389
reed@android.comf76bacf2009-05-13 14:00:33 +00002390///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002391void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002392 SkBaseDevice* device = this->getDevice();
2393 if (NULL != device) {
2394 device->EXPERIMENTAL_optimize(picture);
2395 }
2396}
reed@android.comf76bacf2009-05-13 14:00:33 +00002397
robertphillips9b14f262014-06-04 05:40:44 -07002398void SkCanvas::drawPicture(const SkPicture* picture) {
2399 if (NULL != picture) {
2400 this->onDrawPicture(picture);
2401 }
2402}
2403
2404void SkCanvas::onDrawPicture(const SkPicture* picture) {
2405 SkASSERT(NULL != picture);
2406
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002407 SkBaseDevice* device = this->getTopDevice();
2408 if (NULL != device) {
2409 // Canvas has to first give the device the opportunity to render
2410 // the picture itself.
robertphillips9b14f262014-06-04 05:40:44 -07002411 if (device->EXPERIMENTAL_drawPicture(this, picture)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002412 return; // the device has rendered the entire picture
2413 }
2414 }
2415
robertphillips9b14f262014-06-04 05:40:44 -07002416 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002417}
2418
reed@android.com8a1c16f2008-12-17 15:59:43 +00002419///////////////////////////////////////////////////////////////////////////////
2420///////////////////////////////////////////////////////////////////////////////
2421
2422SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002423 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002424
2425 SkASSERT(canvas);
2426
2427 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2428 fDone = !fImpl->next();
2429}
2430
2431SkCanvas::LayerIter::~LayerIter() {
2432 fImpl->~SkDrawIter();
2433}
2434
2435void SkCanvas::LayerIter::next() {
2436 fDone = !fImpl->next();
2437}
2438
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002439SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440 return fImpl->getDevice();
2441}
2442
2443const SkMatrix& SkCanvas::LayerIter::matrix() const {
2444 return fImpl->getMatrix();
2445}
2446
2447const SkPaint& SkCanvas::LayerIter::paint() const {
2448 const SkPaint* paint = fImpl->getPaint();
2449 if (NULL == paint) {
2450 paint = &fDefaultPaint;
2451 }
2452 return *paint;
2453}
2454
2455const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2456int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2457int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002458
2459///////////////////////////////////////////////////////////////////////////////
2460
fmalitac3b589a2014-06-05 12:40:07 -07002461SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002462
2463///////////////////////////////////////////////////////////////////////////////
2464
2465static bool supported_for_raster_canvas(const SkImageInfo& info) {
2466 switch (info.alphaType()) {
2467 case kPremul_SkAlphaType:
2468 case kOpaque_SkAlphaType:
2469 break;
2470 default:
2471 return false;
2472 }
2473
2474 switch (info.colorType()) {
2475 case kAlpha_8_SkColorType:
2476 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002477 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002478 break;
2479 default:
2480 return false;
2481 }
2482
2483 return true;
2484}
2485
2486SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2487 if (!supported_for_raster_canvas(info)) {
2488 return NULL;
2489 }
2490
2491 SkBitmap bitmap;
2492 if (!bitmap.allocPixels(info)) {
2493 return NULL;
2494 }
2495
2496 // should this functionality be moved into allocPixels()?
2497 if (!bitmap.info().isOpaque()) {
2498 bitmap.eraseColor(0);
2499 }
2500 return SkNEW_ARGS(SkCanvas, (bitmap));
2501}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002502
2503SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2504 if (!supported_for_raster_canvas(info)) {
2505 return NULL;
2506 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002507
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002508 SkBitmap bitmap;
2509 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2510 return NULL;
2511 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002512 return SkNEW_ARGS(SkCanvas, (bitmap));
2513}