blob: 6beb26e4cad9c42d70b99e44f7d62f867a15e222 [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
reed29c857d2014-09-21 10:25:07 -07008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000011#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000012#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
14#include "SkDrawFilter.h"
15#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000016#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000017#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070018#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000022#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070025#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000026#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000027#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000030#if SK_SUPPORT_GPU
31#include "GrRenderTarget.h"
32#endif
33
reed@google.comda17f752012-08-16 18:27:05 +000034// experimental for faster tiled drawing...
35//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000036
reed@android.com8a1c16f2008-12-17 15:59:43 +000037//#define SK_TRACE_SAVERESTORE
38
39#ifdef SK_TRACE_SAVERESTORE
40 static int gLayerCounter;
41 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
42 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
43
44 static int gRecCounter;
45 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
46 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
47
48 static int gCanvasCounter;
49 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
50 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
51#else
52 #define inc_layer()
53 #define dec_layer()
54 #define inc_rec()
55 #define dec_rec()
56 #define inc_canvas()
57 #define dec_canvas()
58#endif
59
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000060typedef SkTLazy<SkPaint> SkLazyPaint;
61
reed@google.com97af1a62012-08-28 12:19:02 +000062void SkCanvas::predrawNotify() {
63 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000064 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000065 }
66}
67
reed@android.com8a1c16f2008-12-17 15:59:43 +000068///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000069
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000070/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000071 The clip/matrix/proc are fields that reflect the top of the save/restore
72 stack. Whenever the canvas changes, it marks a dirty flag, and then before
73 these are used (assuming we're not on a layer) we rebuild these cache
74 values: they reflect the top of the save stack, but translated and clipped
75 by the device's XY offset and bitmap-bounds.
76*/
77struct DeviceCM {
78 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000079 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000080 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000082 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000083
reedd9544982014-09-09 18:46:22 -070084 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
85 bool conservativeRasterClip)
86 : fNext(NULL)
87 , fClip(conservativeRasterClip)
88 {
89 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000091 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +000092 }
reed@google.com4b226022011-01-11 18:32:13 +000093 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +000095 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000096
bungeman@google.com88edf1e2011-08-08 19:41:56 +000097 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -070098 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000099 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 fDevice->unref();
101 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000102 SkDELETE(fPaint);
103 }
reed@google.com4b226022011-01-11 18:32:13 +0000104
reed@google.com045e62d2011-10-24 12:19:46 +0000105 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
106 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000107 int x = fDevice->getOrigin().x();
108 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 int width = fDevice->width();
110 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000111
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 if ((x | y) == 0) {
113 fMatrix = &totalMatrix;
114 fClip = totalClip;
115 } else {
116 fMatrixStorage = totalMatrix;
117 fMatrixStorage.postTranslate(SkIntToScalar(-x),
118 SkIntToScalar(-y));
119 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000120
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 totalClip.translate(-x, -y, &fClip);
122 }
123
reed@google.com045e62d2011-10-24 12:19:46 +0000124 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125
126 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000127
reed@android.com8a1c16f2008-12-17 15:59:43 +0000128 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000129 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130 SkRegion::kDifference_Op);
131 }
reed@google.com4b226022011-01-11 18:32:13 +0000132
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000133 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
134
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135#ifdef SK_DEBUG
136 if (!fClip.isEmpty()) {
137 SkIRect deviceR;
138 deviceR.set(0, 0, width, height);
139 SkASSERT(deviceR.contains(fClip.getBounds()));
140 }
141#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000142 }
143
reed@android.com8a1c16f2008-12-17 15:59:43 +0000144private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000145 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146};
147
148/* This is the record we keep for each save/restore level in the stack.
149 Since a level optionally copies the matrix and/or stack, we have pointers
150 for these fields. If the value is copied for this level, the copy is
151 stored in the ...Storage field, and the pointer points to that. If the
152 value is not copied for this level, we ignore ...Storage, and just point
153 at the corresponding value in the previous level in the stack.
154*/
155class SkCanvas::MCRec {
156public:
reed6f097092014-09-09 12:51:10 -0700157 SkRasterClip fRasterClip;
reedd9544982014-09-09 18:46:22 -0700158 SkMatrix fMatrix;
reed1f836ee2014-07-07 07:49:34 -0700159 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700160 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 /* If there are any layers in the stack, this points to the top-most
162 one that is at or below this level in the stack (so we know what
163 bitmap/device to draw into from this level. This value is NOT
164 reference counted, since the real owner is either our fLayer field,
165 or a previous one in a lower level.)
166 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000167 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168
reedd9544982014-09-09 18:46:22 -0700169 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
170 fMatrix.reset();
171 fFilter = NULL;
172 fLayer = NULL;
173 fTopLayer = NULL;
174
175 // don't bother initializing fNext
176 inc_rec();
177 }
178 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip) {
179 fMatrix = prev.fMatrix;
180 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700182 fTopLayer = prev.fTopLayer;
183
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 // don't bother initializing fNext
185 inc_rec();
186 }
187 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000188 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189 SkDELETE(fLayer);
190 dec_rec();
191 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192};
193
194class SkDrawIter : public SkDraw {
195public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000196 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000197 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000198 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 canvas->updateDeviceCMCache();
200
reed@google.com90c07ea2012-04-13 13:50:27 +0000201 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000203 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 }
reed@google.com4b226022011-01-11 18:32:13 +0000205
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 bool next() {
207 // skip over recs with empty clips
208 if (fSkipEmptyClips) {
209 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
210 fCurrLayer = fCurrLayer->fNext;
211 }
212 }
213
reed@google.comf68c5e22012-02-24 16:38:58 +0000214 const DeviceCM* rec = fCurrLayer;
215 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216
217 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000218 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
219 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000220 fDevice = rec->fDevice;
221 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000223 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224
225 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000226 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000227
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 return true;
229 }
230 return false;
231 }
reed@google.com4b226022011-01-11 18:32:13 +0000232
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000233 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000234 int getX() const { return fDevice->getOrigin().x(); }
235 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236 const SkMatrix& getMatrix() const { return *fMatrix; }
237 const SkRegion& getClip() const { return *fClip; }
238 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240private:
241 SkCanvas* fCanvas;
242 const DeviceCM* fCurrLayer;
243 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 SkBool8 fSkipEmptyClips;
245
246 typedef SkDraw INHERITED;
247};
248
249/////////////////////////////////////////////////////////////////////////////
250
251class AutoDrawLooper {
252public:
reed29c857d2014-09-21 10:25:07 -0700253 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000254 bool skipLayerForImageFilter = false,
255 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000256 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 fFilter = canvas->getDrawFilter();
reed29c857d2014-09-21 10:25:07 -0700258 fPaint = NULL;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000259 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000260 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000261 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262
reed@google.com8926b162012-03-23 15:36:36 +0000263 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
264 SkPaint tmp;
265 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000266 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
267 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000268 // we'll clear the imageFilter for the actual draws in next(), so
269 // it will only be applied during the restore().
270 fDoClearImageFilter = true;
271 }
272
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000273 if (SkDrawLooper* looper = paint.getLooper()) {
274 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
275 looper->contextSize());
276 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000277 fIsSimple = false;
278 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000279 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000280 // can we be marked as simple?
281 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000282 }
283 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000284
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000286 if (fDoClearImageFilter) {
287 fCanvas->internalRestore();
288 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000289 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000290 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000291
reed@google.com4e2b3d32011-04-07 14:18:59 +0000292 const SkPaint& paint() const {
293 SkASSERT(fPaint);
294 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000296
reed@google.com129ec222012-05-15 13:24:09 +0000297 bool next(SkDrawFilter::Type drawType) {
298 if (fDone) {
299 return false;
300 } else if (fIsSimple) {
301 fDone = true;
reed29c857d2014-09-21 10:25:07 -0700302 fPaint = &fOrigPaint;
reed@google.com129ec222012-05-15 13:24:09 +0000303 return !fPaint->nothingToDraw();
304 } else {
305 return this->doNext(drawType);
306 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000307 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000308
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000310 SkLazyPaint fLazyPaint;
311 SkCanvas* fCanvas;
312 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000313 SkDrawFilter* fFilter;
314 const SkPaint* fPaint;
315 int fSaveCount;
reed@google.com8926b162012-03-23 15:36:36 +0000316 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000317 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000318 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000319 SkDrawLooper::Context* fLooperContext;
320 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000321
322 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323};
324
reed@google.com129ec222012-05-15 13:24:09 +0000325bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000326 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000327 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000328 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000329
330 SkPaint* paint = fLazyPaint.set(fOrigPaint);
331
332 if (fDoClearImageFilter) {
333 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000334 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000335
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000336 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000337 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000338 return false;
339 }
340 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000341 if (!fFilter->filter(paint, drawType)) {
342 fDone = true;
343 return false;
344 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000345 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000346 // no looper means we only draw once
347 fDone = true;
348 }
349 }
350 fPaint = paint;
351
352 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000353 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000354 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000355 }
356
357 // call this after any possible paint modifiers
358 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000359 fPaint = NULL;
360 return false;
361 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000362 return true;
363}
364
reed29c857d2014-09-21 10:25:07 -0700365#include "SkColorPriv.h"
366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367////////// macros to place around the internal draw calls //////////////////
368
reed@google.com8926b162012-03-23 15:36:36 +0000369#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000370 this->predrawNotify(); \
reed29c857d2014-09-21 10:25:07 -0700371 AutoDrawLooper looper(this, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000372 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000373 SkDrawIter iter(this);
374
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000375#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000376 this->predrawNotify(); \
reed29c857d2014-09-21 10:25:07 -0700377 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000378 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000380
reed@google.com4e2b3d32011-04-07 14:18:59 +0000381#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382
383////////////////////////////////////////////////////////////////////////////
384
reedd9544982014-09-09 18:46:22 -0700385SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
386 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000387 fCachedLocalClipBounds.setEmpty();
388 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000389 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000390 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700391 fDeviceCMDirty = true;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000392 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000393 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000394 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000395
reed29c857d2014-09-21 10:25:07 -0700396 if (device && device->forceConservativeRasterClip()) {
397 fConservativeRasterClip = true;
398 }
399
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700401 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402
reedd9544982014-09-09 18:46:22 -0700403 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000404 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405
reed@google.com97af1a62012-08-28 12:19:02 +0000406 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000407
reedf92c8662014-08-18 08:02:43 -0700408 if (device) {
409 device->onAttachToCanvas(this);
410 fMCRec->fLayer->fDevice = SkRef(device);
411 fMCRec->fRasterClip.setRect(SkIRect::MakeWH(device->width(), device->height()));
412 }
413 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414}
415
reed@google.comcde92112011-07-06 20:00:52 +0000416SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000417 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
418{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000419 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000420
reedd9544982014-09-09 18:46:22 -0700421 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000422}
423
reedd9544982014-09-09 18:46:22 -0700424static SkBitmap make_nopixels(int width, int height) {
425 SkBitmap bitmap;
426 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
427 return bitmap;
428}
429
430class SkNoPixelsBitmapDevice : public SkBitmapDevice {
431public:
432 SkNoPixelsBitmapDevice(int width, int height) : INHERITED(make_nopixels(width, height)) {}
433
434private:
435
436 typedef SkBitmapDevice INHERITED;
437};
438
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000439SkCanvas::SkCanvas(int width, int height)
440 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
441{
442 inc_canvas();
reedd9544982014-09-09 18:46:22 -0700443
444 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), kDefault_InitFlags)->unref();
445}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000446
reedd9544982014-09-09 18:46:22 -0700447SkCanvas::SkCanvas(int width, int height, InitFlags flags)
448 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
449{
450 inc_canvas();
451
452 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), flags)->unref();
453}
454
reed29c857d2014-09-21 10:25:07 -0700455SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700456 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
457{
458 inc_canvas();
459
460 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000461}
462
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000463SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000464 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
465{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 inc_canvas();
reedd9544982014-09-09 18:46:22 -0700467
468 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000469}
470
reed3716fd02014-09-21 09:39:55 -0700471SkCanvas::SkCanvas(const SkBitmap& bitmap)
472 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed3716fd02014-09-21 09:39:55 -0700473{
474 inc_canvas();
reed29c857d2014-09-21 10:25:07 -0700475
476 this->init(SkNEW_ARGS(SkBitmapDevice, (bitmap)), kDefault_InitFlags)->unref();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477}
478
479SkCanvas::~SkCanvas() {
480 // free up the contents of our deque
481 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000482 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000483
reed@android.com8a1c16f2008-12-17 15:59:43 +0000484 this->internalRestore(); // restore the last, since we're going away
485
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000486 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000487
reed@android.com8a1c16f2008-12-17 15:59:43 +0000488 dec_canvas();
489}
490
reed@android.com8a1c16f2008-12-17 15:59:43 +0000491SkDrawFilter* SkCanvas::getDrawFilter() const {
492 return fMCRec->fFilter;
493}
494
495SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
496 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
497 return filter;
498}
499
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000500SkMetaData& SkCanvas::getMetaData() {
501 // metadata users are rare, so we lazily allocate it. If that changes we
502 // can decide to just make it a field in the device (rather than a ptr)
503 if (NULL == fMetaData) {
504 fMetaData = new SkMetaData;
505 }
506 return *fMetaData;
507}
508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509///////////////////////////////////////////////////////////////////////////////
510
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000511void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000512 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000513 if (device) {
514 device->flush();
515 }
516}
517
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000518SkISize SkCanvas::getTopLayerSize() const {
519 SkBaseDevice* d = this->getTopDevice();
520 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
521}
522
523SkIPoint SkCanvas::getTopLayerOrigin() const {
524 SkBaseDevice* d = this->getTopDevice();
525 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
526}
527
528SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000529 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000530 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
531}
532
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000533SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000535 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 SkASSERT(rec && rec->fLayer);
537 return rec->fLayer->fDevice;
538}
539
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000540SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000541 if (updateMatrixClip) {
542 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
543 }
reed@google.com9266fed2011-03-30 00:18:03 +0000544 return fMCRec->fTopLayer->fDevice;
545}
546
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000547SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000549 SkDeque::F2BIter iter(fMCStack);
550 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000552 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000553
554 if (rootDevice == device) {
555 return device;
556 }
reed@google.com4b226022011-01-11 18:32:13 +0000557
reed@android.com8a1c16f2008-12-17 15:59:43 +0000558 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000559 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560 }
561 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000562 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 }
564
565 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
566 rootDevice = device;
567
568 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000569
reed@android.com8a1c16f2008-12-17 15:59:43 +0000570 /* Now we update our initial region to have the bounds of the new device,
571 and then intersect all of the clips in our stack with these bounds,
572 to ensure that we can't draw outside of the device's bounds (and trash
573 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000574
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 NOTE: this is only a partial-fix, since if the new device is larger than
576 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000577 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
579 reconstruct the correct clips, so this approximation will have to do.
580 The caller really needs to restore() back to the base if they want to
581 accurately take advantage of the new device bounds.
582 */
583
reed@google.com42aea282012-03-28 16:19:15 +0000584 SkIRect bounds;
585 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000586 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000587 } else {
588 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 }
reed@google.com42aea282012-03-28 16:19:15 +0000590 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700591 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000592 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700593 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000594 }
595
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596 return device;
597}
598
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000599bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
600 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
601 return false;
602 }
603
604 bool weAllocated = false;
605 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700606 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000607 return false;
608 }
609 weAllocated = true;
610 }
611
612 SkBitmap bm(*bitmap);
613 bm.lockPixels();
614 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
615 return true;
616 }
617
618 if (weAllocated) {
619 bitmap->setPixelRef(NULL);
620 }
621 return false;
622}
reed@google.com51df9e32010-12-23 19:29:18 +0000623
bsalomon@google.comc6980972011-11-02 19:57:21 +0000624bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000625 SkIRect r = srcRect;
626 const SkISize size = this->getBaseLayerSize();
627 if (!r.intersect(0, 0, size.width(), size.height())) {
628 bitmap->reset();
629 return false;
630 }
631
reed84825042014-09-02 12:50:45 -0700632 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000633 // bitmap will already be reset.
634 return false;
635 }
636 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
637 bitmap->reset();
638 return false;
639 }
640 return true;
641}
642
643bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
644 switch (origInfo.colorType()) {
645 case kUnknown_SkColorType:
646 case kIndex_8_SkColorType:
647 return false;
648 default:
649 break;
650 }
651 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
652 return false;
653 }
654 if (0 == origInfo.width() || 0 == origInfo.height()) {
655 return false;
656 }
657
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000658 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000659 if (!device) {
660 return false;
661 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000662
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000663 const SkISize size = this->getBaseLayerSize();
664 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
665 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000666 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000667 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000668
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000669 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700670 const SkImageInfo info = origInfo.makeWH(srcR.width(), srcR.height());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000671
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000672 // if x or y are negative, then we have to adjust pixels
673 if (x > 0) {
674 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000675 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000676 if (y > 0) {
677 y = 0;
678 }
679 // here x,y are either 0 or negative
680 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000681
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000682 // The device can assert that the requested area is always contained in its bounds
683 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000684}
685
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000686bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
687 if (bitmap.getTexture()) {
688 return false;
689 }
690 SkBitmap bm(bitmap);
691 bm.lockPixels();
692 if (bm.getPixels()) {
693 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
694 }
695 return false;
696}
697
698bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
699 int x, int y) {
700 switch (origInfo.colorType()) {
701 case kUnknown_SkColorType:
702 case kIndex_8_SkColorType:
703 return false;
704 default:
705 break;
706 }
707 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
708 return false;
709 }
710
711 const SkISize size = this->getBaseLayerSize();
712 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
713 if (!target.intersect(0, 0, size.width(), size.height())) {
714 return false;
715 }
716
717 SkBaseDevice* device = this->getDevice();
718 if (!device) {
719 return false;
720 }
721
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000722 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700723 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000724
725 // if x or y are negative, then we have to adjust pixels
726 if (x > 0) {
727 x = 0;
728 }
729 if (y > 0) {
730 y = 0;
731 }
732 // here x,y are either 0 or negative
733 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
734
reed4af35f32014-06-27 17:47:49 -0700735 // Tell our owning surface to bump its generation ID
736 this->predrawNotify();
737
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000738 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000739 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000740}
reed@google.com51df9e32010-12-23 19:29:18 +0000741
junov@google.com4370aed2012-01-18 16:21:08 +0000742SkCanvas* SkCanvas::canvasForDrawIter() {
743 return this;
744}
745
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746//////////////////////////////////////////////////////////////////////////////
747
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748void SkCanvas::updateDeviceCMCache() {
749 if (fDeviceCMDirty) {
750 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700751 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000752 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000753
reed@android.com8a1c16f2008-12-17 15:59:43 +0000754 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000755 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000756 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000757 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000759 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000760 } while ((layer = layer->fNext) != NULL);
761 }
762 fDeviceCMDirty = false;
763 }
764}
765
reed@android.com8a1c16f2008-12-17 15:59:43 +0000766///////////////////////////////////////////////////////////////////////////////
767
Florin Malita5f6102d2014-06-30 10:13:28 -0400768int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000769 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000770
reed@android.com8a1c16f2008-12-17 15:59:43 +0000771 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700772 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000774
Florin Malita5f6102d2014-06-30 10:13:28 -0400775 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000776
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 return saveCount;
778}
779
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000780int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400781 this->willSave();
782 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783}
784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000786#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000788#else
789 return true;
790#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791}
792
junov@chromium.orga907ac32012-02-24 21:54:07 +0000793bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000794 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000795 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000796 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000797 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000798 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000799 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000800
801 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700802 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000803 // Filters may grow the bounds beyond the device bounds.
804 op = SkRegion::kReplace_Op;
805 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000806 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700807 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000809
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 this->getTotalMatrix().mapRect(&r, *bounds);
811 r.roundOut(&ir);
812 // early exit if the layer's bounds are clipped out
813 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000814 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700815 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000816 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000817 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 }
819 } else { // no user bounds, so just use the clip
820 ir = clipBounds;
821 }
822
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000823 if (bounds_affects_clip(flags)) {
824 fClipStack.clipDevRect(ir, op);
825 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700826 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000827 return false;
828 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000829 }
830
831 if (intersection) {
832 *intersection = ir;
833 }
834 return true;
835}
836
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000837int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
838 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
839 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
840}
841
junov@chromium.orga907ac32012-02-24 21:54:07 +0000842int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
843 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000844 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
845 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000846}
847
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000848int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
849 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000850#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000851 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000852#endif
853
junov@chromium.orga907ac32012-02-24 21:54:07 +0000854 // do this before we create the layer. We don't call the public save() since
855 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400856 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000857
858 fDeviceCMDirty = true;
859
860 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000861 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000862 return count;
863 }
864
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000865 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
866 // the clipRectBounds() call above?
867 if (kNoLayer_SaveLayerStrategy == strategy) {
868 return count;
869 }
870
reed@google.comb55deeb2012-01-06 14:43:09 +0000871 // Kill the imagefilter if our device doesn't allow it
872 SkLazyPaint lazyP;
873 if (paint && paint->getImageFilter()) {
874 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000875 if (justForImageFilter) {
876 // early exit if the layer was just for the imageFilter
877 return count;
878 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000879 SkPaint* p = lazyP.set(*paint);
880 p->setImageFilter(NULL);
881 paint = p;
882 }
883 }
884
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000885 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
886 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
887 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000888
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000889 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000890 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700891 device = this->getDevice();
892 if (device) {
893 device = device->createCompatibleDevice(info);
894 }
reed@google.com76dd2772012-01-05 21:15:07 +0000895 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000896 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000897 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000898 if (NULL == device) {
899 SkDebugf("Unable to create device for layer.");
900 return count;
901 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000902
reed@google.com6f8f2922011-03-04 22:27:10 +0000903 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700904 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
905 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000906 device->unref();
907
908 layer->fNext = fMCRec->fTopLayer;
909 fMCRec->fLayer = layer;
910 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
911
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000912 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000913 return count;
914}
915
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000916int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
917 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
918}
919
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
921 SaveFlags flags) {
922 if (0xFF == alpha) {
923 return this->saveLayer(bounds, NULL, flags);
924 } else {
925 SkPaint tmpPaint;
926 tmpPaint.setAlpha(alpha);
927 return this->saveLayer(bounds, &tmpPaint, flags);
928 }
929}
930
931void SkCanvas::restore() {
932 // check for underflow
933 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000934 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000935 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700936 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000937 }
938}
939
940void SkCanvas::internalRestore() {
941 SkASSERT(fMCStack.count() != 0);
942
943 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000944 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000945
Florin Malita5f6102d2014-06-30 10:13:28 -0400946 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000947
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000948 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 DeviceCM* layer = fMCRec->fLayer; // may be null
950 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
951 fMCRec->fLayer = NULL;
952
953 // now do the normal restore()
954 fMCRec->~MCRec(); // balanced in save()
955 fMCStack.pop_back();
956 fMCRec = (MCRec*)fMCStack.back();
957
958 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
959 since if we're being recorded, we don't want to record this (the
960 recorder will have already recorded the restore).
961 */
bsalomon49f085d2014-09-05 13:34:00 -0700962 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000964 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +0000965 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
966 layer->fPaint);
967 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +0000969
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000970 SkASSERT(fSaveLayerCount > 0);
971 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000972 }
973 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000974 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975}
976
977int SkCanvas::getSaveCount() const {
978 return fMCStack.count();
979}
980
981void SkCanvas::restoreToCount(int count) {
982 // sanity check
983 if (count < 1) {
984 count = 1;
985 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000986
reed@google.comb9d1c6a2011-11-28 16:09:24 +0000987 int n = this->getSaveCount() - count;
988 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 this->restore();
990 }
991}
992
reed@google.com7c202932011-12-14 18:48:05 +0000993bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000994 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +0000995}
996
reed29c857d2014-09-21 10:25:07 -0700997SkSurface* SkCanvas::newSurface(const SkImageInfo& info) {
998 return this->onNewSurface(info);
reed@google.com76f10a32014-02-05 15:32:21 +0000999}
1000
reed29c857d2014-09-21 10:25:07 -07001001SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info) {
reed@google.com76f10a32014-02-05 15:32:21 +00001002 SkBaseDevice* dev = this->getDevice();
reed29c857d2014-09-21 10:25:07 -07001003 return dev ? dev->newSurface(info) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001004}
1005
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001006SkImageInfo SkCanvas::imageInfo() const {
1007 SkBaseDevice* dev = this->getDevice();
1008 if (dev) {
1009 return dev->imageInfo();
1010 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001011 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001012 }
1013}
1014
1015const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1016 return this->onPeekPixels(info, rowBytes);
1017}
1018
1019const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1020 SkBaseDevice* dev = this->getDevice();
1021 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1022}
1023
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001024void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1025 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1026 if (pixels && origin) {
1027 *origin = this->getTopDevice(false)->getOrigin();
1028 }
1029 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001030}
1031
1032void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1033 SkBaseDevice* dev = this->getTopDevice();
1034 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1035}
1036
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001037SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1038 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1039 if (NULL == fAddr) {
1040 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001041 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001042 return; // failure, fAddr is NULL
1043 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001044 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1045 return; // failure, fAddr is NULL
1046 }
1047 fAddr = fBitmap.getPixels();
1048 fRowBytes = fBitmap.rowBytes();
1049 }
1050 SkASSERT(fAddr); // success
1051}
1052
1053bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1054 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001055 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001056 } else {
1057 bitmap->reset();
1058 return false;
1059 }
1060}
1061
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001062void SkCanvas::onPushCull(const SkRect& cullRect) {
1063 // do nothing. Subclasses may do something
1064}
1065
1066void SkCanvas::onPopCull() {
1067 // do nothing. Subclasses may do something
1068}
1069
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001071#ifdef SK_DEBUG
1072// Ensure that cull rects are monotonically nested in device space.
1073void SkCanvas::validateCull(const SkIRect& devCull) {
1074 if (fCullStack.isEmpty()
1075 || devCull.isEmpty()
1076 || fCullStack.top().contains(devCull)) {
1077 return;
1078 }
1079
1080 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1081 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1082 fCullStack.top().x(), fCullStack.top().y(),
1083 fCullStack.top().right(), fCullStack.top().bottom()));
1084
1085#ifdef ASSERT_NESTED_CULLING
1086 SkDEBUGFAIL("Invalid cull.");
1087#endif
1088}
1089#endif
1090
1091void SkCanvas::pushCull(const SkRect& cullRect) {
1092 ++fCullCount;
1093 this->onPushCull(cullRect);
1094
1095#ifdef SK_DEBUG
1096 // Map the cull rect into device space.
1097 SkRect mappedCull;
1098 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1099
1100 // Take clipping into account.
1101 SkIRect devClip, devCull;
1102 mappedCull.roundOut(&devCull);
1103 this->getClipDeviceBounds(&devClip);
1104 if (!devCull.intersect(devClip)) {
1105 devCull.setEmpty();
1106 }
1107
1108 this->validateCull(devCull);
1109 fCullStack.push(devCull); // balanced in popCull
1110#endif
1111}
1112
1113void SkCanvas::popCull() {
1114 SkASSERT(fCullStack.count() == fCullCount);
1115
1116 if (fCullCount > 0) {
1117 --fCullCount;
1118 this->onPopCull();
1119
1120 SkDEBUGCODE(fCullStack.pop());
1121 }
1122}
1123
1124/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001125
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001126void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001127 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001128 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001129 return;
1130 }
1131
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001132 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001133 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001134 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001135 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001136
1137 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001138
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001139 SkRect storage;
1140 const SkRect* bounds = NULL;
1141 if (paint && paint->canComputeFastBounds()) {
1142 bitmap.getBounds(&storage);
1143 matrix.mapRect(&storage);
1144 bounds = &paint->computeFastBounds(storage, &storage);
1145 }
1146
1147 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001148
1149 while (iter.next()) {
1150 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1151 }
1152
1153 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001154}
1155
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001156void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001157 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001158 SkPaint tmp;
1159 if (NULL == paint) {
1160 tmp.setDither(true);
1161 paint = &tmp;
1162 }
reed@google.com4b226022011-01-11 18:32:13 +00001163
reed@google.com8926b162012-03-23 15:36:36 +00001164 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001165 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001166 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001167 paint = &looper.paint();
1168 SkImageFilter* filter = paint->getImageFilter();
1169 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001170 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001171 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001172 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001173 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001174 const SkBitmap& src = srcDev->accessBitmap(false);
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(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001178 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->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, src, 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 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1184 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001185 }
1186 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001187 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001188 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001189 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001190 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001191}
1192
reed@google.com8926b162012-03-23 15:36:36 +00001193void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1194 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001195 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001196 return;
1197 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001198 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001199
reed@google.com8926b162012-03-23 15:36:36 +00001200 SkPaint tmp;
1201 if (NULL == paint) {
1202 paint = &tmp;
1203 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001204
reed@google.com8926b162012-03-23 15:36:36 +00001205 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001206
reed@google.com8926b162012-03-23 15:36:36 +00001207 while (iter.next()) {
1208 paint = &looper.paint();
1209 SkImageFilter* filter = paint->getImageFilter();
1210 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1211 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001212 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001213 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001214 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001215 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001216 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001217 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001218 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001219 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001220 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001221 SkPaint tmpUnfiltered(*paint);
1222 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001223 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001224 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001225 }
1226 } else {
1227 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1228 }
1229 }
1230 LOOPER_END
1231}
1232
reed@android.com8a1c16f2008-12-17 15:59:43 +00001233/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001234void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001235 SkMatrix m;
1236 m.setTranslate(dx, dy);
1237 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238}
1239
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001240void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001241 SkMatrix m;
1242 m.setScale(sx, sy);
1243 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244}
1245
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001246void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001247 SkMatrix m;
1248 m.setRotate(degrees);
1249 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001250}
1251
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001252void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001253 SkMatrix m;
1254 m.setSkew(sx, sy);
1255 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001256}
1257
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001258void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001259 if (matrix.isIdentity()) {
1260 return;
1261 }
1262
reed@android.com8a1c16f2008-12-17 15:59:43 +00001263 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001264 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001265 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001266
1267 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001268}
1269
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270void SkCanvas::setMatrix(const SkMatrix& matrix) {
1271 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001272 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001273 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001274 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275}
1276
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277void SkCanvas::resetMatrix() {
1278 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280 matrix.reset();
1281 this->setMatrix(matrix);
1282}
1283
1284//////////////////////////////////////////////////////////////////////////////
1285
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001286void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001287 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1288 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001289}
1290
1291void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001292#ifdef SK_ENABLE_CLIP_QUICKREJECT
1293 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001294 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001295 return false;
1296 }
1297
reed@google.com3b3e8952012-08-16 20:53:31 +00001298 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001299 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001300 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001301
1302 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001303 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001304 }
1305 }
1306#endif
1307
reed@google.com5c3d1472011-02-22 19:12:23 +00001308 AutoValidateClip avc(this);
1309
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001311 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001312 if (!fAllowSoftClip) {
1313 edgeStyle = kHard_ClipEdgeStyle;
1314 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315
reed1f836ee2014-07-07 07:49:34 -07001316 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001317 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001318 // the matrix. This means we don't have to a) make a path, and b) tell
1319 // the region code to scan-convert the path, only to discover that it
1320 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322
reed1f836ee2014-07-07 07:49:34 -07001323 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001324 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001325 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001327 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001328 // and clip against that, since it can handle any matrix. However, to
1329 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1330 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331 SkPath path;
1332
1333 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001334 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335 }
1336}
1337
reed73e714e2014-09-04 09:02:23 -07001338static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1339 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001340 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001341}
1342
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001343void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001344 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001345 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001346 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1347 } else {
1348 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001349 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001350}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001351
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001352void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001353 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001354 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001355 AutoValidateClip avc(this);
1356
1357 fDeviceCMDirty = true;
1358 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001359 if (!fAllowSoftClip) {
1360 edgeStyle = kHard_ClipEdgeStyle;
1361 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001362
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001363 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001364
1365 SkPath devPath;
1366 devPath.addRRect(transformedRRect);
1367
reed73e714e2014-09-04 09:02:23 -07001368 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001369 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001370 }
1371
1372 SkPath path;
1373 path.addRRect(rrect);
1374 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001375 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001376}
1377
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001378void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001379 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1380 SkRect r;
1381 if (!path.isInverseFillType() && path.isRect(&r)) {
1382 this->onClipRect(r, op, edgeStyle);
1383 } else {
1384 this->onClipPath(path, op, edgeStyle);
1385 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001386}
1387
1388void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001389#ifdef SK_ENABLE_CLIP_QUICKREJECT
1390 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001391 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001392 return false;
1393 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001394
reed@google.com3b3e8952012-08-16 20:53:31 +00001395 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001396 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001397 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001398
reed@google.comda17f752012-08-16 18:27:05 +00001399 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001400 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001401 }
1402 }
1403#endif
1404
reed@google.com5c3d1472011-02-22 19:12:23 +00001405 AutoValidateClip avc(this);
1406
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001408 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001409 if (!fAllowSoftClip) {
1410 edgeStyle = kHard_ClipEdgeStyle;
1411 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412
1413 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001414 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415
reed@google.comfe701122011-11-08 19:41:23 +00001416 // Check if the transfomation, or the original path itself
1417 // made us empty. Note this can also happen if we contained NaN
1418 // values. computing the bounds detects this, and will set our
1419 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1420 if (devPath.getBounds().isEmpty()) {
1421 // resetting the path will remove any NaN or other wanky values
1422 // that might upset our scan converter.
1423 devPath.reset();
1424 }
1425
reed@google.com5c3d1472011-02-22 19:12:23 +00001426 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001427 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001428
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001429 if (fAllowSimplifyClip) {
1430 devPath.reset();
1431 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1432 const SkClipStack* clipStack = getClipStack();
1433 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1434 const SkClipStack::Element* element;
1435 while ((element = iter.next())) {
1436 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001437 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001438 if (type != SkClipStack::Element::kEmpty_Type) {
1439 element->asPath(&operand);
1440 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001441 SkRegion::Op elementOp = element->getOp();
1442 if (elementOp == SkRegion::kReplace_Op) {
1443 devPath = operand;
1444 } else {
1445 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1446 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001447 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1448 // perhaps we need an API change to avoid this sort of mixed-signals about
1449 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001450 if (element->isAA()) {
1451 edgeStyle = kSoft_ClipEdgeStyle;
1452 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001453 }
1454 op = SkRegion::kReplace_Op;
1455 }
1456
reed73e714e2014-09-04 09:02:23 -07001457 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458}
1459
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001460void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001461 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001462}
1463
1464void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001465 AutoValidateClip avc(this);
1466
reed@android.com8a1c16f2008-12-17 15:59:43 +00001467 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001468 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469
reed@google.com5c3d1472011-02-22 19:12:23 +00001470 // todo: signal fClipStack that we have a region, and therefore (I guess)
1471 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001472 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001473
reed1f836ee2014-07-07 07:49:34 -07001474 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475}
1476
reed@google.com819c9212011-02-23 18:56:55 +00001477#ifdef SK_DEBUG
1478void SkCanvas::validateClip() const {
1479 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001480 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001481 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001482 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001483 return;
1484 }
1485
reed@google.com819c9212011-02-23 18:56:55 +00001486 SkIRect ir;
1487 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001488 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001489
robertphillips@google.com80214e22012-07-20 15:33:18 +00001490 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001491 const SkClipStack::Element* element;
1492 while ((element = iter.next()) != NULL) {
1493 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001494 case SkClipStack::Element::kRect_Type:
1495 element->getRect().round(&ir);
1496 tmpClip.op(ir, element->getOp());
1497 break;
1498 case SkClipStack::Element::kEmpty_Type:
1499 tmpClip.setEmpty();
1500 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001501 default: {
1502 SkPath path;
1503 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001504 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001505 break;
1506 }
reed@google.com819c9212011-02-23 18:56:55 +00001507 }
1508 }
reed@google.com819c9212011-02-23 18:56:55 +00001509}
1510#endif
1511
reed@google.com90c07ea2012-04-13 13:50:27 +00001512void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001513 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001514 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001515
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001516 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001517 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001518 }
1519}
1520
reed@google.com5c3d1472011-02-22 19:12:23 +00001521///////////////////////////////////////////////////////////////////////////////
1522
reed@google.com754de5f2014-02-24 19:38:20 +00001523bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001524 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001525}
1526
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001527bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001528 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001529}
1530
reed@google.com3b3e8952012-08-16 20:53:31 +00001531bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001532 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001533 return true;
1534
reed1f836ee2014-07-07 07:49:34 -07001535 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536 return true;
1537 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001538
reed1f836ee2014-07-07 07:49:34 -07001539 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001540 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001541 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001542 SkIRect idst;
1543 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001544 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001545 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001546 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001547
reed@android.coma380ae42009-07-21 01:17:02 +00001548 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001549 // TODO: should we use | instead, or compare all 4 at once?
1550 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001551 return true;
1552 }
reed@google.comc0784db2013-12-13 21:16:12 +00001553 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001554 return true;
1555 }
1556 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001557 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001558}
1559
reed@google.com3b3e8952012-08-16 20:53:31 +00001560bool SkCanvas::quickReject(const SkPath& path) const {
1561 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001562}
1563
reed@google.com3b3e8952012-08-16 20:53:31 +00001564bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001565 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001566 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001567 return false;
1568 }
1569
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001570 SkMatrix inverse;
1571 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001572 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001573 if (bounds) {
1574 bounds->setEmpty();
1575 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001576 return false;
1577 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001578
bsalomon49f085d2014-09-05 13:34:00 -07001579 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001580 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001581 // adjust it outwards in case we are antialiasing
1582 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001583
reed@google.com8f4d2302013-12-17 16:44:46 +00001584 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1585 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586 inverse.mapRect(bounds, r);
1587 }
1588 return true;
1589}
1590
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001591bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001592 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001593 if (clip.isEmpty()) {
1594 if (bounds) {
1595 bounds->setEmpty();
1596 }
1597 return false;
1598 }
1599
bsalomon49f085d2014-09-05 13:34:00 -07001600 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001601 *bounds = clip.getBounds();
1602 }
1603 return true;
1604}
1605
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001607 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001610const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001611 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001612}
1613
reed@google.com9c135db2014-03-12 18:28:35 +00001614GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1615 SkBaseDevice* dev = this->getTopDevice();
1616 return dev ? dev->accessRenderTarget() : NULL;
1617}
1618
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001619SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001620 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001621 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622}
1623
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001624GrContext* SkCanvas::getGrContext() {
1625#if SK_SUPPORT_GPU
1626 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001627 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001628 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001629 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001630 return renderTarget->getContext();
1631 }
1632 }
1633#endif
1634
1635 return NULL;
1636
1637}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001638
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001639void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1640 const SkPaint& paint) {
1641 if (outer.isEmpty()) {
1642 return;
1643 }
1644 if (inner.isEmpty()) {
1645 this->drawRRect(outer, paint);
1646 return;
1647 }
1648
1649 // We don't have this method (yet), but technically this is what we should
1650 // be able to assert...
1651 // SkASSERT(outer.contains(inner));
1652 //
1653 // For now at least check for containment of bounds
1654 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1655
1656 this->onDrawDRRect(outer, inner, paint);
1657}
1658
reed@android.com8a1c16f2008-12-17 15:59:43 +00001659//////////////////////////////////////////////////////////////////////////////
1660// These are the virtual drawing methods
1661//////////////////////////////////////////////////////////////////////////////
1662
reed@google.com2a981812011-04-14 18:59:28 +00001663void SkCanvas::clear(SkColor color) {
1664 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001665 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001666 while (iter.next()) {
1667 iter.fDevice->clear(color);
1668 }
1669}
1670
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001671void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001672 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001673 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1674 }
1675}
1676
reed@android.com8a1c16f2008-12-17 15:59:43 +00001677void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001678 this->internalDrawPaint(paint);
1679}
1680
1681void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001682 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001683
1684 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001685 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001686 }
1687
reed@google.com4e2b3d32011-04-07 14:18:59 +00001688 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689}
1690
1691void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1692 const SkPaint& paint) {
1693 if ((long)count <= 0) {
1694 return;
1695 }
1696
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001697 SkRect r, storage;
1698 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001699 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001700 // special-case 2 points (common for drawing a single line)
1701 if (2 == count) {
1702 r.set(pts[0], pts[1]);
1703 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001704 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001705 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001706 bounds = &paint.computeFastStrokeBounds(r, &storage);
1707 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001708 return;
1709 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001710 }
reed@google.coma584aed2012-05-16 14:06:02 +00001711
reed@android.com8a1c16f2008-12-17 15:59:43 +00001712 SkASSERT(pts != NULL);
1713
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001714 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001715
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001717 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718 }
reed@google.com4b226022011-01-11 18:32:13 +00001719
reed@google.com4e2b3d32011-04-07 14:18:59 +00001720 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001721}
1722
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001723void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001724 SkRect storage;
1725 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001727 bounds = &paint.computeFastBounds(r, &storage);
1728 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729 return;
1730 }
1731 }
reed@google.com4b226022011-01-11 18:32:13 +00001732
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001733 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734
1735 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001736 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737 }
1738
reed@google.com4e2b3d32011-04-07 14:18:59 +00001739 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001740}
1741
reed@google.com4ed0fb72012-12-12 20:48:18 +00001742void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001743 SkRect storage;
1744 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001745 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001746 bounds = &paint.computeFastBounds(oval, &storage);
1747 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001748 return;
1749 }
1750 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001751
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001752 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001753
1754 while (iter.next()) {
1755 iter.fDevice->drawOval(iter, oval, looper.paint());
1756 }
1757
1758 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001759}
1760
1761void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001762 SkRect storage;
1763 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001764 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001765 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1766 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001767 return;
1768 }
1769 }
1770
1771 if (rrect.isRect()) {
1772 // call the non-virtual version
1773 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001774 return;
1775 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001776 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001777 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1778 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001779 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001780
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001781 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001782
1783 while (iter.next()) {
1784 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1785 }
1786
1787 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001788}
1789
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001790void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1791 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001792 SkRect storage;
1793 const SkRect* bounds = NULL;
1794 if (paint.canComputeFastBounds()) {
1795 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1796 if (this->quickReject(*bounds)) {
1797 return;
1798 }
1799 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001800
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001801 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001802
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001803 while (iter.next()) {
1804 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1805 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001806
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001807 LOOPER_END
1808}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001809
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001810void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001811 if (!path.isFinite()) {
1812 return;
1813 }
1814
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001815 SkRect storage;
1816 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001817 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001818 const SkRect& pathBounds = path.getBounds();
1819 bounds = &paint.computeFastBounds(pathBounds, &storage);
1820 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001821 return;
1822 }
1823 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001824
1825 const SkRect& r = path.getBounds();
1826 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001827 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001828 this->internalDrawPaint(paint);
1829 }
1830 return;
1831 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001832
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001833 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001834
1835 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001836 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001837 }
1838
reed@google.com4e2b3d32011-04-07 14:18:59 +00001839 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001840}
1841
1842void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1843 const SkPaint* paint) {
1844 SkDEBUGCODE(bitmap.validate();)
1845
reed@google.com3d608122011-11-21 15:16:16 +00001846 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001847 SkRect bounds = {
1848 x, y,
1849 x + SkIntToScalar(bitmap.width()),
1850 y + SkIntToScalar(bitmap.height())
1851 };
1852 if (paint) {
1853 (void)paint->computeFastBounds(bounds, &bounds);
1854 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001855 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001856 return;
1857 }
1858 }
reed@google.com4b226022011-01-11 18:32:13 +00001859
reed@android.com8a1c16f2008-12-17 15:59:43 +00001860 SkMatrix matrix;
1861 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001862 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863}
1864
reed@google.com9987ec32011-09-07 11:57:52 +00001865// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001866void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001867 const SkRect& dst, const SkPaint* paint,
1868 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001869 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870 return;
1871 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001872
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001873 SkRect storage;
1874 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001875 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001876 if (paint) {
1877 bounds = &paint->computeFastBounds(dst, &storage);
1878 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001879 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001880 return;
1881 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001882 }
reed@google.com3d608122011-11-21 15:16:16 +00001883
reed@google.com33535f32012-09-25 15:37:50 +00001884 SkLazyPaint lazy;
1885 if (NULL == paint) {
1886 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001888
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001889 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001890
reed@google.com33535f32012-09-25 15:37:50 +00001891 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001892 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001893 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001894
reed@google.com33535f32012-09-25 15:37:50 +00001895 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001896}
1897
reed@google.com71121732012-09-18 15:14:33 +00001898void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001899 const SkRect& dst, const SkPaint* paint,
1900 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001901 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001902 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001903}
1904
reed@android.com8a1c16f2008-12-17 15:59:43 +00001905void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1906 const SkPaint* paint) {
1907 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001908 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909}
1910
reed@google.com9987ec32011-09-07 11:57:52 +00001911void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1912 const SkIRect& center, const SkRect& dst,
1913 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001914 if (bitmap.drawsNothing()) {
1915 return;
1916 }
reed@google.com3d608122011-11-21 15:16:16 +00001917 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001918 SkRect storage;
1919 const SkRect* bounds = &dst;
1920 if (paint) {
1921 bounds = &paint->computeFastBounds(dst, &storage);
1922 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001923 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001924 return;
1925 }
1926 }
1927
reed@google.com9987ec32011-09-07 11:57:52 +00001928 const int32_t w = bitmap.width();
1929 const int32_t h = bitmap.height();
1930
1931 SkIRect c = center;
1932 // pin center to the bounds of the bitmap
1933 c.fLeft = SkMax32(0, center.fLeft);
1934 c.fTop = SkMax32(0, center.fTop);
1935 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1936 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1937
reed@google.com71121732012-09-18 15:14:33 +00001938 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001939 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001940 };
1941 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001942 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001943 };
reed@google.com9987ec32011-09-07 11:57:52 +00001944 SkScalar dstX[4] = {
1945 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1946 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1947 };
1948 SkScalar dstY[4] = {
1949 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1950 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1951 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001952
reed@google.com9987ec32011-09-07 11:57:52 +00001953 if (dstX[1] > dstX[2]) {
1954 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
1955 dstX[2] = dstX[1];
1956 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001957
reed@google.com9987ec32011-09-07 11:57:52 +00001958 if (dstY[1] > dstY[2]) {
1959 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
1960 dstY[2] = dstY[1];
1961 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001962
reed@google.com9987ec32011-09-07 11:57:52 +00001963 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00001964 SkRect s, d;
1965
reed@google.com9987ec32011-09-07 11:57:52 +00001966 s.fTop = srcY[y];
1967 s.fBottom = srcY[y+1];
1968 d.fTop = dstY[y];
1969 d.fBottom = dstY[y+1];
1970 for (int x = 0; x < 3; x++) {
1971 s.fLeft = srcX[x];
1972 s.fRight = srcX[x+1];
1973 d.fLeft = dstX[x];
1974 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001975 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00001976 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00001977 }
1978 }
1979}
1980
1981void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
1982 const SkRect& dst, const SkPaint* paint) {
1983 SkDEBUGCODE(bitmap.validate();)
1984
1985 // Need a device entry-point, so gpu can use a mesh
1986 this->internalDrawBitmapNine(bitmap, center, dst, paint);
1987}
1988
reed@google.comf67e4cf2011-03-15 20:56:58 +00001989class SkDeviceFilteredPaint {
1990public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001991 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
1992 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00001993 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00001994 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001995 newPaint->setFlags(flags.fFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00001996 fPaint = newPaint;
1997 } else {
1998 fPaint = &paint;
1999 }
2000 }
2001
reed@google.comf67e4cf2011-03-15 20:56:58 +00002002 const SkPaint& paint() const { return *fPaint; }
2003
2004private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002005 const SkPaint* fPaint;
2006 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002007};
2008
bungeman@google.com52c748b2011-08-22 21:30:43 +00002009void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2010 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002011 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002012 draw.fDevice->drawRect(draw, r, paint);
2013 } else {
2014 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002015 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002016 draw.fDevice->drawRect(draw, r, p);
2017 }
2018}
2019
2020void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2021 const char text[], size_t byteLength,
2022 SkScalar x, SkScalar y) {
2023 SkASSERT(byteLength == 0 || text != NULL);
2024
2025 // nothing to draw
2026 if (text == NULL || byteLength == 0 ||
2027 draw.fClip->isEmpty() ||
2028 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2029 return;
2030 }
2031
2032 SkScalar width = 0;
2033 SkPoint start;
2034
2035 start.set(0, 0); // to avoid warning
2036 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2037 SkPaint::kStrikeThruText_Flag)) {
2038 width = paint.measureText(text, byteLength);
2039
2040 SkScalar offsetX = 0;
2041 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2042 offsetX = SkScalarHalf(width);
2043 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2044 offsetX = width;
2045 }
2046 start.set(x - offsetX, y);
2047 }
2048
2049 if (0 == width) {
2050 return;
2051 }
2052
2053 uint32_t flags = paint.getFlags();
2054
2055 if (flags & (SkPaint::kUnderlineText_Flag |
2056 SkPaint::kStrikeThruText_Flag)) {
2057 SkScalar textSize = paint.getTextSize();
2058 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2059 SkRect r;
2060
2061 r.fLeft = start.fX;
2062 r.fRight = start.fX + width;
2063
2064 if (flags & SkPaint::kUnderlineText_Flag) {
2065 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2066 start.fY);
2067 r.fTop = offset;
2068 r.fBottom = offset + height;
2069 DrawRect(draw, paint, r, textSize);
2070 }
2071 if (flags & SkPaint::kStrikeThruText_Flag) {
2072 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2073 start.fY);
2074 r.fTop = offset;
2075 r.fBottom = offset + height;
2076 DrawRect(draw, paint, r, textSize);
2077 }
2078 }
2079}
2080
reed@google.come0d9ce82014-04-23 04:00:17 +00002081void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2082 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002083 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002084
2085 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002086 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002087 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002088 DrawTextDecorations(iter, dfp.paint(),
2089 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002090 }
2091
reed@google.com4e2b3d32011-04-07 14:18:59 +00002092 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002093}
2094
reed@google.come0d9ce82014-04-23 04:00:17 +00002095void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2096 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002097 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002098
reed@android.com8a1c16f2008-12-17 15:59:43 +00002099 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002100 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002102 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002104
reed@google.com4e2b3d32011-04-07 14:18:59 +00002105 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106}
2107
reed@google.come0d9ce82014-04-23 04:00:17 +00002108void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2109 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002110 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002111
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002113 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002115 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002117
reed@google.com4e2b3d32011-04-07 14:18:59 +00002118 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119}
2120
reed@google.come0d9ce82014-04-23 04:00:17 +00002121void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2122 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002123 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002124
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125 while (iter.next()) {
2126 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002127 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002129
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002130 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002131}
2132
fmalita00d5c2c2014-08-21 08:53:26 -07002133void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2134 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002135
2136 // FIXME: temporarily disable quickreject for empty bounds,
2137 // pending implicit blob bounds implementation.
2138 if (!blob->bounds().isEmpty() && paint.canComputeFastBounds()) {
2139 SkRect storage;
2140
2141 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2142 return;
2143 }
2144 }
2145
fmalitaaa1b9122014-08-28 14:32:24 -07002146 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002147
fmalitaaa1b9122014-08-28 14:32:24 -07002148 while (iter.next()) {
2149 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2150 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002151 }
2152
fmalitaaa1b9122014-08-28 14:32:24 -07002153 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002154}
2155
reed@google.come0d9ce82014-04-23 04:00:17 +00002156// These will become non-virtual, so they always call the (virtual) onDraw... method
2157void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2158 const SkPaint& paint) {
2159 this->onDrawText(text, byteLength, x, y, paint);
2160}
2161void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2162 const SkPaint& paint) {
2163 this->onDrawPosText(text, byteLength, pos, paint);
2164}
2165void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2166 SkScalar constY, const SkPaint& paint) {
2167 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2168}
2169void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2170 const SkMatrix* matrix, const SkPaint& paint) {
2171 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2172}
fmalita00d5c2c2014-08-21 08:53:26 -07002173void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2174 const SkPaint& paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002175 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002176 this->onDrawTextBlob(blob, x, y, paint);
2177 }
2178}
reed@google.come0d9ce82014-04-23 04:00:17 +00002179
reed@android.com8a1c16f2008-12-17 15:59:43 +00002180void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2181 const SkPoint verts[], const SkPoint texs[],
2182 const SkColor colors[], SkXfermode* xmode,
2183 const uint16_t indices[], int indexCount,
2184 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002185 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002186
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187 while (iter.next()) {
2188 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002189 colors, xmode, indices, indexCount,
2190 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191 }
reed@google.com4b226022011-01-11 18:32:13 +00002192
reed@google.com4e2b3d32011-04-07 14:18:59 +00002193 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002194}
2195
dandovb3c9d1c2014-08-12 08:34:29 -07002196void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2197 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2198 if (NULL == cubics) {
2199 return;
2200 }
mtklein6cfa73a2014-08-13 13:33:49 -07002201
dandovecfff212014-08-04 10:02:00 -07002202 // Since a patch is always within the convex hull of the control points, we discard it when its
2203 // bounding rectangle is completely outside the current clip.
2204 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002205 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002206 if (this->quickReject(bounds)) {
2207 return;
2208 }
mtklein6cfa73a2014-08-13 13:33:49 -07002209
dandovb3c9d1c2014-08-12 08:34:29 -07002210 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2211}
2212
2213void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2214 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2215
dandovecfff212014-08-04 10:02:00 -07002216 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002217
dandovecfff212014-08-04 10:02:00 -07002218 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002219 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002220 }
mtklein6cfa73a2014-08-13 13:33:49 -07002221
dandovecfff212014-08-04 10:02:00 -07002222 LOOPER_END
2223}
2224
reed@android.com8a1c16f2008-12-17 15:59:43 +00002225//////////////////////////////////////////////////////////////////////////////
2226// These methods are NOT virtual, and therefore must call back into virtual
2227// methods, rather than actually drawing themselves.
2228//////////////////////////////////////////////////////////////////////////////
2229
2230void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002231 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002232 SkPaint paint;
2233
2234 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002235 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002236 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237 }
2238 this->drawPaint(paint);
2239}
2240
reed@android.com845fdac2009-06-23 03:01:32 +00002241void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002242 SkPaint paint;
2243
2244 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002245 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002246 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002247 }
2248 this->drawPaint(paint);
2249}
2250
2251void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2252 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002253
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254 pt.set(x, y);
2255 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2256}
2257
2258void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2259 SkPoint pt;
2260 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002261
reed@android.com8a1c16f2008-12-17 15:59:43 +00002262 pt.set(x, y);
2263 paint.setColor(color);
2264 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2265}
2266
2267void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2268 const SkPaint& paint) {
2269 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002270
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271 pts[0].set(x0, y0);
2272 pts[1].set(x1, y1);
2273 this->drawPoints(kLines_PointMode, 2, pts, paint);
2274}
2275
2276void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2277 SkScalar right, SkScalar bottom,
2278 const SkPaint& paint) {
2279 SkRect r;
2280
2281 r.set(left, top, right, bottom);
2282 this->drawRect(r, paint);
2283}
2284
2285void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2286 const SkPaint& paint) {
2287 if (radius < 0) {
2288 radius = 0;
2289 }
2290
2291 SkRect r;
2292 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002293 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002294}
2295
2296void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2297 const SkPaint& paint) {
2298 if (rx > 0 && ry > 0) {
2299 if (paint.canComputeFastBounds()) {
2300 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002301 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302 return;
2303 }
2304 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002305 SkRRect rrect;
2306 rrect.setRectXY(r, rx, ry);
2307 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002308 } else {
2309 this->drawRect(r, paint);
2310 }
2311}
2312
reed@android.com8a1c16f2008-12-17 15:59:43 +00002313void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2314 SkScalar sweepAngle, bool useCenter,
2315 const SkPaint& paint) {
2316 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2317 this->drawOval(oval, paint);
2318 } else {
2319 SkPath path;
2320 if (useCenter) {
2321 path.moveTo(oval.centerX(), oval.centerY());
2322 }
2323 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2324 if (useCenter) {
2325 path.close();
2326 }
2327 this->drawPath(path, paint);
2328 }
2329}
2330
2331void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2332 const SkPath& path, SkScalar hOffset,
2333 SkScalar vOffset, const SkPaint& paint) {
2334 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002335
reed@android.com8a1c16f2008-12-17 15:59:43 +00002336 matrix.setTranslate(hOffset, vOffset);
2337 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2338}
2339
reed@android.comf76bacf2009-05-13 14:00:33 +00002340///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002341void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002342 SkBaseDevice* device = this->getDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002343 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002344 device->EXPERIMENTAL_optimize(picture);
2345 }
2346}
reed@android.comf76bacf2009-05-13 14:00:33 +00002347
robertphillips9b14f262014-06-04 05:40:44 -07002348void SkCanvas::drawPicture(const SkPicture* picture) {
bsalomon49f085d2014-09-05 13:34:00 -07002349 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002350 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002351 }
2352}
2353
reedd5fa1a42014-08-09 11:08:05 -07002354void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002355 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002356 if (matrix && matrix->isIdentity()) {
2357 matrix = NULL;
2358 }
2359 this->onDrawPicture(picture, matrix, paint);
2360 }
2361}
robertphillips9b14f262014-06-04 05:40:44 -07002362
reedd5fa1a42014-08-09 11:08:05 -07002363void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2364 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002365 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002366 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002367 // Canvas has to first give the device the opportunity to render
2368 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002369 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002370 return; // the device has rendered the entire picture
2371 }
2372 }
2373
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002374 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002375
robertphillipsc5ba71d2014-09-04 08:42:50 -07002376 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002377}
2378
reed@android.com8a1c16f2008-12-17 15:59:43 +00002379///////////////////////////////////////////////////////////////////////////////
2380///////////////////////////////////////////////////////////////////////////////
2381
2382SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002383 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002384
2385 SkASSERT(canvas);
2386
2387 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2388 fDone = !fImpl->next();
2389}
2390
2391SkCanvas::LayerIter::~LayerIter() {
2392 fImpl->~SkDrawIter();
2393}
2394
2395void SkCanvas::LayerIter::next() {
2396 fDone = !fImpl->next();
2397}
2398
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002399SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002400 return fImpl->getDevice();
2401}
2402
2403const SkMatrix& SkCanvas::LayerIter::matrix() const {
2404 return fImpl->getMatrix();
2405}
2406
2407const SkPaint& SkCanvas::LayerIter::paint() const {
2408 const SkPaint* paint = fImpl->getPaint();
2409 if (NULL == paint) {
2410 paint = &fDefaultPaint;
2411 }
2412 return *paint;
2413}
2414
2415const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2416int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2417int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002418
2419///////////////////////////////////////////////////////////////////////////////
2420
fmalitac3b589a2014-06-05 12:40:07 -07002421SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002422
2423///////////////////////////////////////////////////////////////////////////////
2424
2425static bool supported_for_raster_canvas(const SkImageInfo& info) {
2426 switch (info.alphaType()) {
2427 case kPremul_SkAlphaType:
2428 case kOpaque_SkAlphaType:
2429 break;
2430 default:
2431 return false;
2432 }
2433
2434 switch (info.colorType()) {
2435 case kAlpha_8_SkColorType:
2436 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002437 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002438 break;
2439 default:
2440 return false;
2441 }
2442
2443 return true;
2444}
2445
2446SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2447 if (!supported_for_raster_canvas(info)) {
2448 return NULL;
2449 }
2450
2451 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -07002452 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002453 return NULL;
2454 }
2455
2456 // should this functionality be moved into allocPixels()?
2457 if (!bitmap.info().isOpaque()) {
2458 bitmap.eraseColor(0);
2459 }
2460 return SkNEW_ARGS(SkCanvas, (bitmap));
2461}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002462
2463SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2464 if (!supported_for_raster_canvas(info)) {
2465 return NULL;
2466 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002467
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002468 SkBitmap bitmap;
2469 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2470 return NULL;
2471 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002472 return SkNEW_ARGS(SkCanvas, (bitmap));
2473}
reedd5fa1a42014-08-09 11:08:05 -07002474
2475///////////////////////////////////////////////////////////////////////////////
2476
2477SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002478 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002479 : fCanvas(canvas)
2480 , fSaveCount(canvas->getSaveCount())
2481{
bsalomon49f085d2014-09-05 13:34:00 -07002482 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002483 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002484 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002485 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002486 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002487 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002488 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002489 canvas->save();
2490 }
mtklein6cfa73a2014-08-13 13:33:49 -07002491
bsalomon49f085d2014-09-05 13:34:00 -07002492 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002493 canvas->concat(*matrix);
2494 }
2495}
2496
2497SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2498 fCanvas->restoreToCount(fSaveCount);
2499}