blob: 82d48160dce4ccf3227ca9e0f279cd18c6b2e723 [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
8#include "SkCanvas.h"
reed6a070dc2014-11-11 19:36:09 -08009#include "SkCanvasDrawable.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"
piotaixrb5fae932014-09-24 13:03:30 -070016#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000017#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000018#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070019#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000020#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000021#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000022#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000023#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000024#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000025#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070026#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000027#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000028#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080029#include "SkTraceEvent.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000030#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000031
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000032#if SK_SUPPORT_GPU
33#include "GrRenderTarget.h"
34#endif
35
reed@google.comda17f752012-08-16 18:27:05 +000036// experimental for faster tiled drawing...
37//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000038
reed@android.com8a1c16f2008-12-17 15:59:43 +000039//#define SK_TRACE_SAVERESTORE
40
41#ifdef SK_TRACE_SAVERESTORE
42 static int gLayerCounter;
43 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
44 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
45
46 static int gRecCounter;
47 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
48 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
49
50 static int gCanvasCounter;
51 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
52 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
53#else
54 #define inc_layer()
55 #define dec_layer()
56 #define inc_rec()
57 #define dec_rec()
58 #define inc_canvas()
59 #define dec_canvas()
60#endif
61
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000062typedef SkTLazy<SkPaint> SkLazyPaint;
63
reed@google.com97af1a62012-08-28 12:19:02 +000064void SkCanvas::predrawNotify() {
65 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000066 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000067 }
68}
69
reed@android.com8a1c16f2008-12-17 15:59:43 +000070///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000071
reed4a8126e2014-09-22 07:29:03 -070072static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
73 const uint32_t propFlags = props.flags();
74 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
75 flags &= ~SkPaint::kDither_Flag;
76 }
77 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
78 flags &= ~SkPaint::kAntiAlias_Flag;
79 }
80 return flags;
81}
82
83///////////////////////////////////////////////////////////////////////////////
84
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000085/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 The clip/matrix/proc are fields that reflect the top of the save/restore
87 stack. Whenever the canvas changes, it marks a dirty flag, and then before
88 these are used (assuming we're not on a layer) we rebuild these cache
89 values: they reflect the top of the save stack, but translated and clipped
90 by the device's XY offset and bitmap-bounds.
91*/
92struct DeviceCM {
93 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000094 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000095 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000096 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000097 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000098
reedd9544982014-09-09 18:46:22 -070099 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
100 bool conservativeRasterClip)
101 : fNext(NULL)
102 , fClip(conservativeRasterClip)
103 {
104 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000106 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 }
reed@google.com4b226022011-01-11 18:32:13 +0000108 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000110 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000112 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700113 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000114 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000115 fDevice->unref();
116 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000117 SkDELETE(fPaint);
118 }
reed@google.com4b226022011-01-11 18:32:13 +0000119
reed@google.com045e62d2011-10-24 12:19:46 +0000120 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
121 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000122 int x = fDevice->getOrigin().x();
123 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 int width = fDevice->width();
125 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000126
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 if ((x | y) == 0) {
128 fMatrix = &totalMatrix;
129 fClip = totalClip;
130 } else {
131 fMatrixStorage = totalMatrix;
132 fMatrixStorage.postTranslate(SkIntToScalar(-x),
133 SkIntToScalar(-y));
134 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000135
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 totalClip.translate(-x, -y, &fClip);
137 }
138
reed@google.com045e62d2011-10-24 12:19:46 +0000139 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140
141 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000142
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000144 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000145 SkRegion::kDifference_Op);
146 }
reed@google.com4b226022011-01-11 18:32:13 +0000147
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000148 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
149
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150#ifdef SK_DEBUG
151 if (!fClip.isEmpty()) {
152 SkIRect deviceR;
153 deviceR.set(0, 0, width, height);
154 SkASSERT(deviceR.contains(fClip.getBounds()));
155 }
156#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000157 }
158
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000160 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161};
162
163/* This is the record we keep for each save/restore level in the stack.
164 Since a level optionally copies the matrix and/or stack, we have pointers
165 for these fields. If the value is copied for this level, the copy is
166 stored in the ...Storage field, and the pointer points to that. If the
167 value is not copied for this level, we ignore ...Storage, and just point
168 at the corresponding value in the previous level in the stack.
169*/
170class SkCanvas::MCRec {
171public:
reed6f097092014-09-09 12:51:10 -0700172 SkRasterClip fRasterClip;
reedd9544982014-09-09 18:46:22 -0700173 SkMatrix fMatrix;
reed1f836ee2014-07-07 07:49:34 -0700174 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700175 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 /* If there are any layers in the stack, this points to the top-most
177 one that is at or below this level in the stack (so we know what
178 bitmap/device to draw into from this level. This value is NOT
179 reference counted, since the real owner is either our fLayer field,
180 or a previous one in a lower level.)
181 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000182 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183
reedd9544982014-09-09 18:46:22 -0700184 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
185 fMatrix.reset();
186 fFilter = NULL;
187 fLayer = NULL;
188 fTopLayer = NULL;
piotaixrb5fae932014-09-24 13:03:30 -0700189
reedd9544982014-09-09 18:46:22 -0700190 // don't bother initializing fNext
191 inc_rec();
192 }
193 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip) {
194 fMatrix = prev.fMatrix;
195 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700197 fTopLayer = prev.fTopLayer;
piotaixrb5fae932014-09-24 13:03:30 -0700198
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199 // don't bother initializing fNext
200 inc_rec();
201 }
202 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000203 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 SkDELETE(fLayer);
205 dec_rec();
206 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207};
208
209class SkDrawIter : public SkDraw {
210public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000211 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000212 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000213 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 canvas->updateDeviceCMCache();
215
reed@google.com90c07ea2012-04-13 13:50:27 +0000216 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000218 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 }
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 bool next() {
222 // skip over recs with empty clips
223 if (fSkipEmptyClips) {
224 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
225 fCurrLayer = fCurrLayer->fNext;
226 }
227 }
228
reed@google.comf68c5e22012-02-24 16:38:58 +0000229 const DeviceCM* rec = fCurrLayer;
230 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231
232 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000233 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
234 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 fDevice = rec->fDevice;
236 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000238 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239
240 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000242
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 return true;
244 }
245 return false;
246 }
reed@google.com4b226022011-01-11 18:32:13 +0000247
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000248 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000249 int getX() const { return fDevice->getOrigin().x(); }
250 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 const SkMatrix& getMatrix() const { return *fMatrix; }
252 const SkRegion& getClip() const { return *fClip; }
253 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000254
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255private:
256 SkCanvas* fCanvas;
257 const DeviceCM* fCurrLayer;
258 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 SkBool8 fSkipEmptyClips;
260
261 typedef SkDraw INHERITED;
262};
263
264/////////////////////////////////////////////////////////////////////////////
265
266class AutoDrawLooper {
267public:
reed4a8126e2014-09-22 07:29:03 -0700268 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000269 bool skipLayerForImageFilter = false,
270 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000271 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700273 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000274 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000275 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000276 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277
reed@google.com8926b162012-03-23 15:36:36 +0000278 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
279 SkPaint tmp;
280 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000281 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
282 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000283 // we'll clear the imageFilter for the actual draws in next(), so
284 // it will only be applied during the restore().
285 fDoClearImageFilter = true;
286 }
287
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000288 if (SkDrawLooper* looper = paint.getLooper()) {
289 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
290 looper->contextSize());
291 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000292 fIsSimple = false;
293 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000294 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000295 // can we be marked as simple?
296 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000297 }
piotaixrb5fae932014-09-24 13:03:30 -0700298
reed4a8126e2014-09-22 07:29:03 -0700299 uint32_t oldFlags = paint.getFlags();
300 fNewPaintFlags = filter_paint_flags(props, oldFlags);
301 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
302 SkPaint* paint = fLazyPaint.set(fOrigPaint);
303 paint->setFlags(fNewPaintFlags);
304 fPaint = paint;
305 // if we're not simple, doNext() will take care of calling setFlags()
306 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000307 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000308
reed@android.com8a1c16f2008-12-17 15:59:43 +0000309 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000310 if (fDoClearImageFilter) {
311 fCanvas->internalRestore();
312 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000315
reed@google.com4e2b3d32011-04-07 14:18:59 +0000316 const SkPaint& paint() const {
317 SkASSERT(fPaint);
318 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000320
reed@google.com129ec222012-05-15 13:24:09 +0000321 bool next(SkDrawFilter::Type drawType) {
322 if (fDone) {
323 return false;
324 } else if (fIsSimple) {
325 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000326 return !fPaint->nothingToDraw();
327 } else {
328 return this->doNext(drawType);
329 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000330 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000331
reed@android.com8a1c16f2008-12-17 15:59:43 +0000332private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000333 SkLazyPaint fLazyPaint;
334 SkCanvas* fCanvas;
335 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000336 SkDrawFilter* fFilter;
337 const SkPaint* fPaint;
338 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700339 uint32_t fNewPaintFlags;
reed@google.com8926b162012-03-23 15:36:36 +0000340 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000341 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000342 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000343 SkDrawLooper::Context* fLooperContext;
344 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000345
346 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347};
348
reed@google.com129ec222012-05-15 13:24:09 +0000349bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000350 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000351 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000352 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000353
354 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700355 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000356
357 if (fDoClearImageFilter) {
358 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000359 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000360
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000361 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000362 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000363 return false;
364 }
365 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000366 if (!fFilter->filter(paint, drawType)) {
367 fDone = true;
368 return false;
369 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000370 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000371 // no looper means we only draw once
372 fDone = true;
373 }
374 }
375 fPaint = paint;
376
377 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000378 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000379 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000380 }
381
382 // call this after any possible paint modifiers
383 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000384 fPaint = NULL;
385 return false;
386 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000387 return true;
388}
389
reed@android.com8a1c16f2008-12-17 15:59:43 +0000390////////// macros to place around the internal draw calls //////////////////
391
reed@google.com8926b162012-03-23 15:36:36 +0000392#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000393 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700394 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000395 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000396 SkDrawIter iter(this);
397
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000398#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000399 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700400 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000401 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000403
reed@google.com4e2b3d32011-04-07 14:18:59 +0000404#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000405
406////////////////////////////////////////////////////////////////////////////
407
reedd9544982014-09-09 18:46:22 -0700408SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
409 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000410 fCachedLocalClipBounds.setEmpty();
411 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000412 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000413 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700414 fDeviceCMDirty = true;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000415 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000416 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000417 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000418
419 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700420 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000421
reedd9544982014-09-09 18:46:22 -0700422 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424
reed@google.com97af1a62012-08-28 12:19:02 +0000425 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000426
reedf92c8662014-08-18 08:02:43 -0700427 if (device) {
reedb2db8982014-11-13 12:41:02 -0800428 device->initForRootLayer(fProps.pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700429 if (device->forceConservativeRasterClip()) {
430 fConservativeRasterClip = true;
431 }
reedf92c8662014-08-18 08:02:43 -0700432 device->onAttachToCanvas(this);
433 fMCRec->fLayer->fDevice = SkRef(device);
434 fMCRec->fRasterClip.setRect(SkIRect::MakeWH(device->width(), device->height()));
435 }
436 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437}
438
reed@google.comcde92112011-07-06 20:00:52 +0000439SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000440 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700441 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000442{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000443 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000444
reedd9544982014-09-09 18:46:22 -0700445 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000446}
447
reedd9544982014-09-09 18:46:22 -0700448static SkBitmap make_nopixels(int width, int height) {
449 SkBitmap bitmap;
450 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
451 return bitmap;
452}
453
454class SkNoPixelsBitmapDevice : public SkBitmapDevice {
455public:
456 SkNoPixelsBitmapDevice(int width, int height) : INHERITED(make_nopixels(width, height)) {}
457
458private:
piotaixrb5fae932014-09-24 13:03:30 -0700459
reedd9544982014-09-09 18:46:22 -0700460 typedef SkBitmapDevice INHERITED;
461};
462
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000463SkCanvas::SkCanvas(int width, int height)
464 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700465 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000466{
467 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700468
reedd9544982014-09-09 18:46:22 -0700469 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), kDefault_InitFlags)->unref();
470}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000471
reedd9544982014-09-09 18:46:22 -0700472SkCanvas::SkCanvas(int width, int height, InitFlags flags)
473 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700474 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700475{
476 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700477
reedd9544982014-09-09 18:46:22 -0700478 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), flags)->unref();
479}
480
reed4a8126e2014-09-22 07:29:03 -0700481SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700482 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700483 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700484{
485 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700486
reedd9544982014-09-09 18:46:22 -0700487 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000488}
489
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000490SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000491 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700492 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000493{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000494 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700495
reedd9544982014-09-09 18:46:22 -0700496 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000497}
498
reed4a8126e2014-09-22 07:29:03 -0700499SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700500 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700501 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700502{
503 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700504
reed4a8126e2014-09-22 07:29:03 -0700505 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
506 this->init(device, kDefault_InitFlags);
507}
reed29c857d2014-09-21 10:25:07 -0700508
reed4a8126e2014-09-22 07:29:03 -0700509SkCanvas::SkCanvas(const SkBitmap& bitmap)
510 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
511 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
512{
513 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700514
reed4a8126e2014-09-22 07:29:03 -0700515 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
516 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517}
518
519SkCanvas::~SkCanvas() {
520 // free up the contents of our deque
521 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000522 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000523
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 this->internalRestore(); // restore the last, since we're going away
525
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000526 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000527
reed@android.com8a1c16f2008-12-17 15:59:43 +0000528 dec_canvas();
529}
530
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531SkDrawFilter* SkCanvas::getDrawFilter() const {
532 return fMCRec->fFilter;
533}
534
535SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
536 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
537 return filter;
538}
539
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000540SkMetaData& SkCanvas::getMetaData() {
541 // metadata users are rare, so we lazily allocate it. If that changes we
542 // can decide to just make it a field in the device (rather than a ptr)
543 if (NULL == fMetaData) {
544 fMetaData = new SkMetaData;
545 }
546 return *fMetaData;
547}
548
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549///////////////////////////////////////////////////////////////////////////////
550
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000551void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000552 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000553 if (device) {
554 device->flush();
555 }
556}
557
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000558SkISize SkCanvas::getTopLayerSize() const {
559 SkBaseDevice* d = this->getTopDevice();
560 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
561}
562
563SkIPoint SkCanvas::getTopLayerOrigin() const {
564 SkBaseDevice* d = this->getTopDevice();
565 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
566}
567
568SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000569 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000570 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
571}
572
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000573SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000574 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000575 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 SkASSERT(rec && rec->fLayer);
577 return rec->fLayer->fDevice;
578}
579
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000580SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000581 if (updateMatrixClip) {
582 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
583 }
reed@google.com9266fed2011-03-30 00:18:03 +0000584 return fMCRec->fTopLayer->fDevice;
585}
586
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000587SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000588 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000589 SkDeque::F2BIter iter(fMCStack);
590 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000592 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593
594 if (rootDevice == device) {
595 return device;
596 }
reed@google.com4b226022011-01-11 18:32:13 +0000597
reed@android.com8a1c16f2008-12-17 15:59:43 +0000598 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000599 device->onAttachToCanvas(this);
reedb2db8982014-11-13 12:41:02 -0800600 device->initForRootLayer(fProps.pixelGeometry());
reed@android.com8a1c16f2008-12-17 15:59:43 +0000601 }
602 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000603 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000604 }
605
606 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
607 rootDevice = device;
608
609 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000610
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 /* Now we update our initial region to have the bounds of the new device,
612 and then intersect all of the clips in our stack with these bounds,
613 to ensure that we can't draw outside of the device's bounds (and trash
614 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 NOTE: this is only a partial-fix, since if the new device is larger than
617 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000618 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
620 reconstruct the correct clips, so this approximation will have to do.
621 The caller really needs to restore() back to the base if they want to
622 accurately take advantage of the new device bounds.
623 */
624
reed@google.com42aea282012-03-28 16:19:15 +0000625 SkIRect bounds;
626 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000628 } else {
629 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000630 }
reed@google.com42aea282012-03-28 16:19:15 +0000631 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700632 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000633 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700634 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000635 }
636
reed@android.com8a1c16f2008-12-17 15:59:43 +0000637 return device;
638}
639
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000640bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
641 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
642 return false;
643 }
644
645 bool weAllocated = false;
646 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700647 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000648 return false;
649 }
650 weAllocated = true;
651 }
652
653 SkBitmap bm(*bitmap);
654 bm.lockPixels();
655 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
656 return true;
657 }
658
659 if (weAllocated) {
660 bitmap->setPixelRef(NULL);
661 }
662 return false;
663}
reed@google.com51df9e32010-12-23 19:29:18 +0000664
bsalomon@google.comc6980972011-11-02 19:57:21 +0000665bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000666 SkIRect r = srcRect;
667 const SkISize size = this->getBaseLayerSize();
668 if (!r.intersect(0, 0, size.width(), size.height())) {
669 bitmap->reset();
670 return false;
671 }
672
reed84825042014-09-02 12:50:45 -0700673 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000674 // bitmap will already be reset.
675 return false;
676 }
677 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
678 bitmap->reset();
679 return false;
680 }
681 return true;
682}
683
684bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
685 switch (origInfo.colorType()) {
686 case kUnknown_SkColorType:
687 case kIndex_8_SkColorType:
688 return false;
689 default:
690 break;
691 }
692 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
693 return false;
694 }
695 if (0 == origInfo.width() || 0 == origInfo.height()) {
696 return false;
697 }
698
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000699 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000700 if (!device) {
701 return false;
702 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000703
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000704 const SkISize size = this->getBaseLayerSize();
705 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
706 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000707 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000708 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000709
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000710 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700711 const SkImageInfo info = origInfo.makeWH(srcR.width(), srcR.height());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000712
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000713 // if x or y are negative, then we have to adjust pixels
714 if (x > 0) {
715 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000716 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000717 if (y > 0) {
718 y = 0;
719 }
720 // here x,y are either 0 or negative
721 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000722
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000723 // The device can assert that the requested area is always contained in its bounds
724 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000725}
726
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000727bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
728 if (bitmap.getTexture()) {
729 return false;
730 }
731 SkBitmap bm(bitmap);
732 bm.lockPixels();
733 if (bm.getPixels()) {
734 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
735 }
736 return false;
737}
738
739bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
740 int x, int y) {
741 switch (origInfo.colorType()) {
742 case kUnknown_SkColorType:
743 case kIndex_8_SkColorType:
744 return false;
745 default:
746 break;
747 }
748 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
749 return false;
750 }
751
752 const SkISize size = this->getBaseLayerSize();
753 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
754 if (!target.intersect(0, 0, size.width(), size.height())) {
755 return false;
756 }
757
758 SkBaseDevice* device = this->getDevice();
759 if (!device) {
760 return false;
761 }
762
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000763 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700764 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000765
766 // if x or y are negative, then we have to adjust pixels
767 if (x > 0) {
768 x = 0;
769 }
770 if (y > 0) {
771 y = 0;
772 }
773 // here x,y are either 0 or negative
774 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
775
reed4af35f32014-06-27 17:47:49 -0700776 // Tell our owning surface to bump its generation ID
777 this->predrawNotify();
778
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000779 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000780 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000781}
reed@google.com51df9e32010-12-23 19:29:18 +0000782
junov@google.com4370aed2012-01-18 16:21:08 +0000783SkCanvas* SkCanvas::canvasForDrawIter() {
784 return this;
785}
786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787//////////////////////////////////////////////////////////////////////////////
788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789void SkCanvas::updateDeviceCMCache() {
790 if (fDeviceCMDirty) {
791 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700792 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000794
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000796 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000798 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000800 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 } while ((layer = layer->fNext) != NULL);
802 }
803 fDeviceCMDirty = false;
804 }
805}
806
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807///////////////////////////////////////////////////////////////////////////////
808
Florin Malita5f6102d2014-06-30 10:13:28 -0400809int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000810 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000811
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700813 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000815
Florin Malita5f6102d2014-06-30 10:13:28 -0400816 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000817
reed@android.com8a1c16f2008-12-17 15:59:43 +0000818 return saveCount;
819}
820
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000821int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400822 this->willSave();
823 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824}
825
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000827#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000829#else
830 return true;
831#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000832}
833
junov@chromium.orga907ac32012-02-24 21:54:07 +0000834bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000835 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000836 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000837 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000838 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000839 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000840 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000841
842 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700843 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000844 // Filters may grow the bounds beyond the device bounds.
845 op = SkRegion::kReplace_Op;
846 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000847 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700848 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000849 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000850
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 this->getTotalMatrix().mapRect(&r, *bounds);
852 r.roundOut(&ir);
853 // early exit if the layer's bounds are clipped out
854 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000855 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700856 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000857 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000858 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000859 }
860 } else { // no user bounds, so just use the clip
861 ir = clipBounds;
862 }
863
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000864 if (bounds_affects_clip(flags)) {
865 fClipStack.clipDevRect(ir, op);
866 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700867 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000868 return false;
869 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000870 }
871
872 if (intersection) {
873 *intersection = ir;
874 }
875 return true;
876}
877
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000878int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
879 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
880 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
881}
882
junov@chromium.orga907ac32012-02-24 21:54:07 +0000883int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
884 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000885 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
886 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000887}
888
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000889int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
890 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000891#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000892 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000893#endif
894
junov@chromium.orga907ac32012-02-24 21:54:07 +0000895 // do this before we create the layer. We don't call the public save() since
896 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400897 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000898
899 fDeviceCMDirty = true;
900
901 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000902 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000903 return count;
904 }
905
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000906 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
907 // the clipRectBounds() call above?
908 if (kNoLayer_SaveLayerStrategy == strategy) {
909 return count;
910 }
911
reed@google.comb55deeb2012-01-06 14:43:09 +0000912 // Kill the imagefilter if our device doesn't allow it
913 SkLazyPaint lazyP;
914 if (paint && paint->getImageFilter()) {
915 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000916 if (justForImageFilter) {
917 // early exit if the layer was just for the imageFilter
918 return count;
919 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000920 SkPaint* p = lazyP.set(*paint);
921 p->setImageFilter(NULL);
922 paint = p;
923 }
924 }
925
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000926 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
927 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
928 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000929
reedb2db8982014-11-13 12:41:02 -0800930 SkBaseDevice* device = this->getTopDevice();
931 if (NULL == device) {
932 SkDebugf("Unable to find device for layer.");
933 return count;
reed@google.com76dd2772012-01-05 21:15:07 +0000934 }
reedb2db8982014-11-13 12:41:02 -0800935
936 SkBaseDevice::Usage usage = SkBaseDevice::kSaveLayer_Usage;
937 if (paint && paint->getImageFilter()) {
938 usage = SkBaseDevice::kImageFilter_Usage;
939 }
940 device = device->onCreateCompatibleDevice(SkBaseDevice::CreateInfo(info, usage,
941 fProps.pixelGeometry()));
bungeman@google.come25c6842011-08-17 14:53:54 +0000942 if (NULL == device) {
943 SkDebugf("Unable to create device for layer.");
944 return count;
945 }
bsalomon@google.come97f0852011-06-17 13:10:25 +0000946
reed@google.com6f8f2922011-03-04 22:27:10 +0000947 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700948 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
949 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 device->unref();
951
952 layer->fNext = fMCRec->fTopLayer;
953 fMCRec->fLayer = layer;
954 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
955
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000956 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 return count;
958}
959
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000960int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
961 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
962}
963
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
965 SaveFlags flags) {
966 if (0xFF == alpha) {
967 return this->saveLayer(bounds, NULL, flags);
968 } else {
969 SkPaint tmpPaint;
970 tmpPaint.setAlpha(alpha);
971 return this->saveLayer(bounds, &tmpPaint, flags);
972 }
973}
974
975void SkCanvas::restore() {
976 // check for underflow
977 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000978 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700980 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 }
982}
983
984void SkCanvas::internalRestore() {
985 SkASSERT(fMCStack.count() != 0);
986
987 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000988 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989
Florin Malita5f6102d2014-06-30 10:13:28 -0400990 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000991
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000992 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 DeviceCM* layer = fMCRec->fLayer; // may be null
994 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
995 fMCRec->fLayer = NULL;
996
997 // now do the normal restore()
998 fMCRec->~MCRec(); // balanced in save()
999 fMCStack.pop_back();
1000 fMCRec = (MCRec*)fMCStack.back();
1001
1002 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1003 since if we're being recorded, we don't want to record this (the
1004 recorder will have already recorded the restore).
1005 */
bsalomon49f085d2014-09-05 13:34:00 -07001006 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001008 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001009 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1010 layer->fPaint);
1011 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001013
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001014 SkASSERT(fSaveLayerCount > 0);
1015 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 }
1017 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001018 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019}
1020
1021int SkCanvas::getSaveCount() const {
1022 return fMCStack.count();
1023}
1024
1025void SkCanvas::restoreToCount(int count) {
1026 // sanity check
1027 if (count < 1) {
1028 count = 1;
1029 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001030
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001031 int n = this->getSaveCount() - count;
1032 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001033 this->restore();
1034 }
1035}
1036
reed@google.com7c202932011-12-14 18:48:05 +00001037bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001038 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001039}
1040
reed4a8126e2014-09-22 07:29:03 -07001041SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1042 if (NULL == props) {
1043 props = &fProps;
1044 }
1045 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001046}
1047
reed4a8126e2014-09-22 07:29:03 -07001048SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001049 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001050 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001051}
1052
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001053SkImageInfo SkCanvas::imageInfo() const {
1054 SkBaseDevice* dev = this->getDevice();
1055 if (dev) {
1056 return dev->imageInfo();
1057 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001058 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001059 }
1060}
1061
1062const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1063 return this->onPeekPixels(info, rowBytes);
1064}
1065
1066const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1067 SkBaseDevice* dev = this->getDevice();
1068 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1069}
1070
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001071void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1072 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1073 if (pixels && origin) {
1074 *origin = this->getTopDevice(false)->getOrigin();
1075 }
1076 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001077}
1078
1079void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1080 SkBaseDevice* dev = this->getTopDevice();
1081 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1082}
1083
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001084SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1085 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1086 if (NULL == fAddr) {
1087 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001088 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001089 return; // failure, fAddr is NULL
1090 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001091 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1092 return; // failure, fAddr is NULL
1093 }
1094 fAddr = fBitmap.getPixels();
1095 fRowBytes = fBitmap.rowBytes();
1096 }
1097 SkASSERT(fAddr); // success
1098}
1099
1100bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1101 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001102 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001103 } else {
1104 bitmap->reset();
1105 return false;
1106 }
1107}
1108
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001109void SkCanvas::onPushCull(const SkRect& cullRect) {
1110 // do nothing. Subclasses may do something
1111}
1112
1113void SkCanvas::onPopCull() {
1114 // do nothing. Subclasses may do something
1115}
1116
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001118#ifdef SK_DEBUG
1119// Ensure that cull rects are monotonically nested in device space.
1120void SkCanvas::validateCull(const SkIRect& devCull) {
1121 if (fCullStack.isEmpty()
1122 || devCull.isEmpty()
1123 || fCullStack.top().contains(devCull)) {
1124 return;
1125 }
1126
1127 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1128 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1129 fCullStack.top().x(), fCullStack.top().y(),
1130 fCullStack.top().right(), fCullStack.top().bottom()));
1131
1132#ifdef ASSERT_NESTED_CULLING
1133 SkDEBUGFAIL("Invalid cull.");
1134#endif
1135}
1136#endif
1137
1138void SkCanvas::pushCull(const SkRect& cullRect) {
1139 ++fCullCount;
1140 this->onPushCull(cullRect);
1141
1142#ifdef SK_DEBUG
1143 // Map the cull rect into device space.
1144 SkRect mappedCull;
1145 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1146
1147 // Take clipping into account.
1148 SkIRect devClip, devCull;
1149 mappedCull.roundOut(&devCull);
1150 this->getClipDeviceBounds(&devClip);
1151 if (!devCull.intersect(devClip)) {
1152 devCull.setEmpty();
1153 }
1154
1155 this->validateCull(devCull);
1156 fCullStack.push(devCull); // balanced in popCull
1157#endif
1158}
1159
1160void SkCanvas::popCull() {
1161 SkASSERT(fCullStack.count() == fCullCount);
1162
1163 if (fCullCount > 0) {
1164 --fCullCount;
1165 this->onPopCull();
1166
1167 SkDEBUGCODE(fCullStack.pop());
1168 }
1169}
1170
1171/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001173void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001175 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 return;
1177 }
1178
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001179 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001181 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001183
1184 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001185
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001186 SkRect storage;
1187 const SkRect* bounds = NULL;
1188 if (paint && paint->canComputeFastBounds()) {
1189 bitmap.getBounds(&storage);
1190 matrix.mapRect(&storage);
1191 bounds = &paint->computeFastBounds(storage, &storage);
1192 }
1193
1194 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001195
1196 while (iter.next()) {
1197 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1198 }
1199
1200 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201}
1202
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001203void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001204 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205 SkPaint tmp;
1206 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 paint = &tmp;
1208 }
reed@google.com4b226022011-01-11 18:32:13 +00001209
reed@google.com8926b162012-03-23 15:36:36 +00001210 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001212 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001213 paint = &looper.paint();
1214 SkImageFilter* filter = paint->getImageFilter();
1215 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001216 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001217 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001218 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001219 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001220 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001221 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001222 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001223 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001224 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001225 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001226 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001227 SkPaint tmpUnfiltered(*paint);
1228 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001229 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1230 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001231 }
1232 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001233 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001234 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001236 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237}
1238
reed@google.com8926b162012-03-23 15:36:36 +00001239void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1240 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001241 TRACE_EVENT0("skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001242 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001243 return;
1244 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001245 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001246
reed@google.com8926b162012-03-23 15:36:36 +00001247 SkPaint tmp;
1248 if (NULL == paint) {
1249 paint = &tmp;
1250 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001251
reed@google.com8926b162012-03-23 15:36:36 +00001252 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001253
reed@google.com8926b162012-03-23 15:36:36 +00001254 while (iter.next()) {
1255 paint = &looper.paint();
1256 SkImageFilter* filter = paint->getImageFilter();
1257 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1258 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001259 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001260 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001261 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001262 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001263 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001264 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001265 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001266 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001267 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001268 SkPaint tmpUnfiltered(*paint);
1269 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001270 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001271 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001272 }
1273 } else {
1274 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1275 }
1276 }
1277 LOOPER_END
1278}
1279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001281void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001282 SkMatrix m;
1283 m.setTranslate(dx, dy);
1284 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285}
1286
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001287void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001288 SkMatrix m;
1289 m.setScale(sx, sy);
1290 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291}
1292
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001293void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001294 SkMatrix m;
1295 m.setRotate(degrees);
1296 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297}
1298
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001299void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001300 SkMatrix m;
1301 m.setSkew(sx, sy);
1302 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001303}
1304
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001305void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001306 if (matrix.isIdentity()) {
1307 return;
1308 }
1309
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001311 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001312 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001313
1314 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001315}
1316
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317void SkCanvas::setMatrix(const SkMatrix& matrix) {
1318 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001319 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001320 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001321 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322}
1323
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324void SkCanvas::resetMatrix() {
1325 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001326
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 matrix.reset();
1328 this->setMatrix(matrix);
1329}
1330
1331//////////////////////////////////////////////////////////////////////////////
1332
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001333void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001334 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1335 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001336}
1337
1338void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001339#ifdef SK_ENABLE_CLIP_QUICKREJECT
1340 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001341 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001342 return false;
1343 }
1344
reed@google.com3b3e8952012-08-16 20:53:31 +00001345 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001346 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001347 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001348
1349 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001350 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001351 }
1352 }
1353#endif
1354
reed@google.com5c3d1472011-02-22 19:12:23 +00001355 AutoValidateClip avc(this);
1356
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001358 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001359 if (!fAllowSoftClip) {
1360 edgeStyle = kHard_ClipEdgeStyle;
1361 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362
reed1f836ee2014-07-07 07:49:34 -07001363 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001364 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001365 // the matrix. This means we don't have to a) make a path, and b) tell
1366 // the region code to scan-convert the path, only to discover that it
1367 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369
reed1f836ee2014-07-07 07:49:34 -07001370 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001371 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001372 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001374 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001375 // and clip against that, since it can handle any matrix. However, to
1376 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1377 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378 SkPath path;
1379
1380 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001381 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382 }
1383}
1384
reed73e714e2014-09-04 09:02:23 -07001385static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1386 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001387 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001388}
1389
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001390void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001391 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001392 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001393 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1394 } else {
1395 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001396 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001397}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001398
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001399void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001400 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001401 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001402 AutoValidateClip avc(this);
1403
1404 fDeviceCMDirty = true;
1405 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001406 if (!fAllowSoftClip) {
1407 edgeStyle = kHard_ClipEdgeStyle;
1408 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001409
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001410 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001411
1412 SkPath devPath;
1413 devPath.addRRect(transformedRRect);
1414
reed73e714e2014-09-04 09:02:23 -07001415 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001416 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001417 }
1418
1419 SkPath path;
1420 path.addRRect(rrect);
1421 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001422 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001423}
1424
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001425void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001426 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1427 SkRect r;
1428 if (!path.isInverseFillType() && path.isRect(&r)) {
1429 this->onClipRect(r, op, edgeStyle);
1430 } else {
1431 this->onClipPath(path, op, edgeStyle);
1432 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001433}
1434
1435void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001436#ifdef SK_ENABLE_CLIP_QUICKREJECT
1437 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001438 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001439 return false;
1440 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001441
reed@google.com3b3e8952012-08-16 20:53:31 +00001442 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001443 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001444 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001445
reed@google.comda17f752012-08-16 18:27:05 +00001446 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001447 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001448 }
1449 }
1450#endif
1451
reed@google.com5c3d1472011-02-22 19:12:23 +00001452 AutoValidateClip avc(this);
1453
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001455 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001456 if (!fAllowSoftClip) {
1457 edgeStyle = kHard_ClipEdgeStyle;
1458 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459
1460 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001461 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462
reed@google.comfe701122011-11-08 19:41:23 +00001463 // Check if the transfomation, or the original path itself
1464 // made us empty. Note this can also happen if we contained NaN
1465 // values. computing the bounds detects this, and will set our
1466 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1467 if (devPath.getBounds().isEmpty()) {
1468 // resetting the path will remove any NaN or other wanky values
1469 // that might upset our scan converter.
1470 devPath.reset();
1471 }
1472
reed@google.com5c3d1472011-02-22 19:12:23 +00001473 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001474 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001475
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001476 if (fAllowSimplifyClip) {
1477 devPath.reset();
1478 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1479 const SkClipStack* clipStack = getClipStack();
1480 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1481 const SkClipStack::Element* element;
1482 while ((element = iter.next())) {
1483 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001484 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001485 if (type != SkClipStack::Element::kEmpty_Type) {
1486 element->asPath(&operand);
1487 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001488 SkRegion::Op elementOp = element->getOp();
1489 if (elementOp == SkRegion::kReplace_Op) {
1490 devPath = operand;
1491 } else {
1492 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1493 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001494 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1495 // perhaps we need an API change to avoid this sort of mixed-signals about
1496 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001497 if (element->isAA()) {
1498 edgeStyle = kSoft_ClipEdgeStyle;
1499 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001500 }
1501 op = SkRegion::kReplace_Op;
1502 }
1503
reed73e714e2014-09-04 09:02:23 -07001504 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505}
1506
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001507void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001509}
1510
1511void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001512 AutoValidateClip avc(this);
1513
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001515 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516
reed@google.com5c3d1472011-02-22 19:12:23 +00001517 // todo: signal fClipStack that we have a region, and therefore (I guess)
1518 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001519 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001520
reed1f836ee2014-07-07 07:49:34 -07001521 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522}
1523
reed@google.com819c9212011-02-23 18:56:55 +00001524#ifdef SK_DEBUG
1525void SkCanvas::validateClip() const {
1526 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001527 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001528 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001529 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001530 return;
1531 }
1532
reed@google.com819c9212011-02-23 18:56:55 +00001533 SkIRect ir;
1534 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001535 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001536
robertphillips@google.com80214e22012-07-20 15:33:18 +00001537 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001538 const SkClipStack::Element* element;
1539 while ((element = iter.next()) != NULL) {
1540 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001541 case SkClipStack::Element::kRect_Type:
1542 element->getRect().round(&ir);
1543 tmpClip.op(ir, element->getOp());
1544 break;
1545 case SkClipStack::Element::kEmpty_Type:
1546 tmpClip.setEmpty();
1547 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001548 default: {
1549 SkPath path;
1550 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001551 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001552 break;
1553 }
reed@google.com819c9212011-02-23 18:56:55 +00001554 }
1555 }
reed@google.com819c9212011-02-23 18:56:55 +00001556}
1557#endif
1558
reed@google.com90c07ea2012-04-13 13:50:27 +00001559void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001560 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001561 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001562
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001563 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001564 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001565 }
1566}
1567
reed@google.com5c3d1472011-02-22 19:12:23 +00001568///////////////////////////////////////////////////////////////////////////////
1569
reed@google.com754de5f2014-02-24 19:38:20 +00001570bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001571 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001572}
1573
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001574bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001575 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001576}
1577
reed@google.com3b3e8952012-08-16 20:53:31 +00001578bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001579 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001580 return true;
1581
reed1f836ee2014-07-07 07:49:34 -07001582 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583 return true;
1584 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585
reed1f836ee2014-07-07 07:49:34 -07001586 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001587 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001588 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001589 SkIRect idst;
1590 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001591 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001592 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001593 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001594
reed@android.coma380ae42009-07-21 01:17:02 +00001595 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001596 // TODO: should we use | instead, or compare all 4 at once?
1597 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001598 return true;
1599 }
reed@google.comc0784db2013-12-13 21:16:12 +00001600 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001601 return true;
1602 }
1603 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605}
1606
reed@google.com3b3e8952012-08-16 20:53:31 +00001607bool SkCanvas::quickReject(const SkPath& path) const {
1608 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609}
1610
reed@google.com3b3e8952012-08-16 20:53:31 +00001611bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001612 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001613 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614 return false;
1615 }
1616
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001617 SkMatrix inverse;
1618 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001619 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001620 if (bounds) {
1621 bounds->setEmpty();
1622 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001623 return false;
1624 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625
bsalomon49f085d2014-09-05 13:34:00 -07001626 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001627 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001628 // adjust it outwards in case we are antialiasing
1629 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001630
reed@google.com8f4d2302013-12-17 16:44:46 +00001631 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1632 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633 inverse.mapRect(bounds, r);
1634 }
1635 return true;
1636}
1637
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001638bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001639 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001640 if (clip.isEmpty()) {
1641 if (bounds) {
1642 bounds->setEmpty();
1643 }
1644 return false;
1645 }
1646
bsalomon49f085d2014-09-05 13:34:00 -07001647 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001648 *bounds = clip.getBounds();
1649 }
1650 return true;
1651}
1652
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001654 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655}
1656
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001657const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001658 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001659}
1660
reed@google.com9c135db2014-03-12 18:28:35 +00001661GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1662 SkBaseDevice* dev = this->getTopDevice();
1663 return dev ? dev->accessRenderTarget() : NULL;
1664}
1665
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001666GrContext* SkCanvas::getGrContext() {
1667#if SK_SUPPORT_GPU
1668 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001669 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001670 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001671 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001672 return renderTarget->getContext();
1673 }
1674 }
1675#endif
1676
1677 return NULL;
1678
1679}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001680
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001681void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1682 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001683 TRACE_EVENT0("skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001684 if (outer.isEmpty()) {
1685 return;
1686 }
1687 if (inner.isEmpty()) {
1688 this->drawRRect(outer, paint);
1689 return;
1690 }
1691
1692 // We don't have this method (yet), but technically this is what we should
1693 // be able to assert...
1694 // SkASSERT(outer.contains(inner));
1695 //
1696 // For now at least check for containment of bounds
1697 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1698
1699 this->onDrawDRRect(outer, inner, paint);
1700}
1701
reed@android.com8a1c16f2008-12-17 15:59:43 +00001702//////////////////////////////////////////////////////////////////////////////
1703// These are the virtual drawing methods
1704//////////////////////////////////////////////////////////////////////////////
1705
reed@google.com2a981812011-04-14 18:59:28 +00001706void SkCanvas::clear(SkColor color) {
1707 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001708 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001709 while (iter.next()) {
1710 iter.fDevice->clear(color);
1711 }
1712}
1713
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001714void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001715 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001716 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1717 }
1718}
1719
reed@android.com8a1c16f2008-12-17 15:59:43 +00001720void SkCanvas::drawPaint(const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001721 TRACE_EVENT0("skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001722 this->internalDrawPaint(paint);
1723}
1724
1725void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001726 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001727
1728 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001729 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730 }
1731
reed@google.com4e2b3d32011-04-07 14:18:59 +00001732 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733}
1734
1735void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1736 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001737 TRACE_EVENT1("skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001738 if ((long)count <= 0) {
1739 return;
1740 }
1741
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001742 SkRect r, storage;
1743 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001744 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001745 // special-case 2 points (common for drawing a single line)
1746 if (2 == count) {
1747 r.set(pts[0], pts[1]);
1748 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001749 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001750 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001751 bounds = &paint.computeFastStrokeBounds(r, &storage);
1752 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001753 return;
1754 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001755 }
reed@google.coma584aed2012-05-16 14:06:02 +00001756
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757 SkASSERT(pts != NULL);
1758
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001759 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001760
reed@android.com8a1c16f2008-12-17 15:59:43 +00001761 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001762 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 }
reed@google.com4b226022011-01-11 18:32:13 +00001764
reed@google.com4e2b3d32011-04-07 14:18:59 +00001765 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766}
1767
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001768void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001769 TRACE_EVENT0("skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001770 SkRect storage;
1771 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001773 bounds = &paint.computeFastBounds(r, &storage);
1774 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 return;
1776 }
1777 }
reed@google.com4b226022011-01-11 18:32:13 +00001778
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001779 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780
1781 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001782 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 }
1784
reed@google.com4e2b3d32011-04-07 14:18:59 +00001785 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
reed@google.com4ed0fb72012-12-12 20:48:18 +00001788void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001789 TRACE_EVENT0("skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001790 SkRect storage;
1791 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001792 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001793 bounds = &paint.computeFastBounds(oval, &storage);
1794 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001795 return;
1796 }
1797 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001798
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001799 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001800
1801 while (iter.next()) {
1802 iter.fDevice->drawOval(iter, oval, looper.paint());
1803 }
1804
1805 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001806}
1807
1808void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001809 TRACE_EVENT0("skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001810 SkRect storage;
1811 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001812 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001813 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1814 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001815 return;
1816 }
1817 }
1818
1819 if (rrect.isRect()) {
1820 // call the non-virtual version
1821 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001822 return;
1823 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001824 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001825 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1826 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001827 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001828
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001829 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001830
1831 while (iter.next()) {
1832 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1833 }
1834
1835 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001836}
1837
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001838void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1839 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001840 SkRect storage;
1841 const SkRect* bounds = NULL;
1842 if (paint.canComputeFastBounds()) {
1843 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1844 if (this->quickReject(*bounds)) {
1845 return;
1846 }
1847 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001848
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001849 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001850
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001851 while (iter.next()) {
1852 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1853 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001854
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001855 LOOPER_END
1856}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001857
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001858void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08001859 TRACE_EVENT0("skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00001860 if (!path.isFinite()) {
1861 return;
1862 }
1863
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001864 SkRect storage;
1865 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001866 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001867 const SkRect& pathBounds = path.getBounds();
1868 bounds = &paint.computeFastBounds(pathBounds, &storage);
1869 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001870 return;
1871 }
1872 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001873
1874 const SkRect& r = path.getBounds();
1875 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001876 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001877 this->internalDrawPaint(paint);
1878 }
1879 return;
1880 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001881
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001882 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883
1884 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001885 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886 }
1887
reed@google.com4e2b3d32011-04-07 14:18:59 +00001888 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001889}
1890
piotaixrb5fae932014-09-24 13:03:30 -07001891void SkCanvas::drawImage(const SkImage* image, SkScalar left, SkScalar top,
1892 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001893 TRACE_EVENT0("skia", "SkCanvas::drawImage()");
piotaixrb5fae932014-09-24 13:03:30 -07001894 image->draw(this, left, top, paint);
1895}
1896
1897void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src,
1898 const SkRect& dst,
1899 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001900 TRACE_EVENT0("skia", "SkCanvas::drawImageRect()");
piotaixr5ceff912014-09-26 07:36:26 -07001901 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001902}
1903
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1905 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001906 TRACE_EVENT0("skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001907 SkDEBUGCODE(bitmap.validate();)
1908
reed@google.com3d608122011-11-21 15:16:16 +00001909 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001910 SkRect bounds = {
1911 x, y,
1912 x + SkIntToScalar(bitmap.width()),
1913 y + SkIntToScalar(bitmap.height())
1914 };
1915 if (paint) {
1916 (void)paint->computeFastBounds(bounds, &bounds);
1917 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001918 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001919 return;
1920 }
1921 }
reed@google.com4b226022011-01-11 18:32:13 +00001922
reed@android.com8a1c16f2008-12-17 15:59:43 +00001923 SkMatrix matrix;
1924 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001925 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001926}
1927
reed@google.com9987ec32011-09-07 11:57:52 +00001928// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001929void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001930 const SkRect& dst, const SkPaint* paint,
1931 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001932 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933 return;
1934 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001935
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001936 SkRect storage;
1937 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001938 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001939 if (paint) {
1940 bounds = &paint->computeFastBounds(dst, &storage);
1941 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001942 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001943 return;
1944 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001945 }
reed@google.com3d608122011-11-21 15:16:16 +00001946
reed@google.com33535f32012-09-25 15:37:50 +00001947 SkLazyPaint lazy;
1948 if (NULL == paint) {
1949 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001950 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001951
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001952 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001953
reed@google.com33535f32012-09-25 15:37:50 +00001954 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001955 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001956 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001957
reed@google.com33535f32012-09-25 15:37:50 +00001958 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001959}
1960
reed@google.com71121732012-09-18 15:14:33 +00001961void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001962 const SkRect& dst, const SkPaint* paint,
1963 DrawBitmapRectFlags flags) {
danakj8f757f52014-11-04 11:48:43 -08001964 TRACE_EVENT0("skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00001965 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001966 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001967}
1968
reed@android.com8a1c16f2008-12-17 15:59:43 +00001969void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1970 const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08001971 TRACE_EVENT0("skia", "SkCanvas::drawBitmapMatrix()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00001972 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001973 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001974}
1975
reed@google.com9987ec32011-09-07 11:57:52 +00001976void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1977 const SkIRect& center, const SkRect& dst,
1978 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001979 if (bitmap.drawsNothing()) {
1980 return;
1981 }
reed@google.com3d608122011-11-21 15:16:16 +00001982 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001983 SkRect storage;
1984 const SkRect* bounds = &dst;
1985 if (paint) {
1986 bounds = &paint->computeFastBounds(dst, &storage);
1987 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001988 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001989 return;
1990 }
1991 }
1992
reed@google.com9987ec32011-09-07 11:57:52 +00001993 const int32_t w = bitmap.width();
1994 const int32_t h = bitmap.height();
1995
1996 SkIRect c = center;
1997 // pin center to the bounds of the bitmap
1998 c.fLeft = SkMax32(0, center.fLeft);
1999 c.fTop = SkMax32(0, center.fTop);
2000 c.fRight = SkPin32(center.fRight, c.fLeft, w);
2001 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
2002
reed@google.com71121732012-09-18 15:14:33 +00002003 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002004 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00002005 };
2006 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002007 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002008 };
reed@google.com9987ec32011-09-07 11:57:52 +00002009 SkScalar dstX[4] = {
2010 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2011 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2012 };
2013 SkScalar dstY[4] = {
2014 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2015 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2016 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002017
reed@google.com9987ec32011-09-07 11:57:52 +00002018 if (dstX[1] > dstX[2]) {
2019 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2020 dstX[2] = dstX[1];
2021 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002022
reed@google.com9987ec32011-09-07 11:57:52 +00002023 if (dstY[1] > dstY[2]) {
2024 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2025 dstY[2] = dstY[1];
2026 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002027
reed@google.com9987ec32011-09-07 11:57:52 +00002028 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002029 SkRect s, d;
2030
reed@google.com9987ec32011-09-07 11:57:52 +00002031 s.fTop = srcY[y];
2032 s.fBottom = srcY[y+1];
2033 d.fTop = dstY[y];
2034 d.fBottom = dstY[y+1];
2035 for (int x = 0; x < 3; x++) {
2036 s.fLeft = srcX[x];
2037 s.fRight = srcX[x+1];
2038 d.fLeft = dstX[x];
2039 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002040 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002041 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002042 }
2043 }
2044}
2045
2046void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2047 const SkRect& dst, const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08002048 TRACE_EVENT0("skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002049 SkDEBUGCODE(bitmap.validate();)
2050
2051 // Need a device entry-point, so gpu can use a mesh
2052 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2053}
2054
reed@google.comf67e4cf2011-03-15 20:56:58 +00002055class SkDeviceFilteredPaint {
2056public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002057 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
reedb2db8982014-11-13 12:41:02 -08002058 if (device->shouldDisableLCD(paint)) {
2059 uint32_t flags = paint.getFlags();
2060 flags &= ~SkPaint::kLCDRenderText_Flag;
2061 flags |= SkPaint::kGenA8FromLCD_Flag;
reed@google.coma076e9b2011-04-06 20:17:29 +00002062 SkPaint* newPaint = fLazy.set(paint);
reedb2db8982014-11-13 12:41:02 -08002063 newPaint->setFlags(flags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002064 fPaint = newPaint;
2065 } else {
2066 fPaint = &paint;
2067 }
2068 }
2069
reed@google.comf67e4cf2011-03-15 20:56:58 +00002070 const SkPaint& paint() const { return *fPaint; }
2071
2072private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002073 const SkPaint* fPaint;
2074 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002075};
2076
bungeman@google.com52c748b2011-08-22 21:30:43 +00002077void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2078 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002079 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002080 draw.fDevice->drawRect(draw, r, paint);
2081 } else {
2082 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002083 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002084 draw.fDevice->drawRect(draw, r, p);
2085 }
2086}
2087
2088void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2089 const char text[], size_t byteLength,
2090 SkScalar x, SkScalar y) {
2091 SkASSERT(byteLength == 0 || text != NULL);
2092
2093 // nothing to draw
2094 if (text == NULL || byteLength == 0 ||
2095 draw.fClip->isEmpty() ||
2096 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2097 return;
2098 }
2099
2100 SkScalar width = 0;
2101 SkPoint start;
2102
2103 start.set(0, 0); // to avoid warning
2104 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2105 SkPaint::kStrikeThruText_Flag)) {
2106 width = paint.measureText(text, byteLength);
2107
2108 SkScalar offsetX = 0;
2109 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2110 offsetX = SkScalarHalf(width);
2111 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2112 offsetX = width;
2113 }
2114 start.set(x - offsetX, y);
2115 }
2116
2117 if (0 == width) {
2118 return;
2119 }
2120
2121 uint32_t flags = paint.getFlags();
2122
2123 if (flags & (SkPaint::kUnderlineText_Flag |
2124 SkPaint::kStrikeThruText_Flag)) {
2125 SkScalar textSize = paint.getTextSize();
2126 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2127 SkRect r;
2128
2129 r.fLeft = start.fX;
2130 r.fRight = start.fX + width;
2131
2132 if (flags & SkPaint::kUnderlineText_Flag) {
2133 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2134 start.fY);
2135 r.fTop = offset;
2136 r.fBottom = offset + height;
2137 DrawRect(draw, paint, r, textSize);
2138 }
2139 if (flags & SkPaint::kStrikeThruText_Flag) {
2140 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2141 start.fY);
2142 r.fTop = offset;
2143 r.fBottom = offset + height;
2144 DrawRect(draw, paint, r, textSize);
2145 }
2146 }
2147}
2148
reed@google.come0d9ce82014-04-23 04:00:17 +00002149void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2150 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002151 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152
2153 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002154 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002155 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002156 DrawTextDecorations(iter, dfp.paint(),
2157 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158 }
2159
reed@google.com4e2b3d32011-04-07 14:18:59 +00002160 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161}
2162
reed@google.come0d9ce82014-04-23 04:00:17 +00002163void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2164 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002165 SkPoint textOffset = SkPoint::Make(0, 0);
2166
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002167 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002168
reed@android.com8a1c16f2008-12-17 15:59:43 +00002169 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002170 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002171 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002172 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002174
reed@google.com4e2b3d32011-04-07 14:18:59 +00002175 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002176}
2177
reed@google.come0d9ce82014-04-23 04:00:17 +00002178void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2179 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002180
2181 SkPoint textOffset = SkPoint::Make(0, constY);
2182
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002183 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002184
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002186 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002187 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002188 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002189 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002190
reed@google.com4e2b3d32011-04-07 14:18:59 +00002191 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002192}
2193
reed@google.come0d9ce82014-04-23 04:00:17 +00002194void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2195 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002196 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002197
reed@android.com8a1c16f2008-12-17 15:59:43 +00002198 while (iter.next()) {
2199 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002200 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002202
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002203 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002204}
2205
fmalita00d5c2c2014-08-21 08:53:26 -07002206void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2207 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002208
fmalita19653d12014-10-16 11:53:30 -07002209 if (paint.canComputeFastBounds()) {
fmalita7ba7aa72014-08-29 09:46:36 -07002210 SkRect storage;
2211
2212 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2213 return;
2214 }
2215 }
2216
fmalitaaa1b9122014-08-28 14:32:24 -07002217 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002218
fmalitaaa1b9122014-08-28 14:32:24 -07002219 while (iter.next()) {
2220 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2221 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002222 }
2223
fmalitaaa1b9122014-08-28 14:32:24 -07002224 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002225}
2226
reed@google.come0d9ce82014-04-23 04:00:17 +00002227// These will become non-virtual, so they always call the (virtual) onDraw... method
2228void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2229 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002230 TRACE_EVENT0("skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002231 this->onDrawText(text, byteLength, x, y, paint);
2232}
2233void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2234 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002235 TRACE_EVENT0("skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002236 this->onDrawPosText(text, byteLength, pos, paint);
2237}
2238void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2239 SkScalar constY, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002240 TRACE_EVENT0("skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002241 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2242}
2243void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2244 const SkMatrix* matrix, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002245 TRACE_EVENT0("skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002246 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2247}
fmalita00d5c2c2014-08-21 08:53:26 -07002248void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2249 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002250 TRACE_EVENT0("skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002251 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002252 this->onDrawTextBlob(blob, x, y, paint);
2253 }
2254}
reed@google.come0d9ce82014-04-23 04:00:17 +00002255
reed@android.com8a1c16f2008-12-17 15:59:43 +00002256void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2257 const SkPoint verts[], const SkPoint texs[],
2258 const SkColor colors[], SkXfermode* xmode,
2259 const uint16_t indices[], int indexCount,
2260 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002261 TRACE_EVENT0("skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002262 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002263
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264 while (iter.next()) {
2265 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002266 colors, xmode, indices, indexCount,
2267 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002268 }
reed@google.com4b226022011-01-11 18:32:13 +00002269
reed@google.com4e2b3d32011-04-07 14:18:59 +00002270 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271}
2272
dandovb3c9d1c2014-08-12 08:34:29 -07002273void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2274 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002275 TRACE_EVENT0("skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002276 if (NULL == cubics) {
2277 return;
2278 }
mtklein6cfa73a2014-08-13 13:33:49 -07002279
dandovecfff212014-08-04 10:02:00 -07002280 // Since a patch is always within the convex hull of the control points, we discard it when its
2281 // bounding rectangle is completely outside the current clip.
2282 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002283 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002284 if (this->quickReject(bounds)) {
2285 return;
2286 }
mtklein6cfa73a2014-08-13 13:33:49 -07002287
dandovb3c9d1c2014-08-12 08:34:29 -07002288 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2289}
2290
2291void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2292 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2293
dandovecfff212014-08-04 10:02:00 -07002294 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002295
dandovecfff212014-08-04 10:02:00 -07002296 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002297 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002298 }
mtklein6cfa73a2014-08-13 13:33:49 -07002299
dandovecfff212014-08-04 10:02:00 -07002300 LOOPER_END
2301}
2302
reed6a070dc2014-11-11 19:36:09 -08002303void SkCanvas::EXPERIMENTAL_drawDrawable(SkCanvasDrawable* dr) {
2304 if (dr) {
2305 SkRect bounds;
2306 if (!dr->getBounds(&bounds) || !this->quickReject(bounds)) {
2307 this->onDrawDrawable(dr);
2308 }
2309 }
2310}
2311
2312void SkCanvas::onDrawDrawable(SkCanvasDrawable* dr) {
2313 dr->draw(this);
2314}
2315
reed@android.com8a1c16f2008-12-17 15:59:43 +00002316//////////////////////////////////////////////////////////////////////////////
2317// These methods are NOT virtual, and therefore must call back into virtual
2318// methods, rather than actually drawing themselves.
2319//////////////////////////////////////////////////////////////////////////////
2320
2321void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002322 SkXfermode::Mode mode) {
danakj8f757f52014-11-04 11:48:43 -08002323 TRACE_EVENT0("skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002324 SkPaint paint;
2325
2326 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002327 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002328 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329 }
2330 this->drawPaint(paint);
2331}
2332
reed@android.com845fdac2009-06-23 03:01:32 +00002333void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj8f757f52014-11-04 11:48:43 -08002334 TRACE_EVENT0("skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002335 SkPaint paint;
2336
2337 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002338 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002339 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 }
2341 this->drawPaint(paint);
2342}
2343
2344void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002345 TRACE_EVENT0("skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002346 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002347
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348 pt.set(x, y);
2349 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2350}
2351
2352void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj8f757f52014-11-04 11:48:43 -08002353 TRACE_EVENT0("skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002354 SkPoint pt;
2355 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002356
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357 pt.set(x, y);
2358 paint.setColor(color);
2359 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2360}
2361
2362void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2363 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002364 TRACE_EVENT0("skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002365 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002366
reed@android.com8a1c16f2008-12-17 15:59:43 +00002367 pts[0].set(x0, y0);
2368 pts[1].set(x1, y1);
2369 this->drawPoints(kLines_PointMode, 2, pts, paint);
2370}
2371
2372void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2373 SkScalar right, SkScalar bottom,
2374 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002375 TRACE_EVENT0("skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002376 SkRect r;
2377
2378 r.set(left, top, right, bottom);
2379 this->drawRect(r, paint);
2380}
2381
2382void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2383 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002384 TRACE_EVENT0("skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002385 if (radius < 0) {
2386 radius = 0;
2387 }
2388
2389 SkRect r;
2390 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002391 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002392}
2393
2394void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2395 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002396 TRACE_EVENT0("skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002397 if (rx > 0 && ry > 0) {
2398 if (paint.canComputeFastBounds()) {
2399 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002400 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002401 return;
2402 }
2403 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002404 SkRRect rrect;
2405 rrect.setRectXY(r, rx, ry);
2406 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002407 } else {
2408 this->drawRect(r, paint);
2409 }
2410}
2411
reed@android.com8a1c16f2008-12-17 15:59:43 +00002412void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2413 SkScalar sweepAngle, bool useCenter,
2414 const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002415 TRACE_EVENT0("skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002416 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2417 this->drawOval(oval, paint);
2418 } else {
2419 SkPath path;
2420 if (useCenter) {
2421 path.moveTo(oval.centerX(), oval.centerY());
2422 }
2423 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2424 if (useCenter) {
2425 path.close();
2426 }
2427 this->drawPath(path, paint);
2428 }
2429}
2430
2431void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2432 const SkPath& path, SkScalar hOffset,
2433 SkScalar vOffset, const SkPaint& paint) {
danakj8f757f52014-11-04 11:48:43 -08002434 TRACE_EVENT0("skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002436
reed@android.com8a1c16f2008-12-17 15:59:43 +00002437 matrix.setTranslate(hOffset, vOffset);
2438 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2439}
2440
reed@android.comf76bacf2009-05-13 14:00:33 +00002441///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002442void SkCanvas::drawPicture(const SkPicture* picture) {
danakj8f757f52014-11-04 11:48:43 -08002443 TRACE_EVENT0("skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002444 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002445 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002446 }
2447}
2448
reedd5fa1a42014-08-09 11:08:05 -07002449void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
danakj8f757f52014-11-04 11:48:43 -08002450 TRACE_EVENT0("skia", "SkCanvas::drawPicture(SkMatrix, SkPaint)");
bsalomon49f085d2014-09-05 13:34:00 -07002451 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002452 if (matrix && matrix->isIdentity()) {
2453 matrix = NULL;
2454 }
2455 this->onDrawPicture(picture, matrix, paint);
2456 }
2457}
robertphillips9b14f262014-06-04 05:40:44 -07002458
reedd5fa1a42014-08-09 11:08:05 -07002459void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2460 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002461 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002462 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002463 // Canvas has to first give the device the opportunity to render
2464 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002465 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002466 return; // the device has rendered the entire picture
2467 }
2468 }
2469
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002470 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002471
robertphillipsc5ba71d2014-09-04 08:42:50 -07002472 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002473}
2474
reed@android.com8a1c16f2008-12-17 15:59:43 +00002475///////////////////////////////////////////////////////////////////////////////
2476///////////////////////////////////////////////////////////////////////////////
2477
2478SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002479 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002480
2481 SkASSERT(canvas);
2482
2483 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2484 fDone = !fImpl->next();
2485}
2486
2487SkCanvas::LayerIter::~LayerIter() {
2488 fImpl->~SkDrawIter();
2489}
2490
2491void SkCanvas::LayerIter::next() {
2492 fDone = !fImpl->next();
2493}
2494
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002495SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002496 return fImpl->getDevice();
2497}
2498
2499const SkMatrix& SkCanvas::LayerIter::matrix() const {
2500 return fImpl->getMatrix();
2501}
2502
2503const SkPaint& SkCanvas::LayerIter::paint() const {
2504 const SkPaint* paint = fImpl->getPaint();
2505 if (NULL == paint) {
2506 paint = &fDefaultPaint;
2507 }
2508 return *paint;
2509}
2510
2511const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2512int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2513int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002514
2515///////////////////////////////////////////////////////////////////////////////
2516
fmalitac3b589a2014-06-05 12:40:07 -07002517SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002518
2519///////////////////////////////////////////////////////////////////////////////
2520
2521static bool supported_for_raster_canvas(const SkImageInfo& info) {
2522 switch (info.alphaType()) {
2523 case kPremul_SkAlphaType:
2524 case kOpaque_SkAlphaType:
2525 break;
2526 default:
2527 return false;
2528 }
2529
2530 switch (info.colorType()) {
2531 case kAlpha_8_SkColorType:
2532 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002533 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002534 break;
2535 default:
2536 return false;
2537 }
2538
2539 return true;
2540}
2541
2542SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2543 if (!supported_for_raster_canvas(info)) {
2544 return NULL;
2545 }
2546
2547 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -07002548 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002549 return NULL;
2550 }
2551
2552 // should this functionality be moved into allocPixels()?
2553 if (!bitmap.info().isOpaque()) {
2554 bitmap.eraseColor(0);
2555 }
2556 return SkNEW_ARGS(SkCanvas, (bitmap));
2557}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002558
2559SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2560 if (!supported_for_raster_canvas(info)) {
2561 return NULL;
2562 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002563
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002564 SkBitmap bitmap;
2565 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2566 return NULL;
2567 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002568 return SkNEW_ARGS(SkCanvas, (bitmap));
2569}
reedd5fa1a42014-08-09 11:08:05 -07002570
2571///////////////////////////////////////////////////////////////////////////////
2572
2573SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002574 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002575 : fCanvas(canvas)
2576 , fSaveCount(canvas->getSaveCount())
2577{
bsalomon49f085d2014-09-05 13:34:00 -07002578 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002579 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002580 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002581 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002582 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002583 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002584 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002585 canvas->save();
2586 }
mtklein6cfa73a2014-08-13 13:33:49 -07002587
bsalomon49f085d2014-09-05 13:34:00 -07002588 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002589 canvas->concat(*matrix);
2590 }
2591}
2592
2593SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2594 fCanvas->restoreToCount(fSaveCount);
2595}