blob: c6b57396e0dcf08a4e81d5160c0aa019b027a4fb [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
reed9f014712014-06-18 15:51:20 -07008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000012#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000016#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000017#include "SkPathOps.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000019#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000020#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000021#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000022#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkTemplates.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000024#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000025#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000028#if SK_SUPPORT_GPU
29#include "GrRenderTarget.h"
30#endif
31
reed@google.comda17f752012-08-16 18:27:05 +000032// experimental for faster tiled drawing...
33//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000034
reed@android.com8a1c16f2008-12-17 15:59:43 +000035//#define SK_TRACE_SAVERESTORE
36
37#ifdef SK_TRACE_SAVERESTORE
38 static int gLayerCounter;
39 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
40 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
41
42 static int gRecCounter;
43 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
44 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
45
46 static int gCanvasCounter;
47 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
48 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
49#else
50 #define inc_layer()
51 #define dec_layer()
52 #define inc_rec()
53 #define dec_rec()
54 #define inc_canvas()
55 #define dec_canvas()
56#endif
57
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000058typedef SkTLazy<SkPaint> SkLazyPaint;
59
reed@google.com97af1a62012-08-28 12:19:02 +000060void SkCanvas::predrawNotify() {
61 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000062 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000063 }
64}
65
reed@android.com8a1c16f2008-12-17 15:59:43 +000066///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000067
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000068/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000069 The clip/matrix/proc are fields that reflect the top of the save/restore
70 stack. Whenever the canvas changes, it marks a dirty flag, and then before
71 these are used (assuming we're not on a layer) we rebuild these cache
72 values: they reflect the top of the save stack, but translated and clipped
73 by the device's XY offset and bitmap-bounds.
74*/
75struct DeviceCM {
76 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000077 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000078 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000079 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000080 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000081
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000082 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 : fNext(NULL) {
84 if (NULL != device) {
85 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000086 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 }
reed@google.com4b226022011-01-11 18:32:13 +000088 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000089 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000090 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000091
bungeman@google.com88edf1e2011-08-08 19:41:56 +000092 ~DeviceCM() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 if (NULL != fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000094 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +000095 fDevice->unref();
96 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +000097 SkDELETE(fPaint);
98 }
reed@google.com4b226022011-01-11 18:32:13 +000099
reed@google.com045e62d2011-10-24 12:19:46 +0000100 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
101 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000102 int x = fDevice->getOrigin().x();
103 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 int width = fDevice->width();
105 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000106
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 if ((x | y) == 0) {
108 fMatrix = &totalMatrix;
109 fClip = totalClip;
110 } else {
111 fMatrixStorage = totalMatrix;
112 fMatrixStorage.postTranslate(SkIntToScalar(-x),
113 SkIntToScalar(-y));
114 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000115
reed@android.com8a1c16f2008-12-17 15:59:43 +0000116 totalClip.translate(-x, -y, &fClip);
117 }
118
reed@google.com045e62d2011-10-24 12:19:46 +0000119 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000120
121 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000122
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000124 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 SkRegion::kDifference_Op);
126 }
reed@google.com4b226022011-01-11 18:32:13 +0000127
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000128 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
129
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130#ifdef SK_DEBUG
131 if (!fClip.isEmpty()) {
132 SkIRect deviceR;
133 deviceR.set(0, 0, width, height);
134 SkASSERT(deviceR.contains(fClip.getBounds()));
135 }
136#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000137 }
138
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000140 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141};
142
143/* This is the record we keep for each save/restore level in the stack.
144 Since a level optionally copies the matrix and/or stack, we have pointers
145 for these fields. If the value is copied for this level, the copy is
146 stored in the ...Storage field, and the pointer points to that. If the
147 value is not copied for this level, we ignore ...Storage, and just point
148 at the corresponding value in the previous level in the stack.
149*/
150class SkCanvas::MCRec {
151public:
reed1f836ee2014-07-07 07:49:34 -0700152 SkMatrix fMatrix;
153 SkRasterClip fRasterClip;
154 SkDrawFilter* fFilter; // the current filter (or null)
reed@google.com4b226022011-01-11 18:32:13 +0000155
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156 DeviceCM* fLayer;
157 /* If there are any layers in the stack, this points to the top-most
158 one that is at or below this level in the stack (so we know what
159 bitmap/device to draw into from this level. This value is NOT
160 reference counted, since the real owner is either our fLayer field,
161 or a previous one in a lower level.)
162 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000163 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164
Florin Malita5f6102d2014-06-30 10:13:28 -0400165 MCRec(const MCRec* prev) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 if (NULL != prev) {
reed1f836ee2014-07-07 07:49:34 -0700167 fMatrix = prev->fMatrix;
168 fRasterClip = prev->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169
170 fFilter = prev->fFilter;
reed@google.com82065d62011-02-07 15:30:46 +0000171 SkSafeRef(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172
173 fTopLayer = prev->fTopLayer;
174 } else { // no prev
reed1f836ee2014-07-07 07:49:34 -0700175 fMatrix.reset();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 fFilter = NULL;
177 fTopLayer = NULL;
178 }
179 fLayer = NULL;
180
181 // don't bother initializing fNext
182 inc_rec();
183 }
184 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000185 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 SkDELETE(fLayer);
187 dec_rec();
188 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189};
190
191class SkDrawIter : public SkDraw {
192public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000193 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000194 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000195 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 canvas->updateDeviceCMCache();
197
reed@google.com90c07ea2012-04-13 13:50:27 +0000198 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000200 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 }
reed@google.com4b226022011-01-11 18:32:13 +0000202
reed@android.com8a1c16f2008-12-17 15:59:43 +0000203 bool next() {
204 // skip over recs with empty clips
205 if (fSkipEmptyClips) {
206 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
207 fCurrLayer = fCurrLayer->fNext;
208 }
209 }
210
reed@google.comf68c5e22012-02-24 16:38:58 +0000211 const DeviceCM* rec = fCurrLayer;
212 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213
214 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000215 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
216 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 fDevice = rec->fDevice;
218 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000220 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221
222 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000223 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000224
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 return true;
226 }
227 return false;
228 }
reed@google.com4b226022011-01-11 18:32:13 +0000229
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000230 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000231 int getX() const { return fDevice->getOrigin().x(); }
232 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 const SkMatrix& getMatrix() const { return *fMatrix; }
234 const SkRegion& getClip() const { return *fClip; }
235 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000236
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237private:
238 SkCanvas* fCanvas;
239 const DeviceCM* fCurrLayer;
240 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 SkBool8 fSkipEmptyClips;
242
243 typedef SkDraw INHERITED;
244};
245
246/////////////////////////////////////////////////////////////////////////////
247
248class AutoDrawLooper {
249public:
reed@google.com8926b162012-03-23 15:36:36 +0000250 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000251 bool skipLayerForImageFilter = false,
252 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000253 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254 fFilter = canvas->getDrawFilter();
reed@google.com4e2b3d32011-04-07 14:18:59 +0000255 fPaint = NULL;
256 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000257 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000258 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259
reed@google.com8926b162012-03-23 15:36:36 +0000260 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
261 SkPaint tmp;
262 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000263 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
264 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000265 // we'll clear the imageFilter for the actual draws in next(), so
266 // it will only be applied during the restore().
267 fDoClearImageFilter = true;
268 }
269
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000270 if (SkDrawLooper* looper = paint.getLooper()) {
271 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
272 looper->contextSize());
273 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000274 fIsSimple = false;
275 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000276 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000277 // can we be marked as simple?
278 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000279 }
280 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000281
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000283 if (fDoClearImageFilter) {
284 fCanvas->internalRestore();
285 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000286 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000288
reed@google.com4e2b3d32011-04-07 14:18:59 +0000289 const SkPaint& paint() const {
290 SkASSERT(fPaint);
291 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000293
reed@google.com129ec222012-05-15 13:24:09 +0000294 bool next(SkDrawFilter::Type drawType) {
295 if (fDone) {
296 return false;
297 } else if (fIsSimple) {
298 fDone = true;
299 fPaint = &fOrigPaint;
300 return !fPaint->nothingToDraw();
301 } else {
302 return this->doNext(drawType);
303 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000304 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000305
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000307 SkLazyPaint fLazyPaint;
308 SkCanvas* fCanvas;
309 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000310 SkDrawFilter* fFilter;
311 const SkPaint* fPaint;
312 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000313 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000314 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000315 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000316 SkDrawLooper::Context* fLooperContext;
317 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000318
319 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320};
321
reed@google.com129ec222012-05-15 13:24:09 +0000322bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000323 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000324 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000325 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000326
327 SkPaint* paint = fLazyPaint.set(fOrigPaint);
328
329 if (fDoClearImageFilter) {
330 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000331 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000332
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000333 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000334 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000335 return false;
336 }
337 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000338 if (!fFilter->filter(paint, drawType)) {
339 fDone = true;
340 return false;
341 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000342 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000343 // no looper means we only draw once
344 fDone = true;
345 }
346 }
347 fPaint = paint;
348
349 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000350 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000351 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000352 }
353
354 // call this after any possible paint modifiers
355 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000356 fPaint = NULL;
357 return false;
358 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000359 return true;
360}
361
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362#include "SkColorPriv.h"
363
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364////////// macros to place around the internal draw calls //////////////////
365
reed@google.com8926b162012-03-23 15:36:36 +0000366#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000367 this->predrawNotify(); \
reed@google.com8926b162012-03-23 15:36:36 +0000368 AutoDrawLooper looper(this, paint, true); \
369 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000370 SkDrawIter iter(this);
371
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000372#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000373 this->predrawNotify(); \
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000374 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000375 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000377
reed@google.com4e2b3d32011-04-07 14:18:59 +0000378#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379
380////////////////////////////////////////////////////////////////////////////
381
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000382SkBaseDevice* SkCanvas::init(SkBaseDevice* device) {
reed@google.comc0784db2013-12-13 21:16:12 +0000383 fCachedLocalClipBounds.setEmpty();
384 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000385 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000386 fAllowSimplifyClip = false;
agl@chromium.org447bcfa2009-12-12 00:06:17 +0000387 fDeviceCMDirty = false;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000388 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000389 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000390 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000391
392 fMCRec = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400393 new (fMCRec) MCRec(NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000394
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000395 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000396 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000397
reed@google.com97af1a62012-08-28 12:19:02 +0000398 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000399
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000400 return this->setRootDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000401}
402
reed@google.comcde92112011-07-06 20:00:52 +0000403SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000404 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
405{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000406 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000407
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000408 this->init(NULL);
409}
410
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000411SkCanvas::SkCanvas(int width, int height)
412 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
413{
414 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000415
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000416 SkBitmap bitmap;
commit-bot@chromium.orga3264e52014-05-30 13:26:10 +0000417 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000418 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
419}
420
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000421SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000422 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
423{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 inc_canvas();
425
426 this->init(device);
427}
428
429SkCanvas::SkCanvas(const SkBitmap& bitmap)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000430 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
431{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432 inc_canvas();
433
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000434 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)))->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000435}
436
437SkCanvas::~SkCanvas() {
438 // free up the contents of our deque
439 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000440 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000441
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442 this->internalRestore(); // restore the last, since we're going away
443
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000444 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000445
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 dec_canvas();
447}
448
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449SkDrawFilter* SkCanvas::getDrawFilter() const {
450 return fMCRec->fFilter;
451}
452
453SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
454 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
455 return filter;
456}
457
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000458SkMetaData& SkCanvas::getMetaData() {
459 // metadata users are rare, so we lazily allocate it. If that changes we
460 // can decide to just make it a field in the device (rather than a ptr)
461 if (NULL == fMetaData) {
462 fMetaData = new SkMetaData;
463 }
464 return *fMetaData;
465}
466
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467///////////////////////////////////////////////////////////////////////////////
468
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000469void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000470 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000471 if (device) {
472 device->flush();
473 }
474}
475
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000476SkISize SkCanvas::getTopLayerSize() const {
477 SkBaseDevice* d = this->getTopDevice();
478 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
479}
480
481SkIPoint SkCanvas::getTopLayerOrigin() const {
482 SkBaseDevice* d = this->getTopDevice();
483 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
484}
485
486SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000487 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000488 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
489}
490
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000491SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000492 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000493 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 SkASSERT(rec && rec->fLayer);
495 return rec->fLayer->fDevice;
496}
497
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000498SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000499 if (updateMatrixClip) {
500 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
501 }
reed@google.com9266fed2011-03-30 00:18:03 +0000502 return fMCRec->fTopLayer->fDevice;
503}
504
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000505SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000506 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000507 SkDeque::F2BIter iter(fMCStack);
508 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000510 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511
512 if (rootDevice == device) {
513 return device;
514 }
reed@google.com4b226022011-01-11 18:32:13 +0000515
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000517 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518 }
519 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000520 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 }
522
523 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
524 rootDevice = device;
525
526 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000527
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 /* Now we update our initial region to have the bounds of the new device,
529 and then intersect all of the clips in our stack with these bounds,
530 to ensure that we can't draw outside of the device's bounds (and trash
531 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533 NOTE: this is only a partial-fix, since if the new device is larger than
534 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000535 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
537 reconstruct the correct clips, so this approximation will have to do.
538 The caller really needs to restore() back to the base if they want to
539 accurately take advantage of the new device bounds.
540 */
541
reed@google.com42aea282012-03-28 16:19:15 +0000542 SkIRect bounds;
543 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000545 } else {
546 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547 }
reed@google.com42aea282012-03-28 16:19:15 +0000548 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700549 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000550 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700551 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000552 }
553
reed@android.com8a1c16f2008-12-17 15:59:43 +0000554 return device;
555}
556
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000557bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
558 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
559 return false;
560 }
561
562 bool weAllocated = false;
563 if (NULL == bitmap->pixelRef()) {
564 if (!bitmap->allocPixels()) {
565 return false;
566 }
567 weAllocated = true;
568 }
569
570 SkBitmap bm(*bitmap);
571 bm.lockPixels();
572 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
573 return true;
574 }
575
576 if (weAllocated) {
577 bitmap->setPixelRef(NULL);
578 }
579 return false;
580}
reed@google.com51df9e32010-12-23 19:29:18 +0000581
bsalomon@google.comc6980972011-11-02 19:57:21 +0000582bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000583 SkIRect r = srcRect;
584 const SkISize size = this->getBaseLayerSize();
585 if (!r.intersect(0, 0, size.width(), size.height())) {
586 bitmap->reset();
587 return false;
588 }
589
590 if (!bitmap->allocN32Pixels(r.width(), r.height())) {
591 // bitmap will already be reset.
592 return false;
593 }
594 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
595 bitmap->reset();
596 return false;
597 }
598 return true;
599}
600
601bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
602 switch (origInfo.colorType()) {
603 case kUnknown_SkColorType:
604 case kIndex_8_SkColorType:
605 return false;
606 default:
607 break;
608 }
609 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
610 return false;
611 }
612 if (0 == origInfo.width() || 0 == origInfo.height()) {
613 return false;
614 }
615
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000616 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000617 if (!device) {
618 return false;
619 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000620
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000621 const SkISize size = this->getBaseLayerSize();
622 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
623 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000624 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000625 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000626
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000627 SkImageInfo info = origInfo;
628 // the intersect may have shrunk info's logical size
629 info.fWidth = srcR.width();
630 info.fHeight = srcR.height();
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000631
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000632 // if x or y are negative, then we have to adjust pixels
633 if (x > 0) {
634 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000635 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000636 if (y > 0) {
637 y = 0;
638 }
639 // here x,y are either 0 or negative
640 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000641
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000642 // The device can assert that the requested area is always contained in its bounds
643 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000644}
645
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000646bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
647 if (bitmap.getTexture()) {
648 return false;
649 }
650 SkBitmap bm(bitmap);
651 bm.lockPixels();
652 if (bm.getPixels()) {
653 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
654 }
655 return false;
656}
657
658bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
659 int x, int y) {
660 switch (origInfo.colorType()) {
661 case kUnknown_SkColorType:
662 case kIndex_8_SkColorType:
663 return false;
664 default:
665 break;
666 }
667 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
668 return false;
669 }
670
671 const SkISize size = this->getBaseLayerSize();
672 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
673 if (!target.intersect(0, 0, size.width(), size.height())) {
674 return false;
675 }
676
677 SkBaseDevice* device = this->getDevice();
678 if (!device) {
679 return false;
680 }
681
682 SkImageInfo info = origInfo;
683 // the intersect may have shrunk info's logical size
684 info.fWidth = target.width();
685 info.fHeight = target.height();
686
687 // if x or y are negative, then we have to adjust pixels
688 if (x > 0) {
689 x = 0;
690 }
691 if (y > 0) {
692 y = 0;
693 }
694 // here x,y are either 0 or negative
695 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
696
reed4af35f32014-06-27 17:47:49 -0700697 // Tell our owning surface to bump its generation ID
698 this->predrawNotify();
699
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000700 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000701 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000702}
reed@google.com51df9e32010-12-23 19:29:18 +0000703
junov@google.com4370aed2012-01-18 16:21:08 +0000704SkCanvas* SkCanvas::canvasForDrawIter() {
705 return this;
706}
707
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708//////////////////////////////////////////////////////////////////////////////
709
reed@android.com8a1c16f2008-12-17 15:59:43 +0000710void SkCanvas::updateDeviceCMCache() {
711 if (fDeviceCMDirty) {
712 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700713 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000714 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000715
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000717 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000718 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000719 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000721 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 } while ((layer = layer->fNext) != NULL);
723 }
724 fDeviceCMDirty = false;
725 }
726}
727
reed@android.com8a1c16f2008-12-17 15:59:43 +0000728///////////////////////////////////////////////////////////////////////////////
729
Florin Malita5f6102d2014-06-30 10:13:28 -0400730int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000731 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000732
reed@android.com8a1c16f2008-12-17 15:59:43 +0000733 MCRec* newTop = (MCRec*)fMCStack.push_back();
Florin Malita5f6102d2014-06-30 10:13:28 -0400734 new (newTop) MCRec(fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000736
Florin Malita5f6102d2014-06-30 10:13:28 -0400737 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000738
reed@android.com8a1c16f2008-12-17 15:59:43 +0000739 return saveCount;
740}
741
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000742int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400743 this->willSave();
744 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745}
746
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000748#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000750#else
751 return true;
752#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000753}
754
junov@chromium.orga907ac32012-02-24 21:54:07 +0000755bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000756 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000757 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000758 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000759 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000760 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000761 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000762
763 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700764 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000765 // Filters may grow the bounds beyond the device bounds.
766 op = SkRegion::kReplace_Op;
767 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000768 SkIRect ir;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 if (NULL != bounds) {
770 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000771
reed@android.com8a1c16f2008-12-17 15:59:43 +0000772 this->getTotalMatrix().mapRect(&r, *bounds);
773 r.roundOut(&ir);
774 // early exit if the layer's bounds are clipped out
775 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000776 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700777 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000778 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000779 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780 }
781 } else { // no user bounds, so just use the clip
782 ir = clipBounds;
783 }
784
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000785 if (bounds_affects_clip(flags)) {
786 fClipStack.clipDevRect(ir, op);
787 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700788 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000789 return false;
790 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000791 }
792
793 if (intersection) {
794 *intersection = ir;
795 }
796 return true;
797}
798
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000799int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
800 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
801 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
802}
803
junov@chromium.orga907ac32012-02-24 21:54:07 +0000804int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
805 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000806 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
807 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000808}
809
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000810int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
811 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000812#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000813 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000814#endif
815
junov@chromium.orga907ac32012-02-24 21:54:07 +0000816 // do this before we create the layer. We don't call the public save() since
817 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400818 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000819
820 fDeviceCMDirty = true;
821
822 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000823 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 return count;
825 }
826
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000827 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
828 // the clipRectBounds() call above?
829 if (kNoLayer_SaveLayerStrategy == strategy) {
830 return count;
831 }
832
reed@google.comb55deeb2012-01-06 14:43:09 +0000833 // Kill the imagefilter if our device doesn't allow it
834 SkLazyPaint lazyP;
835 if (paint && paint->getImageFilter()) {
836 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000837 if (justForImageFilter) {
838 // early exit if the layer was just for the imageFilter
839 return count;
840 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000841 SkPaint* p = lazyP.set(*paint);
842 p->setImageFilter(NULL);
843 paint = p;
844 }
845 }
846
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000847 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
848 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
849 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000851 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000852 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700853 device = this->getDevice();
854 if (device) {
855 device = device->createCompatibleDevice(info);
856 }
reed@google.com76dd2772012-01-05 21:15:07 +0000857 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000858 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000859 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000860 if (NULL == device) {
861 SkDebugf("Unable to create device for layer.");
862 return count;
863 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000864
reed@google.com6f8f2922011-03-04 22:27:10 +0000865 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000866 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000867 device->unref();
868
869 layer->fNext = fMCRec->fTopLayer;
870 fMCRec->fLayer = layer;
871 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
872
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000873 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000874 return count;
875}
876
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000877int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
878 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
879}
880
reed@android.com8a1c16f2008-12-17 15:59:43 +0000881int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
882 SaveFlags flags) {
883 if (0xFF == alpha) {
884 return this->saveLayer(bounds, NULL, flags);
885 } else {
886 SkPaint tmpPaint;
887 tmpPaint.setAlpha(alpha);
888 return this->saveLayer(bounds, &tmpPaint, flags);
889 }
890}
891
892void SkCanvas::restore() {
893 // check for underflow
894 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000895 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000896 this->internalRestore();
897 }
898}
899
900void SkCanvas::internalRestore() {
901 SkASSERT(fMCStack.count() != 0);
902
903 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000904 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905
Florin Malita5f6102d2014-06-30 10:13:28 -0400906 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000907
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000908 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000909 DeviceCM* layer = fMCRec->fLayer; // may be null
910 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
911 fMCRec->fLayer = NULL;
912
913 // now do the normal restore()
914 fMCRec->~MCRec(); // balanced in save()
915 fMCStack.pop_back();
916 fMCRec = (MCRec*)fMCStack.back();
917
918 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
919 since if we're being recorded, we don't want to record this (the
920 recorder will have already recorded the restore).
921 */
922 if (NULL != layer) {
923 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000924 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000925 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
926 layer->fPaint);
927 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000928 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000929
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000930 SkASSERT(fSaveLayerCount > 0);
931 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000932 }
933 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000934 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935}
936
937int SkCanvas::getSaveCount() const {
938 return fMCStack.count();
939}
940
941void SkCanvas::restoreToCount(int count) {
942 // sanity check
943 if (count < 1) {
944 count = 1;
945 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000946
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000947 int n = this->getSaveCount() - count;
948 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 this->restore();
950 }
951}
952
reed@google.com7c202932011-12-14 18:48:05 +0000953bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000954 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000955}
956
reed@google.com76f10a32014-02-05 15:32:21 +0000957SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
958 return this->onNewSurface(info);
959}
960
961SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
962 SkBaseDevice* dev = this->getDevice();
963 return dev ? dev->newSurface(info) : NULL;
964}
965
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000966SkImageInfo SkCanvas::imageInfo() const {
967 SkBaseDevice* dev = this->getDevice();
968 if (dev) {
969 return dev->imageInfo();
970 } else {
reed@google.com900ecf22014-02-20 20:55:37 +0000971 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000972 }
973}
974
975const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
976 return this->onPeekPixels(info, rowBytes);
977}
978
979const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
980 SkBaseDevice* dev = this->getDevice();
981 return dev ? dev->peekPixels(info, rowBytes) : NULL;
982}
983
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +0000984void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
985 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
986 if (pixels && origin) {
987 *origin = this->getTopDevice(false)->getOrigin();
988 }
989 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +0000990}
991
992void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
993 SkBaseDevice* dev = this->getTopDevice();
994 return dev ? dev->accessPixels(info, rowBytes) : NULL;
995}
996
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +0000997SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
998 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
999 if (NULL == fAddr) {
1000 fInfo = canvas->imageInfo();
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +00001001 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.allocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001002 return; // failure, fAddr is NULL
1003 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001004 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1005 return; // failure, fAddr is NULL
1006 }
1007 fAddr = fBitmap.getPixels();
1008 fRowBytes = fBitmap.rowBytes();
1009 }
1010 SkASSERT(fAddr); // success
1011}
1012
1013bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1014 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001015 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001016 } else {
1017 bitmap->reset();
1018 return false;
1019 }
1020}
1021
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001022void SkCanvas::onPushCull(const SkRect& cullRect) {
1023 // do nothing. Subclasses may do something
1024}
1025
1026void SkCanvas::onPopCull() {
1027 // do nothing. Subclasses may do something
1028}
1029
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001031#ifdef SK_DEBUG
1032// Ensure that cull rects are monotonically nested in device space.
1033void SkCanvas::validateCull(const SkIRect& devCull) {
1034 if (fCullStack.isEmpty()
1035 || devCull.isEmpty()
1036 || fCullStack.top().contains(devCull)) {
1037 return;
1038 }
1039
1040 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1041 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1042 fCullStack.top().x(), fCullStack.top().y(),
1043 fCullStack.top().right(), fCullStack.top().bottom()));
1044
1045#ifdef ASSERT_NESTED_CULLING
1046 SkDEBUGFAIL("Invalid cull.");
1047#endif
1048}
1049#endif
1050
1051void SkCanvas::pushCull(const SkRect& cullRect) {
1052 ++fCullCount;
1053 this->onPushCull(cullRect);
1054
1055#ifdef SK_DEBUG
1056 // Map the cull rect into device space.
1057 SkRect mappedCull;
1058 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1059
1060 // Take clipping into account.
1061 SkIRect devClip, devCull;
1062 mappedCull.roundOut(&devCull);
1063 this->getClipDeviceBounds(&devClip);
1064 if (!devCull.intersect(devClip)) {
1065 devCull.setEmpty();
1066 }
1067
1068 this->validateCull(devCull);
1069 fCullStack.push(devCull); // balanced in popCull
1070#endif
1071}
1072
1073void SkCanvas::popCull() {
1074 SkASSERT(fCullStack.count() == fCullCount);
1075
1076 if (fCullCount > 0) {
1077 --fCullCount;
1078 this->onPopCull();
1079
1080 SkDEBUGCODE(fCullStack.pop());
1081 }
1082}
1083
1084/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001086void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001088 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 return;
1090 }
1091
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001092 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001094 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001096
1097 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001098
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001099 SkRect storage;
1100 const SkRect* bounds = NULL;
1101 if (paint && paint->canComputeFastBounds()) {
1102 bitmap.getBounds(&storage);
1103 matrix.mapRect(&storage);
1104 bounds = &paint->computeFastBounds(storage, &storage);
1105 }
1106
1107 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001108
1109 while (iter.next()) {
1110 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1111 }
1112
1113 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001114}
1115
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001116void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001117 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 SkPaint tmp;
1119 if (NULL == paint) {
1120 tmp.setDither(true);
1121 paint = &tmp;
1122 }
reed@google.com4b226022011-01-11 18:32:13 +00001123
reed@google.com8926b162012-03-23 15:36:36 +00001124 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001126 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001127 paint = &looper.paint();
1128 SkImageFilter* filter = paint->getImageFilter();
1129 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001130 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001131 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001132 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001133 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001134 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001135 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001136 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001137 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001138 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001139 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001140 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001141 SkPaint tmpUnfiltered(*paint);
1142 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001143 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1144 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001145 }
1146 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001147 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001148 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001149 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001150 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151}
1152
reed@google.com8926b162012-03-23 15:36:36 +00001153void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1154 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001155 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001156 return;
1157 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001158 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001159
reed@google.com8926b162012-03-23 15:36:36 +00001160 SkPaint tmp;
1161 if (NULL == paint) {
1162 paint = &tmp;
1163 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001164
reed@google.com8926b162012-03-23 15:36:36 +00001165 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001166
reed@google.com8926b162012-03-23 15:36:36 +00001167 while (iter.next()) {
1168 paint = &looper.paint();
1169 SkImageFilter* filter = paint->getImageFilter();
1170 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1171 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001172 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001173 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001174 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001175 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001176 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001177 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001178 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001179 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001180 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001181 SkPaint tmpUnfiltered(*paint);
1182 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001183 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001184 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001185 }
1186 } else {
1187 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1188 }
1189 }
1190 LOOPER_END
1191}
1192
reed@android.com8a1c16f2008-12-17 15:59:43 +00001193/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001194void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001195 SkMatrix m;
1196 m.setTranslate(dx, dy);
1197 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198}
1199
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001200void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001201 SkMatrix m;
1202 m.setScale(sx, sy);
1203 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204}
1205
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001206void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001207 SkMatrix m;
1208 m.setRotate(degrees);
1209 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001210}
1211
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001212void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001213 SkMatrix m;
1214 m.setSkew(sx, sy);
1215 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001216}
1217
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001218void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001219 if (matrix.isIdentity()) {
1220 return;
1221 }
1222
reed@android.com8a1c16f2008-12-17 15:59:43 +00001223 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001224 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001225 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001226
1227 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001228}
1229
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230void SkCanvas::setMatrix(const SkMatrix& matrix) {
1231 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001232 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001233 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001234 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235}
1236
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237void SkCanvas::resetMatrix() {
1238 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001239
reed@android.com8a1c16f2008-12-17 15:59:43 +00001240 matrix.reset();
1241 this->setMatrix(matrix);
1242}
1243
1244//////////////////////////////////////////////////////////////////////////////
1245
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001246void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001247 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1248 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001249}
1250
1251void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001252#ifdef SK_ENABLE_CLIP_QUICKREJECT
1253 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001254 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001255 return false;
1256 }
1257
reed@google.com3b3e8952012-08-16 20:53:31 +00001258 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001259 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001260 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001261
1262 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001263 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001264 }
1265 }
1266#endif
1267
reed@google.com5c3d1472011-02-22 19:12:23 +00001268 AutoValidateClip avc(this);
1269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001271 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001272 if (!fAllowSoftClip) {
1273 edgeStyle = kHard_ClipEdgeStyle;
1274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275
reed1f836ee2014-07-07 07:49:34 -07001276 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001277 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001278 // the matrix. This means we don't have to a) make a path, and b) tell
1279 // the region code to scan-convert the path, only to discover that it
1280 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282
reed1f836ee2014-07-07 07:49:34 -07001283 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001284 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed1f836ee2014-07-07 07:49:34 -07001285 fMCRec->fRasterClip.op(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001286 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001287 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001288 // and clip against that, since it can handle any matrix. However, to
1289 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1290 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 SkPath path;
1292
1293 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001294 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 }
1296}
1297
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001298static void clip_path_helper(const SkCanvas* canvas, SkRasterClip* currClip,
1299 const SkPath& devPath, SkRegion::Op op, bool doAA) {
reed@google.com759876a2011-03-03 14:32:51 +00001300 // base is used to limit the size (and therefore memory allocation) of the
1301 // region that results from scan converting devPath.
1302 SkRegion base;
1303
reed@google.com819c9212011-02-23 18:56:55 +00001304 if (SkRegion::kIntersect_Op == op) {
reed@google.com759876a2011-03-03 14:32:51 +00001305 // since we are intersect, we can do better (tighter) with currRgn's
1306 // bounds, than just using the device. However, if currRgn is complex,
1307 // our region blitter may hork, so we do that case in two steps.
reed@google.com00177082011-10-12 14:34:30 +00001308 if (currClip->isRect()) {
commit-bot@chromium.orgb446fc72013-07-10 20:55:39 +00001309 // FIXME: we should also be able to do this when currClip->isBW(),
1310 // but relaxing the test above triggers GM asserts in
1311 // SkRgnBuilder::blitH(). We need to investigate what's going on.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001312 currClip->setPath(devPath, currClip->bwRgn(), doAA);
reed@google.com759876a2011-03-03 14:32:51 +00001313 } else {
reed@google.com00177082011-10-12 14:34:30 +00001314 base.setRect(currClip->getBounds());
1315 SkRasterClip clip;
1316 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001317 currClip->op(clip, op);
reed@google.com759876a2011-03-03 14:32:51 +00001318 }
reed@google.com819c9212011-02-23 18:56:55 +00001319 } else {
reed52d9ac62014-06-30 09:05:34 -07001320 const SkISize size = canvas->getBaseLayerSize();
1321 base.setRect(0, 0, size.width(), size.height());
reed@google.com819c9212011-02-23 18:56:55 +00001322
1323 if (SkRegion::kReplace_Op == op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001324 currClip->setPath(devPath, base, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001325 } else {
reed@google.com00177082011-10-12 14:34:30 +00001326 SkRasterClip clip;
1327 clip.setPath(devPath, base, doAA);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001328 currClip->op(clip, op);
reed@google.com819c9212011-02-23 18:56:55 +00001329 }
1330 }
1331}
1332
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001333void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001334 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001335 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001336 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1337 } else {
1338 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001339 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001340}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001341
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001342void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001343 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001344 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001345 AutoValidateClip avc(this);
1346
1347 fDeviceCMDirty = true;
1348 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001349 if (!fAllowSoftClip) {
1350 edgeStyle = kHard_ClipEdgeStyle;
1351 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001352
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001353 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001354
1355 SkPath devPath;
1356 devPath.addRRect(transformedRRect);
1357
reed1f836ee2014-07-07 07:49:34 -07001358 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001359 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001360 }
1361
1362 SkPath path;
1363 path.addRRect(rrect);
1364 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001365 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001366}
1367
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001368void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001369 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1370 SkRect r;
1371 if (!path.isInverseFillType() && path.isRect(&r)) {
1372 this->onClipRect(r, op, edgeStyle);
1373 } else {
1374 this->onClipPath(path, op, edgeStyle);
1375 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001376}
1377
1378void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001379#ifdef SK_ENABLE_CLIP_QUICKREJECT
1380 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001381 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001382 return false;
1383 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001384
reed@google.com3b3e8952012-08-16 20:53:31 +00001385 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001386 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001387 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001388
reed@google.comda17f752012-08-16 18:27:05 +00001389 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001390 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001391 }
1392 }
1393#endif
1394
reed@google.com5c3d1472011-02-22 19:12:23 +00001395 AutoValidateClip avc(this);
1396
reed@android.com8a1c16f2008-12-17 15:59:43 +00001397 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001398 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001399 if (!fAllowSoftClip) {
1400 edgeStyle = kHard_ClipEdgeStyle;
1401 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402
1403 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001404 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405
reed@google.comfe701122011-11-08 19:41:23 +00001406 // Check if the transfomation, or the original path itself
1407 // made us empty. Note this can also happen if we contained NaN
1408 // values. computing the bounds detects this, and will set our
1409 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1410 if (devPath.getBounds().isEmpty()) {
1411 // resetting the path will remove any NaN or other wanky values
1412 // that might upset our scan converter.
1413 devPath.reset();
1414 }
1415
reed@google.com5c3d1472011-02-22 19:12:23 +00001416 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001417 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001418
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001419 if (fAllowSimplifyClip) {
1420 devPath.reset();
1421 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1422 const SkClipStack* clipStack = getClipStack();
1423 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1424 const SkClipStack::Element* element;
1425 while ((element = iter.next())) {
1426 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001427 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001428 if (type != SkClipStack::Element::kEmpty_Type) {
1429 element->asPath(&operand);
1430 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001431 SkRegion::Op elementOp = element->getOp();
1432 if (elementOp == SkRegion::kReplace_Op) {
1433 devPath = operand;
1434 } else {
1435 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1436 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001437 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1438 // perhaps we need an API change to avoid this sort of mixed-signals about
1439 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001440 if (element->isAA()) {
1441 edgeStyle = kSoft_ClipEdgeStyle;
1442 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001443 }
1444 op = SkRegion::kReplace_Op;
1445 }
1446
reed1f836ee2014-07-07 07:49:34 -07001447 clip_path_helper(this, &fMCRec->fRasterClip, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448}
1449
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001450void SkCanvas::updateClipConservativelyUsingBounds(const SkRect& bounds, SkRegion::Op op,
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001451 bool inverseFilled) {
1452 // This is for updating the clip conservatively using only bounds
1453 // information.
1454 // Contract:
1455 // The current clip must contain the true clip. The true
1456 // clip is the clip that would have normally been computed
1457 // by calls to clipPath and clipRRect
1458 // Objective:
1459 // Keep the current clip as small as possible without
1460 // breaking the contract, using only clip bounding rectangles
1461 // (for performance).
1462
1463 // N.B.: This *never* calls back through a virtual on canvas, so subclasses
1464 // don't have to worry about getting caught in a loop. Thus anywhere
1465 // we call a virtual method, we explicitly prefix it with
1466 // SkCanvas:: to be sure to call the base-class.
skia.committer@gmail.coma5d3e772013-05-30 07:01:29 +00001467
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001468 if (inverseFilled) {
1469 switch (op) {
1470 case SkRegion::kIntersect_Op:
1471 case SkRegion::kDifference_Op:
1472 // These ops can only shrink the current clip. So leaving
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001473 // the clip unchanged conservatively respects the contract.
1474 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001475 case SkRegion::kUnion_Op:
1476 case SkRegion::kReplace_Op:
1477 case SkRegion::kReverseDifference_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001478 case SkRegion::kXOR_Op: {
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001479 // These ops can grow the current clip up to the extents of
1480 // the input clip, which is inverse filled, so we just set
1481 // the current clip to the device bounds.
1482 SkRect deviceBounds;
1483 SkIRect deviceIBounds;
1484 this->getDevice()->getGlobalBounds(&deviceIBounds);
reed@google.com44699382013-10-31 17:28:30 +00001485 deviceBounds = SkRect::Make(deviceIBounds);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001486
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001487 // set the clip in device space
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001488 SkMatrix savedMatrix = this->getTotalMatrix();
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001489 this->SkCanvas::setMatrix(SkMatrix::I());
skia.committer@gmail.com370a8992014-03-01 03:02:09 +00001490 this->SkCanvas::onClipRect(deviceBounds, SkRegion::kReplace_Op,
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001491 kHard_ClipEdgeStyle);
commit-bot@chromium.org091a5942014-04-18 14:19:31 +00001492 this->setMatrix(savedMatrix);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001493 break;
1494 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001495 default:
1496 SkASSERT(0); // unhandled op?
1497 }
1498 } else {
1499 // Not inverse filled
1500 switch (op) {
1501 case SkRegion::kIntersect_Op:
1502 case SkRegion::kUnion_Op:
1503 case SkRegion::kReplace_Op:
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001504 this->SkCanvas::onClipRect(bounds, op, kHard_ClipEdgeStyle);
1505 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001506 case SkRegion::kDifference_Op:
1507 // Difference can only shrink the current clip.
1508 // Leaving clip unchanged conservatively fullfills the contract.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001509 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001510 case SkRegion::kReverseDifference_Op:
1511 // To reverse, we swap in the bounds with a replace op.
1512 // As with difference, leave it unchanged.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001513 this->SkCanvas::onClipRect(bounds, SkRegion::kReplace_Op, kHard_ClipEdgeStyle);
1514 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001515 case SkRegion::kXOR_Op:
1516 // Be conservative, based on (A XOR B) always included in (A union B),
1517 // which is always included in (bounds(A) union bounds(B))
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001518 this->SkCanvas::onClipRect(bounds, SkRegion::kUnion_Op, kHard_ClipEdgeStyle);
1519 break;
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001520 default:
1521 SkASSERT(0); // unhandled op?
1522 }
1523 }
junov@chromium.orged8d6bb2013-05-29 19:09:48 +00001524}
1525
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001526void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001527 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528}
1529
1530void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001531 AutoValidateClip avc(this);
1532
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001534 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001535
reed@google.com5c3d1472011-02-22 19:12:23 +00001536 // todo: signal fClipStack that we have a region, and therefore (I guess)
1537 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001538 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001539
reed1f836ee2014-07-07 07:49:34 -07001540 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001541}
1542
reed@google.com819c9212011-02-23 18:56:55 +00001543#ifdef SK_DEBUG
1544void SkCanvas::validateClip() const {
1545 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001546 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001547 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001548 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001549 return;
1550 }
1551
reed@google.com819c9212011-02-23 18:56:55 +00001552 SkIRect ir;
1553 ir.set(0, 0, device->width(), device->height());
reed@google.com00177082011-10-12 14:34:30 +00001554 SkRasterClip tmpClip(ir);
reed@google.com819c9212011-02-23 18:56:55 +00001555
robertphillips@google.com80214e22012-07-20 15:33:18 +00001556 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001557 const SkClipStack::Element* element;
1558 while ((element = iter.next()) != NULL) {
1559 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001560 case SkClipStack::Element::kRect_Type:
1561 element->getRect().round(&ir);
1562 tmpClip.op(ir, element->getOp());
1563 break;
1564 case SkClipStack::Element::kEmpty_Type:
1565 tmpClip.setEmpty();
1566 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001567 default: {
1568 SkPath path;
1569 element->asPath(&path);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001570 clip_path_helper(this, &tmpClip, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001571 break;
1572 }
reed@google.com819c9212011-02-23 18:56:55 +00001573 }
1574 }
reed@google.com819c9212011-02-23 18:56:55 +00001575}
1576#endif
1577
reed@google.com90c07ea2012-04-13 13:50:27 +00001578void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001579 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001580 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001581
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001582 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001583 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001584 }
1585}
1586
reed@google.com5c3d1472011-02-22 19:12:23 +00001587///////////////////////////////////////////////////////////////////////////////
1588
reed@google.com754de5f2014-02-24 19:38:20 +00001589bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001590 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001591}
1592
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001593bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001594 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001595}
1596
reed@google.com3b3e8952012-08-16 20:53:31 +00001597bool SkCanvas::quickReject(const SkRect& rect) const {
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001598
reed@google.com16078632011-12-06 18:56:37 +00001599 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001600 return true;
1601
reed1f836ee2014-07-07 07:49:34 -07001602 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 return true;
1604 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605
reed1f836ee2014-07-07 07:49:34 -07001606 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001607 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001608 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001609 SkIRect idst;
1610 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001611 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001612 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001613 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001614
reed@android.coma380ae42009-07-21 01:17:02 +00001615 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001616 // TODO: should we use | instead, or compare all 4 at once?
1617 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001618 return true;
1619 }
reed@google.comc0784db2013-12-13 21:16:12 +00001620 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001621 return true;
1622 }
1623 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625}
1626
reed@google.com3b3e8952012-08-16 20:53:31 +00001627bool SkCanvas::quickReject(const SkPath& path) const {
1628 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001629}
1630
reed@google.com3b3e8952012-08-16 20:53:31 +00001631bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001632 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001633 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634 return false;
1635 }
1636
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001637 SkMatrix inverse;
1638 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001639 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001640 if (bounds) {
1641 bounds->setEmpty();
1642 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001643 return false;
1644 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001645
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001646 if (NULL != bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001647 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001648 // adjust it outwards in case we are antialiasing
1649 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001650
reed@google.com8f4d2302013-12-17 16:44:46 +00001651 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1652 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653 inverse.mapRect(bounds, r);
1654 }
1655 return true;
1656}
1657
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001658bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001659 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001660 if (clip.isEmpty()) {
1661 if (bounds) {
1662 bounds->setEmpty();
1663 }
1664 return false;
1665 }
1666
1667 if (NULL != bounds) {
1668 *bounds = clip.getBounds();
1669 }
1670 return true;
1671}
1672
reed@android.com8a1c16f2008-12-17 15:59:43 +00001673const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001674 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001675}
1676
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001677#ifdef SK_SUPPORT_LEGACY_GETCLIPTYPE
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001678SkCanvas::ClipType SkCanvas::getClipType() const {
reed1f836ee2014-07-07 07:49:34 -07001679 if (fMCRec->fRasterClip.isEmpty()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001680 return kEmpty_ClipType;
1681 }
reed1f836ee2014-07-07 07:49:34 -07001682 if (fMCRec->fRasterClip.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001683 return kRect_ClipType;
1684 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001685 return kComplex_ClipType;
1686}
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001687#endif
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001688
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001689const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001690 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001691}
1692
1693void SkCanvas::internal_private_getTotalClipAsPath(SkPath* path) const {
1694 path->reset();
1695
reed1f836ee2014-07-07 07:49:34 -07001696 const SkRegion& rgn = fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001697 if (rgn.isEmpty()) {
1698 return;
1699 }
1700 (void)rgn.getBoundaryPath(path);
1701}
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702
reed@google.com9c135db2014-03-12 18:28:35 +00001703GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1704 SkBaseDevice* dev = this->getTopDevice();
1705 return dev ? dev->accessRenderTarget() : NULL;
1706}
1707
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001708SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001709 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001710 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001711}
1712
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001713GrContext* SkCanvas::getGrContext() {
1714#if SK_SUPPORT_GPU
1715 SkBaseDevice* device = this->getTopDevice();
1716 if (NULL != device) {
1717 GrRenderTarget* renderTarget = device->accessRenderTarget();
1718 if (NULL != renderTarget) {
1719 return renderTarget->getContext();
1720 }
1721 }
1722#endif
1723
1724 return NULL;
1725
1726}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001727
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001728void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1729 const SkPaint& paint) {
1730 if (outer.isEmpty()) {
1731 return;
1732 }
1733 if (inner.isEmpty()) {
1734 this->drawRRect(outer, paint);
1735 return;
1736 }
1737
1738 // We don't have this method (yet), but technically this is what we should
1739 // be able to assert...
1740 // SkASSERT(outer.contains(inner));
1741 //
1742 // For now at least check for containment of bounds
1743 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1744
1745 this->onDrawDRRect(outer, inner, paint);
1746}
1747
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748//////////////////////////////////////////////////////////////////////////////
1749// These are the virtual drawing methods
1750//////////////////////////////////////////////////////////////////////////////
1751
reed@google.com2a981812011-04-14 18:59:28 +00001752void SkCanvas::clear(SkColor color) {
1753 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001754 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001755 while (iter.next()) {
1756 iter.fDevice->clear(color);
1757 }
1758}
1759
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001760void SkCanvas::onDiscard() {
1761 if (NULL != fSurfaceBase) {
1762 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1763 }
1764}
1765
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001767 this->internalDrawPaint(paint);
1768}
1769
1770void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001771 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772
1773 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001774 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 }
1776
reed@google.com4e2b3d32011-04-07 14:18:59 +00001777 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001778}
1779
1780void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1781 const SkPaint& paint) {
1782 if ((long)count <= 0) {
1783 return;
1784 }
1785
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001786 SkRect r, storage;
1787 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001788 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001789 // special-case 2 points (common for drawing a single line)
1790 if (2 == count) {
1791 r.set(pts[0], pts[1]);
1792 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001793 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001794 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001795 bounds = &paint.computeFastStrokeBounds(r, &storage);
1796 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001797 return;
1798 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001799 }
reed@google.coma584aed2012-05-16 14:06:02 +00001800
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801 SkASSERT(pts != NULL);
1802
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001803 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001804
reed@android.com8a1c16f2008-12-17 15:59:43 +00001805 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001806 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001807 }
reed@google.com4b226022011-01-11 18:32:13 +00001808
reed@google.com4e2b3d32011-04-07 14:18:59 +00001809 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810}
1811
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001812void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001813 SkRect storage;
1814 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001816 bounds = &paint.computeFastBounds(r, &storage);
1817 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818 return;
1819 }
1820 }
reed@google.com4b226022011-01-11 18:32:13 +00001821
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001822 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001823
1824 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001825 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001826 }
1827
reed@google.com4e2b3d32011-04-07 14:18:59 +00001828 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001829}
1830
reed@google.com4ed0fb72012-12-12 20:48:18 +00001831void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001832 SkRect storage;
1833 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001834 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001835 bounds = &paint.computeFastBounds(oval, &storage);
1836 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001837 return;
1838 }
1839 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001840
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001841 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001842
1843 while (iter.next()) {
1844 iter.fDevice->drawOval(iter, oval, looper.paint());
1845 }
1846
1847 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001848}
1849
1850void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001851 SkRect storage;
1852 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001853 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001854 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1855 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001856 return;
1857 }
1858 }
1859
1860 if (rrect.isRect()) {
1861 // call the non-virtual version
1862 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001863 return;
1864 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001865 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001866 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1867 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001868 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001869
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001870 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001871
1872 while (iter.next()) {
1873 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1874 }
1875
1876 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001877}
1878
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001879void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1880 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001881 SkRect storage;
1882 const SkRect* bounds = NULL;
1883 if (paint.canComputeFastBounds()) {
1884 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1885 if (this->quickReject(*bounds)) {
1886 return;
1887 }
1888 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001889
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001890 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001891
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001892 while (iter.next()) {
1893 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1894 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001895
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001896 LOOPER_END
1897}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001898
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001899void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001900 if (!path.isFinite()) {
1901 return;
1902 }
1903
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001904 SkRect storage;
1905 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001906 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001907 const SkRect& pathBounds = path.getBounds();
1908 bounds = &paint.computeFastBounds(pathBounds, &storage);
1909 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001910 return;
1911 }
1912 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001913
1914 const SkRect& r = path.getBounds();
1915 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001916 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001917 this->internalDrawPaint(paint);
1918 }
1919 return;
1920 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001922 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001923
1924 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001925 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926 }
1927
reed@google.com4e2b3d32011-04-07 14:18:59 +00001928 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001929}
1930
1931void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1932 const SkPaint* paint) {
1933 SkDEBUGCODE(bitmap.validate();)
1934
reed@google.com3d608122011-11-21 15:16:16 +00001935 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001936 SkRect bounds = {
1937 x, y,
1938 x + SkIntToScalar(bitmap.width()),
1939 y + SkIntToScalar(bitmap.height())
1940 };
1941 if (paint) {
1942 (void)paint->computeFastBounds(bounds, &bounds);
1943 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001944 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001945 return;
1946 }
1947 }
reed@google.com4b226022011-01-11 18:32:13 +00001948
reed@android.com8a1c16f2008-12-17 15:59:43 +00001949 SkMatrix matrix;
1950 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001951 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001952}
1953
reed@google.com9987ec32011-09-07 11:57:52 +00001954// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001955void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001956 const SkRect& dst, const SkPaint* paint,
1957 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001958 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959 return;
1960 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001961
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001962 SkRect storage;
1963 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001964 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001965 if (paint) {
1966 bounds = &paint->computeFastBounds(dst, &storage);
1967 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001968 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001969 return;
1970 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001971 }
reed@google.com3d608122011-11-21 15:16:16 +00001972
reed@google.com33535f32012-09-25 15:37:50 +00001973 SkLazyPaint lazy;
1974 if (NULL == paint) {
1975 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001976 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001977
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001978 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001979
reed@google.com33535f32012-09-25 15:37:50 +00001980 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001981 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001982 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001983
reed@google.com33535f32012-09-25 15:37:50 +00001984 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001985}
1986
reed@google.com71121732012-09-18 15:14:33 +00001987void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001988 const SkRect& dst, const SkPaint* paint,
1989 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001990 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001991 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001992}
1993
reed@android.com8a1c16f2008-12-17 15:59:43 +00001994void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1995 const SkPaint* paint) {
1996 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001997 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001998}
1999
reed@google.com9987ec32011-09-07 11:57:52 +00002000void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
2001 const SkIRect& center, const SkRect& dst,
2002 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002003 if (bitmap.drawsNothing()) {
2004 return;
2005 }
reed@google.com3d608122011-11-21 15:16:16 +00002006 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002007 SkRect storage;
2008 const SkRect* bounds = &dst;
2009 if (paint) {
2010 bounds = &paint->computeFastBounds(dst, &storage);
2011 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002012 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002013 return;
2014 }
2015 }
2016
reed@google.com9987ec32011-09-07 11:57:52 +00002017 const int32_t w = bitmap.width();
2018 const int32_t h = bitmap.height();
2019
2020 SkIRect c = center;
2021 // pin center to the bounds of the bitmap
2022 c.fLeft = SkMax32(0, center.fLeft);
2023 c.fTop = SkMax32(0, center.fTop);
2024 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2025 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2026
reed@google.com71121732012-09-18 15:14:33 +00002027 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002028 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002029 };
2030 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002031 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002032 };
reed@google.com9987ec32011-09-07 11:57:52 +00002033 SkScalar dstX[4] = {
2034 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2035 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2036 };
2037 SkScalar dstY[4] = {
2038 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2039 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2040 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002041
reed@google.com9987ec32011-09-07 11:57:52 +00002042 if (dstX[1] > dstX[2]) {
2043 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2044 dstX[2] = dstX[1];
2045 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002046
reed@google.com9987ec32011-09-07 11:57:52 +00002047 if (dstY[1] > dstY[2]) {
2048 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2049 dstY[2] = dstY[1];
2050 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002051
reed@google.com9987ec32011-09-07 11:57:52 +00002052 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002053 SkRect s, d;
2054
reed@google.com9987ec32011-09-07 11:57:52 +00002055 s.fTop = srcY[y];
2056 s.fBottom = srcY[y+1];
2057 d.fTop = dstY[y];
2058 d.fBottom = dstY[y+1];
2059 for (int x = 0; x < 3; x++) {
2060 s.fLeft = srcX[x];
2061 s.fRight = srcX[x+1];
2062 d.fLeft = dstX[x];
2063 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002064 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002065 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002066 }
2067 }
2068}
2069
2070void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2071 const SkRect& dst, const SkPaint* paint) {
2072 SkDEBUGCODE(bitmap.validate();)
2073
2074 // Need a device entry-point, so gpu can use a mesh
2075 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2076}
2077
reed@google.comf67e4cf2011-03-15 20:56:58 +00002078class SkDeviceFilteredPaint {
2079public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002080 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2081 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002082 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002083 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002084 newPaint->setFlags(flags.fFlags);
2085 newPaint->setHinting(flags.fHinting);
2086 fPaint = newPaint;
2087 } else {
2088 fPaint = &paint;
2089 }
2090 }
2091
reed@google.comf67e4cf2011-03-15 20:56:58 +00002092 const SkPaint& paint() const { return *fPaint; }
2093
2094private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002095 const SkPaint* fPaint;
2096 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002097};
2098
bungeman@google.com52c748b2011-08-22 21:30:43 +00002099void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2100 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002101 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002102 draw.fDevice->drawRect(draw, r, paint);
2103 } else {
2104 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002105 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002106 draw.fDevice->drawRect(draw, r, p);
2107 }
2108}
2109
2110void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2111 const char text[], size_t byteLength,
2112 SkScalar x, SkScalar y) {
2113 SkASSERT(byteLength == 0 || text != NULL);
2114
2115 // nothing to draw
2116 if (text == NULL || byteLength == 0 ||
2117 draw.fClip->isEmpty() ||
2118 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2119 return;
2120 }
2121
2122 SkScalar width = 0;
2123 SkPoint start;
2124
2125 start.set(0, 0); // to avoid warning
2126 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2127 SkPaint::kStrikeThruText_Flag)) {
2128 width = paint.measureText(text, byteLength);
2129
2130 SkScalar offsetX = 0;
2131 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2132 offsetX = SkScalarHalf(width);
2133 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2134 offsetX = width;
2135 }
2136 start.set(x - offsetX, y);
2137 }
2138
2139 if (0 == width) {
2140 return;
2141 }
2142
2143 uint32_t flags = paint.getFlags();
2144
2145 if (flags & (SkPaint::kUnderlineText_Flag |
2146 SkPaint::kStrikeThruText_Flag)) {
2147 SkScalar textSize = paint.getTextSize();
2148 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2149 SkRect r;
2150
2151 r.fLeft = start.fX;
2152 r.fRight = start.fX + width;
2153
2154 if (flags & SkPaint::kUnderlineText_Flag) {
2155 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2156 start.fY);
2157 r.fTop = offset;
2158 r.fBottom = offset + height;
2159 DrawRect(draw, paint, r, textSize);
2160 }
2161 if (flags & SkPaint::kStrikeThruText_Flag) {
2162 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2163 start.fY);
2164 r.fTop = offset;
2165 r.fBottom = offset + height;
2166 DrawRect(draw, paint, r, textSize);
2167 }
2168 }
2169}
2170
reed@google.come0d9ce82014-04-23 04:00:17 +00002171void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2172 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002173 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174
2175 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002176 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002177 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002178 DrawTextDecorations(iter, dfp.paint(),
2179 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002180 }
2181
reed@google.com4e2b3d32011-04-07 14:18:59 +00002182 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183}
2184
reed@google.come0d9ce82014-04-23 04:00:17 +00002185void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2186 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002187 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002188
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002190 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002192 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002194
reed@google.com4e2b3d32011-04-07 14:18:59 +00002195 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196}
2197
reed@google.come0d9ce82014-04-23 04:00:17 +00002198void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2199 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002200 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002201
reed@android.com8a1c16f2008-12-17 15:59:43 +00002202 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002203 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002204 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002205 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002206 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002207
reed@google.com4e2b3d32011-04-07 14:18:59 +00002208 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002209}
2210
reed@google.come0d9ce82014-04-23 04:00:17 +00002211void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2212 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002213 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002214
reed@android.com8a1c16f2008-12-17 15:59:43 +00002215 while (iter.next()) {
2216 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002217 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002218 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002219
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002220 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002221}
2222
reed@google.come0d9ce82014-04-23 04:00:17 +00002223// These will become non-virtual, so they always call the (virtual) onDraw... method
2224void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2225 const SkPaint& paint) {
2226 this->onDrawText(text, byteLength, x, y, paint);
2227}
2228void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2229 const SkPaint& paint) {
2230 this->onDrawPosText(text, byteLength, pos, paint);
2231}
2232void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2233 SkScalar constY, const SkPaint& paint) {
2234 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2235}
2236void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2237 const SkMatrix* matrix, const SkPaint& paint) {
2238 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2239}
2240
reed@android.com8a1c16f2008-12-17 15:59:43 +00002241void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2242 const SkPoint verts[], const SkPoint texs[],
2243 const SkColor colors[], SkXfermode* xmode,
2244 const uint16_t indices[], int indexCount,
2245 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002246 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002247
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248 while (iter.next()) {
2249 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002250 colors, xmode, indices, indexCount,
2251 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252 }
reed@google.com4b226022011-01-11 18:32:13 +00002253
reed@google.com4e2b3d32011-04-07 14:18:59 +00002254 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002255}
2256
dandovecfff212014-08-04 10:02:00 -07002257void SkCanvas::drawPatch(const SkPatch& patch, const SkPaint& paint) {
2258
2259 // Since a patch is always within the convex hull of the control points, we discard it when its
2260 // bounding rectangle is completely outside the current clip.
2261 SkRect bounds;
dandov963137b2014-08-07 07:49:53 -07002262 bounds.set(patch.getControlPoints(), SkPatch::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002263 if (this->quickReject(bounds)) {
2264 return;
2265 }
2266
2267 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
2268
2269 while (iter.next()) {
2270 iter.fDevice->drawPatch(iter, patch, paint);
2271 }
2272
2273 LOOPER_END
2274}
2275
reed@android.com8a1c16f2008-12-17 15:59:43 +00002276//////////////////////////////////////////////////////////////////////////////
2277// These methods are NOT virtual, and therefore must call back into virtual
2278// methods, rather than actually drawing themselves.
2279//////////////////////////////////////////////////////////////////////////////
2280
2281void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002282 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283 SkPaint paint;
2284
2285 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002286 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002287 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288 }
2289 this->drawPaint(paint);
2290}
2291
reed@android.com845fdac2009-06-23 03:01:32 +00002292void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002293 SkPaint paint;
2294
2295 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002296 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002297 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002298 }
2299 this->drawPaint(paint);
2300}
2301
2302void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2303 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002304
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305 pt.set(x, y);
2306 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2307}
2308
2309void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2310 SkPoint pt;
2311 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002312
reed@android.com8a1c16f2008-12-17 15:59:43 +00002313 pt.set(x, y);
2314 paint.setColor(color);
2315 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2316}
2317
2318void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2319 const SkPaint& paint) {
2320 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002321
reed@android.com8a1c16f2008-12-17 15:59:43 +00002322 pts[0].set(x0, y0);
2323 pts[1].set(x1, y1);
2324 this->drawPoints(kLines_PointMode, 2, pts, paint);
2325}
2326
2327void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2328 SkScalar right, SkScalar bottom,
2329 const SkPaint& paint) {
2330 SkRect r;
2331
2332 r.set(left, top, right, bottom);
2333 this->drawRect(r, paint);
2334}
2335
2336void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2337 const SkPaint& paint) {
2338 if (radius < 0) {
2339 radius = 0;
2340 }
2341
2342 SkRect r;
2343 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002344 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002345}
2346
2347void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2348 const SkPaint& paint) {
2349 if (rx > 0 && ry > 0) {
2350 if (paint.canComputeFastBounds()) {
2351 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002352 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002353 return;
2354 }
2355 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002356 SkRRect rrect;
2357 rrect.setRectXY(r, rx, ry);
2358 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359 } else {
2360 this->drawRect(r, paint);
2361 }
2362}
2363
reed@android.com8a1c16f2008-12-17 15:59:43 +00002364void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2365 SkScalar sweepAngle, bool useCenter,
2366 const SkPaint& paint) {
2367 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2368 this->drawOval(oval, paint);
2369 } else {
2370 SkPath path;
2371 if (useCenter) {
2372 path.moveTo(oval.centerX(), oval.centerY());
2373 }
2374 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2375 if (useCenter) {
2376 path.close();
2377 }
2378 this->drawPath(path, paint);
2379 }
2380}
2381
2382void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2383 const SkPath& path, SkScalar hOffset,
2384 SkScalar vOffset, const SkPaint& paint) {
2385 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002386
reed@android.com8a1c16f2008-12-17 15:59:43 +00002387 matrix.setTranslate(hOffset, vOffset);
2388 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2389}
2390
reed@android.comf76bacf2009-05-13 14:00:33 +00002391///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002392void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002393 SkBaseDevice* device = this->getDevice();
2394 if (NULL != device) {
2395 device->EXPERIMENTAL_optimize(picture);
2396 }
2397}
reed@android.comf76bacf2009-05-13 14:00:33 +00002398
robertphillips9b14f262014-06-04 05:40:44 -07002399void SkCanvas::drawPicture(const SkPicture* picture) {
2400 if (NULL != picture) {
reedd5fa1a42014-08-09 11:08:05 -07002401 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002402 }
2403}
2404
reedd5fa1a42014-08-09 11:08:05 -07002405void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
2406 if (NULL != picture) {
2407 if (matrix && matrix->isIdentity()) {
2408 matrix = NULL;
2409 }
2410 this->onDrawPicture(picture, matrix, paint);
2411 }
2412}
robertphillips9b14f262014-06-04 05:40:44 -07002413
reedd5fa1a42014-08-09 11:08:05 -07002414void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2415 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002416 SkBaseDevice* device = this->getTopDevice();
2417 if (NULL != device) {
2418 // Canvas has to first give the device the opportunity to render
2419 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002420 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002421 return; // the device has rendered the entire picture
2422 }
2423 }
2424
reedd5fa1a42014-08-09 11:08:05 -07002425 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->width(), picture->height());
2426
robertphillips9b14f262014-06-04 05:40:44 -07002427 picture->draw(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002428}
2429
reed@android.com8a1c16f2008-12-17 15:59:43 +00002430///////////////////////////////////////////////////////////////////////////////
2431///////////////////////////////////////////////////////////////////////////////
2432
2433SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002434 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435
2436 SkASSERT(canvas);
2437
2438 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2439 fDone = !fImpl->next();
2440}
2441
2442SkCanvas::LayerIter::~LayerIter() {
2443 fImpl->~SkDrawIter();
2444}
2445
2446void SkCanvas::LayerIter::next() {
2447 fDone = !fImpl->next();
2448}
2449
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002450SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451 return fImpl->getDevice();
2452}
2453
2454const SkMatrix& SkCanvas::LayerIter::matrix() const {
2455 return fImpl->getMatrix();
2456}
2457
2458const SkPaint& SkCanvas::LayerIter::paint() const {
2459 const SkPaint* paint = fImpl->getPaint();
2460 if (NULL == paint) {
2461 paint = &fDefaultPaint;
2462 }
2463 return *paint;
2464}
2465
2466const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2467int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2468int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002469
2470///////////////////////////////////////////////////////////////////////////////
2471
fmalitac3b589a2014-06-05 12:40:07 -07002472SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002473
2474///////////////////////////////////////////////////////////////////////////////
2475
2476static bool supported_for_raster_canvas(const SkImageInfo& info) {
2477 switch (info.alphaType()) {
2478 case kPremul_SkAlphaType:
2479 case kOpaque_SkAlphaType:
2480 break;
2481 default:
2482 return false;
2483 }
2484
2485 switch (info.colorType()) {
2486 case kAlpha_8_SkColorType:
2487 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002488 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002489 break;
2490 default:
2491 return false;
2492 }
2493
2494 return true;
2495}
2496
2497SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2498 if (!supported_for_raster_canvas(info)) {
2499 return NULL;
2500 }
2501
2502 SkBitmap bitmap;
2503 if (!bitmap.allocPixels(info)) {
2504 return NULL;
2505 }
2506
2507 // should this functionality be moved into allocPixels()?
2508 if (!bitmap.info().isOpaque()) {
2509 bitmap.eraseColor(0);
2510 }
2511 return SkNEW_ARGS(SkCanvas, (bitmap));
2512}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002513
2514SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2515 if (!supported_for_raster_canvas(info)) {
2516 return NULL;
2517 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002518
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002519 SkBitmap bitmap;
2520 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2521 return NULL;
2522 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002523 return SkNEW_ARGS(SkCanvas, (bitmap));
2524}
reedd5fa1a42014-08-09 11:08:05 -07002525
2526///////////////////////////////////////////////////////////////////////////////
2527
2528SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
2529 const SkPaint* paint, int width, int height)
2530 : fCanvas(canvas)
2531 , fSaveCount(canvas->getSaveCount())
2532{
2533 if (NULL != paint) {
2534 SkRect bounds = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
2535 if (matrix) {
2536 matrix->mapRect(&bounds);
2537 }
2538 canvas->saveLayer(&bounds, paint);
2539 } else if (NULL != matrix) {
2540 canvas->save();
2541 }
2542
2543 if (NULL != matrix) {
2544 canvas->concat(*matrix);
2545 }
2546}
2547
2548SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2549 fCanvas->restoreToCount(fSaveCount);
2550}