blob: dcc7047dbcee58ecf56d9d914a09b109afa3baeb [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"
reedd5fa1a42014-08-09 11:08:05 -07009#include "SkCanvasPriv.h"
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000010#include "SkBitmapDevice.h"
senorblanco@chromium.org9c397442012-09-27 21:57:45 +000011#include "SkDeviceImageFilterProxy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkDraw.h"
13#include "SkDrawFilter.h"
14#include "SkDrawLooper.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000015#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000016#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070017#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000018#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000019#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000020#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000021#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000022#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000023#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070024#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000025#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000026#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000029#if SK_SUPPORT_GPU
30#include "GrRenderTarget.h"
31#endif
32
reed@google.comda17f752012-08-16 18:27:05 +000033// experimental for faster tiled drawing...
34//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000035
reed@android.com8a1c16f2008-12-17 15:59:43 +000036//#define SK_TRACE_SAVERESTORE
37
38#ifdef SK_TRACE_SAVERESTORE
39 static int gLayerCounter;
40 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
41 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
42
43 static int gRecCounter;
44 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
45 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
46
47 static int gCanvasCounter;
48 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
49 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
50#else
51 #define inc_layer()
52 #define dec_layer()
53 #define inc_rec()
54 #define dec_rec()
55 #define inc_canvas()
56 #define dec_canvas()
57#endif
58
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000059typedef SkTLazy<SkPaint> SkLazyPaint;
60
reed@google.com97af1a62012-08-28 12:19:02 +000061void SkCanvas::predrawNotify() {
62 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000063 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000064 }
65}
66
reed@android.com8a1c16f2008-12-17 15:59:43 +000067///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000068
reed3716fd02014-09-21 09:39:55 -070069static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
70 const uint32_t propFlags = props.flags();
71 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
72 flags &= ~SkPaint::kDither_Flag;
73 }
74 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
75 flags &= ~SkPaint::kAntiAlias_Flag;
76 }
77 return flags;
78}
79
80///////////////////////////////////////////////////////////////////////////////
81
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000082/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000083 The clip/matrix/proc are fields that reflect the top of the save/restore
84 stack. Whenever the canvas changes, it marks a dirty flag, and then before
85 these are used (assuming we're not on a layer) we rebuild these cache
86 values: they reflect the top of the save stack, but translated and clipped
87 by the device's XY offset and bitmap-bounds.
88*/
89struct DeviceCM {
90 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000091 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000092 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000093 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000094 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000095
reedd9544982014-09-09 18:46:22 -070096 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
97 bool conservativeRasterClip)
98 : fNext(NULL)
99 , fClip(conservativeRasterClip)
100 {
101 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000103 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 }
reed@google.com4b226022011-01-11 18:32:13 +0000105 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000107 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000109 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700110 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000111 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000112 fDevice->unref();
113 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000114 SkDELETE(fPaint);
115 }
reed@google.com4b226022011-01-11 18:32:13 +0000116
reed@google.com045e62d2011-10-24 12:19:46 +0000117 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
118 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000119 int x = fDevice->getOrigin().x();
120 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 int width = fDevice->width();
122 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000123
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124 if ((x | y) == 0) {
125 fMatrix = &totalMatrix;
126 fClip = totalClip;
127 } else {
128 fMatrixStorage = totalMatrix;
129 fMatrixStorage.postTranslate(SkIntToScalar(-x),
130 SkIntToScalar(-y));
131 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000132
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 totalClip.translate(-x, -y, &fClip);
134 }
135
reed@google.com045e62d2011-10-24 12:19:46 +0000136 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137
138 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000139
reed@android.com8a1c16f2008-12-17 15:59:43 +0000140 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000141 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 SkRegion::kDifference_Op);
143 }
reed@google.com4b226022011-01-11 18:32:13 +0000144
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000145 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
146
reed@android.com8a1c16f2008-12-17 15:59:43 +0000147#ifdef SK_DEBUG
148 if (!fClip.isEmpty()) {
149 SkIRect deviceR;
150 deviceR.set(0, 0, width, height);
151 SkASSERT(deviceR.contains(fClip.getBounds()));
152 }
153#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000154 }
155
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000157 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158};
159
160/* This is the record we keep for each save/restore level in the stack.
161 Since a level optionally copies the matrix and/or stack, we have pointers
162 for these fields. If the value is copied for this level, the copy is
163 stored in the ...Storage field, and the pointer points to that. If the
164 value is not copied for this level, we ignore ...Storage, and just point
165 at the corresponding value in the previous level in the stack.
166*/
167class SkCanvas::MCRec {
168public:
reed6f097092014-09-09 12:51:10 -0700169 SkRasterClip fRasterClip;
reedd9544982014-09-09 18:46:22 -0700170 SkMatrix fMatrix;
reed1f836ee2014-07-07 07:49:34 -0700171 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700172 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173 /* If there are any layers in the stack, this points to the top-most
174 one that is at or below this level in the stack (so we know what
175 bitmap/device to draw into from this level. This value is NOT
176 reference counted, since the real owner is either our fLayer field,
177 or a previous one in a lower level.)
178 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000179 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180
reedd9544982014-09-09 18:46:22 -0700181 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
182 fMatrix.reset();
183 fFilter = NULL;
184 fLayer = NULL;
185 fTopLayer = NULL;
186
187 // don't bother initializing fNext
188 inc_rec();
189 }
190 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip) {
191 fMatrix = prev.fMatrix;
192 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700194 fTopLayer = prev.fTopLayer;
195
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 // don't bother initializing fNext
197 inc_rec();
198 }
199 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000200 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 SkDELETE(fLayer);
202 dec_rec();
203 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204};
205
206class SkDrawIter : public SkDraw {
207public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000208 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000209 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000210 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211 canvas->updateDeviceCMCache();
212
reed@google.com90c07ea2012-04-13 13:50:27 +0000213 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000214 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000215 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 }
reed@google.com4b226022011-01-11 18:32:13 +0000217
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 bool next() {
219 // skip over recs with empty clips
220 if (fSkipEmptyClips) {
221 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
222 fCurrLayer = fCurrLayer->fNext;
223 }
224 }
225
reed@google.comf68c5e22012-02-24 16:38:58 +0000226 const DeviceCM* rec = fCurrLayer;
227 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228
229 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000230 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
231 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 fDevice = rec->fDevice;
233 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000235 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000236
237 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 return true;
241 }
242 return false;
243 }
reed@google.com4b226022011-01-11 18:32:13 +0000244
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000245 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000246 int getX() const { return fDevice->getOrigin().x(); }
247 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 const SkMatrix& getMatrix() const { return *fMatrix; }
249 const SkRegion& getClip() const { return *fClip; }
250 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000251
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252private:
253 SkCanvas* fCanvas;
254 const DeviceCM* fCurrLayer;
255 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 SkBool8 fSkipEmptyClips;
257
258 typedef SkDraw INHERITED;
259};
260
261/////////////////////////////////////////////////////////////////////////////
262
263class AutoDrawLooper {
264public:
reed3716fd02014-09-21 09:39:55 -0700265 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000266 bool skipLayerForImageFilter = false,
267 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000268 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 fFilter = canvas->getDrawFilter();
reed3716fd02014-09-21 09:39:55 -0700270 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000271 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000272 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000273 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274
reed@google.com8926b162012-03-23 15:36:36 +0000275 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
276 SkPaint tmp;
277 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000278 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
279 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000280 // we'll clear the imageFilter for the actual draws in next(), so
281 // it will only be applied during the restore().
282 fDoClearImageFilter = true;
283 }
284
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000285 if (SkDrawLooper* looper = paint.getLooper()) {
286 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
287 looper->contextSize());
288 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000289 fIsSimple = false;
290 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000291 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000292 // can we be marked as simple?
293 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000294 }
reed3716fd02014-09-21 09:39:55 -0700295
296 uint32_t oldFlags = paint.getFlags();
297 fNewPaintFlags = filter_paint_flags(props, oldFlags);
298 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
299 SkPaint* paint = fLazyPaint.set(fOrigPaint);
300 paint->setFlags(fNewPaintFlags);
301 fPaint = paint;
302 // if we're not simple, doNext() will take care of calling setFlags()
303 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000304 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000305
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000307 if (fDoClearImageFilter) {
308 fCanvas->internalRestore();
309 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000310 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000311 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000312
reed@google.com4e2b3d32011-04-07 14:18:59 +0000313 const SkPaint& paint() const {
314 SkASSERT(fPaint);
315 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000316 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000317
reed@google.com129ec222012-05-15 13:24:09 +0000318 bool next(SkDrawFilter::Type drawType) {
319 if (fDone) {
320 return false;
321 } else if (fIsSimple) {
322 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000323 return !fPaint->nothingToDraw();
324 } else {
325 return this->doNext(drawType);
326 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000327 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000328
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000330 SkLazyPaint fLazyPaint;
331 SkCanvas* fCanvas;
332 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000333 SkDrawFilter* fFilter;
334 const SkPaint* fPaint;
335 int fSaveCount;
reed3716fd02014-09-21 09:39:55 -0700336 uint32_t fNewPaintFlags;
reed@google.com8926b162012-03-23 15:36:36 +0000337 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000338 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000339 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000340 SkDrawLooper::Context* fLooperContext;
341 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000342
343 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344};
345
reed@google.com129ec222012-05-15 13:24:09 +0000346bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000347 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000348 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000349 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000350
351 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed3716fd02014-09-21 09:39:55 -0700352 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000353
354 if (fDoClearImageFilter) {
355 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000356 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000357
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000358 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000359 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000360 return false;
361 }
362 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000363 if (!fFilter->filter(paint, drawType)) {
364 fDone = true;
365 return false;
366 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000367 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000368 // no looper means we only draw once
369 fDone = true;
370 }
371 }
372 fPaint = paint;
373
374 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000375 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000376 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000377 }
378
379 // call this after any possible paint modifiers
380 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000381 fPaint = NULL;
382 return false;
383 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000384 return true;
385}
386
reed@android.com8a1c16f2008-12-17 15:59:43 +0000387////////// macros to place around the internal draw calls //////////////////
388
reed@google.com8926b162012-03-23 15:36:36 +0000389#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000390 this->predrawNotify(); \
reed3716fd02014-09-21 09:39:55 -0700391 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000392 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000393 SkDrawIter iter(this);
394
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000395#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000396 this->predrawNotify(); \
reed3716fd02014-09-21 09:39:55 -0700397 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000398 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000399 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000400
reed@google.com4e2b3d32011-04-07 14:18:59 +0000401#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000402
403////////////////////////////////////////////////////////////////////////////
404
reed3716fd02014-09-21 09:39:55 -0700405void SkCanvas::setupDevice(SkBaseDevice* device) {
406 device->setPixelGeometry(fProps.pixelGeometry());
407}
408
reedd9544982014-09-09 18:46:22 -0700409SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
410 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000411 fCachedLocalClipBounds.setEmpty();
412 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000413 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000414 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700415 fDeviceCMDirty = true;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000416 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000417 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000418 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419
420 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700421 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422
reedd9544982014-09-09 18:46:22 -0700423 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425
reed@google.com97af1a62012-08-28 12:19:02 +0000426 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000427
reedf92c8662014-08-18 08:02:43 -0700428 if (device) {
reed3716fd02014-09-21 09:39:55 -0700429 this->setupDevice(device);
430 if (device->forceConservativeRasterClip()) {
431 fConservativeRasterClip = true;
432 }
reedf92c8662014-08-18 08:02:43 -0700433 device->onAttachToCanvas(this);
434 fMCRec->fLayer->fDevice = SkRef(device);
435 fMCRec->fRasterClip.setRect(SkIRect::MakeWH(device->width(), device->height()));
436 }
437 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000438}
439
reed@google.comcde92112011-07-06 20:00:52 +0000440SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000441 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed3716fd02014-09-21 09:39:55 -0700442 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000443{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000444 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000445
reedd9544982014-09-09 18:46:22 -0700446 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000447}
448
reedd9544982014-09-09 18:46:22 -0700449static SkBitmap make_nopixels(int width, int height) {
450 SkBitmap bitmap;
451 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
452 return bitmap;
453}
454
455class SkNoPixelsBitmapDevice : public SkBitmapDevice {
456public:
457 SkNoPixelsBitmapDevice(int width, int height) : INHERITED(make_nopixels(width, height)) {}
458
459private:
460
461 typedef SkBitmapDevice INHERITED;
462};
463
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000464SkCanvas::SkCanvas(int width, int height)
465 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed3716fd02014-09-21 09:39:55 -0700466 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000467{
468 inc_canvas();
reedd9544982014-09-09 18:46:22 -0700469
470 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), kDefault_InitFlags)->unref();
471}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000472
reedd9544982014-09-09 18:46:22 -0700473SkCanvas::SkCanvas(int width, int height, InitFlags flags)
474 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed3716fd02014-09-21 09:39:55 -0700475 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700476{
477 inc_canvas();
478
479 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), flags)->unref();
480}
481
reed3716fd02014-09-21 09:39:55 -0700482SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700483 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed3716fd02014-09-21 09:39:55 -0700484 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700485{
486 inc_canvas();
487
488 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000489}
490
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000491SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000492 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed3716fd02014-09-21 09:39:55 -0700493 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000494{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000495 inc_canvas();
reedd9544982014-09-09 18:46:22 -0700496
497 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498}
499
reed3716fd02014-09-21 09:39:55 -0700500SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000501 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed3716fd02014-09-21 09:39:55 -0700502 , fProps(props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000503{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000504 inc_canvas();
reed3716fd02014-09-21 09:39:55 -0700505
506 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
507 this->init(device, kDefault_InitFlags);
508}
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509
reed3716fd02014-09-21 09:39:55 -0700510SkCanvas::SkCanvas(const SkBitmap& bitmap)
511 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
512 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
513{
514 inc_canvas();
515
516 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
517 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000518}
519
520SkCanvas::~SkCanvas() {
521 // free up the contents of our deque
522 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000523 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000524
reed@android.com8a1c16f2008-12-17 15:59:43 +0000525 this->internalRestore(); // restore the last, since we're going away
526
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000527 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000528
reed@android.com8a1c16f2008-12-17 15:59:43 +0000529 dec_canvas();
530}
531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532SkDrawFilter* SkCanvas::getDrawFilter() const {
533 return fMCRec->fFilter;
534}
535
536SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
537 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
538 return filter;
539}
540
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000541SkMetaData& SkCanvas::getMetaData() {
542 // metadata users are rare, so we lazily allocate it. If that changes we
543 // can decide to just make it a field in the device (rather than a ptr)
544 if (NULL == fMetaData) {
545 fMetaData = new SkMetaData;
546 }
547 return *fMetaData;
548}
549
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550///////////////////////////////////////////////////////////////////////////////
551
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000552void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000553 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000554 if (device) {
555 device->flush();
556 }
557}
558
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000559SkISize SkCanvas::getTopLayerSize() const {
560 SkBaseDevice* d = this->getTopDevice();
561 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
562}
563
564SkIPoint SkCanvas::getTopLayerOrigin() const {
565 SkBaseDevice* d = this->getTopDevice();
566 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
567}
568
569SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000570 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000571 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
572}
573
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000574SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000575 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000576 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000577 SkASSERT(rec && rec->fLayer);
578 return rec->fLayer->fDevice;
579}
580
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000581SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000582 if (updateMatrixClip) {
583 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
584 }
reed@google.com9266fed2011-03-30 00:18:03 +0000585 return fMCRec->fTopLayer->fDevice;
586}
587
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000588SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000589 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000590 SkDeque::F2BIter iter(fMCStack);
591 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000593 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594
595 if (rootDevice == device) {
596 return device;
597 }
reed@google.com4b226022011-01-11 18:32:13 +0000598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000600 device->onAttachToCanvas(this);
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;
reed3716fd02014-09-21 09:39:55 -0700608 this->setupDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609
610 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000611
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 /* Now we update our initial region to have the bounds of the new device,
613 and then intersect all of the clips in our stack with these bounds,
614 to ensure that we can't draw outside of the device's bounds (and trash
615 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000616
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617 NOTE: this is only a partial-fix, since if the new device is larger than
618 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000619 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
621 reconstruct the correct clips, so this approximation will have to do.
622 The caller really needs to restore() back to the base if they want to
623 accurately take advantage of the new device bounds.
624 */
625
reed@google.com42aea282012-03-28 16:19:15 +0000626 SkIRect bounds;
627 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000628 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000629 } else {
630 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631 }
reed@google.com42aea282012-03-28 16:19:15 +0000632 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700633 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000634 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700635 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000636 }
637
reed@android.com8a1c16f2008-12-17 15:59:43 +0000638 return device;
639}
640
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000641bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
642 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
643 return false;
644 }
645
646 bool weAllocated = false;
647 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700648 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000649 return false;
650 }
651 weAllocated = true;
652 }
653
654 SkBitmap bm(*bitmap);
655 bm.lockPixels();
656 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
657 return true;
658 }
659
660 if (weAllocated) {
661 bitmap->setPixelRef(NULL);
662 }
663 return false;
664}
reed@google.com51df9e32010-12-23 19:29:18 +0000665
bsalomon@google.comc6980972011-11-02 19:57:21 +0000666bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000667 SkIRect r = srcRect;
668 const SkISize size = this->getBaseLayerSize();
669 if (!r.intersect(0, 0, size.width(), size.height())) {
670 bitmap->reset();
671 return false;
672 }
673
reed84825042014-09-02 12:50:45 -0700674 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000675 // bitmap will already be reset.
676 return false;
677 }
678 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
679 bitmap->reset();
680 return false;
681 }
682 return true;
683}
684
685bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
686 switch (origInfo.colorType()) {
687 case kUnknown_SkColorType:
688 case kIndex_8_SkColorType:
689 return false;
690 default:
691 break;
692 }
693 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
694 return false;
695 }
696 if (0 == origInfo.width() || 0 == origInfo.height()) {
697 return false;
698 }
699
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000700 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000701 if (!device) {
702 return false;
703 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000704
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000705 const SkISize size = this->getBaseLayerSize();
706 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
707 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000708 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000709 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000710
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000711 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700712 const SkImageInfo info = origInfo.makeWH(srcR.width(), srcR.height());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000713
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000714 // if x or y are negative, then we have to adjust pixels
715 if (x > 0) {
716 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000717 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000718 if (y > 0) {
719 y = 0;
720 }
721 // here x,y are either 0 or negative
722 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000723
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000724 // The device can assert that the requested area is always contained in its bounds
725 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000726}
727
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000728bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
729 if (bitmap.getTexture()) {
730 return false;
731 }
732 SkBitmap bm(bitmap);
733 bm.lockPixels();
734 if (bm.getPixels()) {
735 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
736 }
737 return false;
738}
739
740bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
741 int x, int y) {
742 switch (origInfo.colorType()) {
743 case kUnknown_SkColorType:
744 case kIndex_8_SkColorType:
745 return false;
746 default:
747 break;
748 }
749 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
750 return false;
751 }
752
753 const SkISize size = this->getBaseLayerSize();
754 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
755 if (!target.intersect(0, 0, size.width(), size.height())) {
756 return false;
757 }
758
759 SkBaseDevice* device = this->getDevice();
760 if (!device) {
761 return false;
762 }
763
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000764 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700765 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000766
767 // if x or y are negative, then we have to adjust pixels
768 if (x > 0) {
769 x = 0;
770 }
771 if (y > 0) {
772 y = 0;
773 }
774 // here x,y are either 0 or negative
775 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
776
reed4af35f32014-06-27 17:47:49 -0700777 // Tell our owning surface to bump its generation ID
778 this->predrawNotify();
779
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000780 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000781 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000782}
reed@google.com51df9e32010-12-23 19:29:18 +0000783
junov@google.com4370aed2012-01-18 16:21:08 +0000784SkCanvas* SkCanvas::canvasForDrawIter() {
785 return this;
786}
787
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788//////////////////////////////////////////////////////////////////////////////
789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790void SkCanvas::updateDeviceCMCache() {
791 if (fDeviceCMDirty) {
792 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700793 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000795
reed@android.com8a1c16f2008-12-17 15:59:43 +0000796 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000797 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000799 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000801 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802 } while ((layer = layer->fNext) != NULL);
803 }
804 fDeviceCMDirty = false;
805 }
806}
807
reed@android.com8a1c16f2008-12-17 15:59:43 +0000808///////////////////////////////////////////////////////////////////////////////
809
Florin Malita5f6102d2014-06-30 10:13:28 -0400810int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000811 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000812
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700814 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000816
Florin Malita5f6102d2014-06-30 10:13:28 -0400817 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000818
reed@android.com8a1c16f2008-12-17 15:59:43 +0000819 return saveCount;
820}
821
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000822int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400823 this->willSave();
824 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000825}
826
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000828#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000829 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000830#else
831 return true;
832#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000833}
834
junov@chromium.orga907ac32012-02-24 21:54:07 +0000835bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000836 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000837 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000838 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000839 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000840 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000841 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000842
843 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700844 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000845 // Filters may grow the bounds beyond the device bounds.
846 op = SkRegion::kReplace_Op;
847 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000848 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700849 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000850 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000851
reed@android.com8a1c16f2008-12-17 15:59:43 +0000852 this->getTotalMatrix().mapRect(&r, *bounds);
853 r.roundOut(&ir);
854 // early exit if the layer's bounds are clipped out
855 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000856 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700857 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000858 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000859 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000860 }
861 } else { // no user bounds, so just use the clip
862 ir = clipBounds;
863 }
864
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000865 if (bounds_affects_clip(flags)) {
866 fClipStack.clipDevRect(ir, op);
867 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700868 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000869 return false;
870 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000871 }
872
873 if (intersection) {
874 *intersection = ir;
875 }
876 return true;
877}
878
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000879int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
880 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
881 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
882}
883
junov@chromium.orga907ac32012-02-24 21:54:07 +0000884int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
885 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000886 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
887 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000888}
889
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000890int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
891 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000892#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000893 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000894#endif
895
junov@chromium.orga907ac32012-02-24 21:54:07 +0000896 // do this before we create the layer. We don't call the public save() since
897 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400898 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000899
900 fDeviceCMDirty = true;
901
902 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000903 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000904 return count;
905 }
906
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000907 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
908 // the clipRectBounds() call above?
909 if (kNoLayer_SaveLayerStrategy == strategy) {
910 return count;
911 }
912
reed@google.comb55deeb2012-01-06 14:43:09 +0000913 // Kill the imagefilter if our device doesn't allow it
914 SkLazyPaint lazyP;
915 if (paint && paint->getImageFilter()) {
916 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000917 if (justForImageFilter) {
918 // early exit if the layer was just for the imageFilter
919 return count;
920 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000921 SkPaint* p = lazyP.set(*paint);
922 p->setImageFilter(NULL);
923 paint = p;
924 }
925 }
926
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000927 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
928 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
929 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000931 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000932 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700933 device = this->getDevice();
934 if (device) {
935 device = device->createCompatibleDevice(info);
936 }
reed@google.com76dd2772012-01-05 21:15:07 +0000937 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000938 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000939 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000940 if (NULL == device) {
941 SkDebugf("Unable to create device for layer.");
942 return count;
943 }
reed3716fd02014-09-21 09:39:55 -0700944 this->setupDevice(device);
bsalomon@google.come97f0852011-06-17 13:10:25 +0000945
reed@google.com6f8f2922011-03-04 22:27:10 +0000946 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700947 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
948 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000949 device->unref();
950
951 layer->fNext = fMCRec->fTopLayer;
952 fMCRec->fLayer = layer;
953 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
954
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000955 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000956 return count;
957}
958
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000959int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
960 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
961}
962
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
964 SaveFlags flags) {
965 if (0xFF == alpha) {
966 return this->saveLayer(bounds, NULL, flags);
967 } else {
968 SkPaint tmpPaint;
969 tmpPaint.setAlpha(alpha);
970 return this->saveLayer(bounds, &tmpPaint, flags);
971 }
972}
973
974void SkCanvas::restore() {
975 // check for underflow
976 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000977 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700979 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 }
981}
982
983void SkCanvas::internalRestore() {
984 SkASSERT(fMCStack.count() != 0);
985
986 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000987 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988
Florin Malita5f6102d2014-06-30 10:13:28 -0400989 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000990
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000991 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000992 DeviceCM* layer = fMCRec->fLayer; // may be null
993 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
994 fMCRec->fLayer = NULL;
995
996 // now do the normal restore()
997 fMCRec->~MCRec(); // balanced in save()
998 fMCStack.pop_back();
999 fMCRec = (MCRec*)fMCStack.back();
1000
1001 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1002 since if we're being recorded, we don't want to record this (the
1003 recorder will have already recorded the restore).
1004 */
bsalomon49f085d2014-09-05 13:34:00 -07001005 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001006 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001007 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001008 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1009 layer->fPaint);
1010 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001012
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001013 SkASSERT(fSaveLayerCount > 0);
1014 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001015 }
1016 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001017 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001018}
1019
1020int SkCanvas::getSaveCount() const {
1021 return fMCStack.count();
1022}
1023
1024void SkCanvas::restoreToCount(int count) {
1025 // sanity check
1026 if (count < 1) {
1027 count = 1;
1028 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001029
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001030 int n = this->getSaveCount() - count;
1031 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001032 this->restore();
1033 }
1034}
1035
reed@google.com7c202932011-12-14 18:48:05 +00001036bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001037 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001038}
1039
reed3716fd02014-09-21 09:39:55 -07001040SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1041 if (NULL == props) {
1042 props = &fProps;
1043 }
1044 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001045}
1046
reed3716fd02014-09-21 09:39:55 -07001047SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001048 SkBaseDevice* dev = this->getDevice();
reed3716fd02014-09-21 09:39:55 -07001049 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001050}
1051
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001052SkImageInfo SkCanvas::imageInfo() const {
1053 SkBaseDevice* dev = this->getDevice();
1054 if (dev) {
1055 return dev->imageInfo();
1056 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001057 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001058 }
1059}
1060
1061const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1062 return this->onPeekPixels(info, rowBytes);
1063}
1064
1065const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1066 SkBaseDevice* dev = this->getDevice();
1067 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1068}
1069
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001070void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1071 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1072 if (pixels && origin) {
1073 *origin = this->getTopDevice(false)->getOrigin();
1074 }
1075 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001076}
1077
1078void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1079 SkBaseDevice* dev = this->getTopDevice();
1080 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1081}
1082
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001083SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1084 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1085 if (NULL == fAddr) {
1086 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001087 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001088 return; // failure, fAddr is NULL
1089 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001090 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1091 return; // failure, fAddr is NULL
1092 }
1093 fAddr = fBitmap.getPixels();
1094 fRowBytes = fBitmap.rowBytes();
1095 }
1096 SkASSERT(fAddr); // success
1097}
1098
1099bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1100 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001101 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001102 } else {
1103 bitmap->reset();
1104 return false;
1105 }
1106}
1107
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001108void SkCanvas::onPushCull(const SkRect& cullRect) {
1109 // do nothing. Subclasses may do something
1110}
1111
1112void SkCanvas::onPopCull() {
1113 // do nothing. Subclasses may do something
1114}
1115
reed@android.com8a1c16f2008-12-17 15:59:43 +00001116/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001117#ifdef SK_DEBUG
1118// Ensure that cull rects are monotonically nested in device space.
1119void SkCanvas::validateCull(const SkIRect& devCull) {
1120 if (fCullStack.isEmpty()
1121 || devCull.isEmpty()
1122 || fCullStack.top().contains(devCull)) {
1123 return;
1124 }
1125
1126 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1127 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1128 fCullStack.top().x(), fCullStack.top().y(),
1129 fCullStack.top().right(), fCullStack.top().bottom()));
1130
1131#ifdef ASSERT_NESTED_CULLING
1132 SkDEBUGFAIL("Invalid cull.");
1133#endif
1134}
1135#endif
1136
1137void SkCanvas::pushCull(const SkRect& cullRect) {
1138 ++fCullCount;
1139 this->onPushCull(cullRect);
1140
1141#ifdef SK_DEBUG
1142 // Map the cull rect into device space.
1143 SkRect mappedCull;
1144 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1145
1146 // Take clipping into account.
1147 SkIRect devClip, devCull;
1148 mappedCull.roundOut(&devCull);
1149 this->getClipDeviceBounds(&devClip);
1150 if (!devCull.intersect(devClip)) {
1151 devCull.setEmpty();
1152 }
1153
1154 this->validateCull(devCull);
1155 fCullStack.push(devCull); // balanced in popCull
1156#endif
1157}
1158
1159void SkCanvas::popCull() {
1160 SkASSERT(fCullStack.count() == fCullCount);
1161
1162 if (fCullCount > 0) {
1163 --fCullCount;
1164 this->onPopCull();
1165
1166 SkDEBUGCODE(fCullStack.pop());
1167 }
1168}
1169
1170/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001172void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001173 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001174 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001175 return;
1176 }
1177
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001178 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001179 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001180 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001181 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001182
1183 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001184
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001185 SkRect storage;
1186 const SkRect* bounds = NULL;
1187 if (paint && paint->canComputeFastBounds()) {
1188 bitmap.getBounds(&storage);
1189 matrix.mapRect(&storage);
1190 bounds = &paint->computeFastBounds(storage, &storage);
1191 }
1192
1193 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001194
1195 while (iter.next()) {
1196 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1197 }
1198
1199 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001200}
1201
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001202void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001203 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001204 SkPaint tmp;
1205 if (NULL == paint) {
1206 tmp.setDither(true);
1207 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) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001241 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001242 return;
1243 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001244 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001245
reed@google.com8926b162012-03-23 15:36:36 +00001246 SkPaint tmp;
1247 if (NULL == paint) {
1248 paint = &tmp;
1249 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001250
reed@google.com8926b162012-03-23 15:36:36 +00001251 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001252
reed@google.com8926b162012-03-23 15:36:36 +00001253 while (iter.next()) {
1254 paint = &looper.paint();
1255 SkImageFilter* filter = paint->getImageFilter();
1256 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1257 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001258 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001259 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001260 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001261 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001262 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001263 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001264 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001265 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001266 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001267 SkPaint tmpUnfiltered(*paint);
1268 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001269 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001270 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001271 }
1272 } else {
1273 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1274 }
1275 }
1276 LOOPER_END
1277}
1278
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001280void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001281 SkMatrix m;
1282 m.setTranslate(dx, dy);
1283 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284}
1285
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001286void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001287 SkMatrix m;
1288 m.setScale(sx, sy);
1289 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290}
1291
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001292void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001293 SkMatrix m;
1294 m.setRotate(degrees);
1295 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296}
1297
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001298void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001299 SkMatrix m;
1300 m.setSkew(sx, sy);
1301 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001302}
1303
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001304void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001305 if (matrix.isIdentity()) {
1306 return;
1307 }
1308
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001310 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001311 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001312
1313 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001314}
1315
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316void SkCanvas::setMatrix(const SkMatrix& matrix) {
1317 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001318 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001319 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001320 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321}
1322
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323void SkCanvas::resetMatrix() {
1324 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001325
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 matrix.reset();
1327 this->setMatrix(matrix);
1328}
1329
1330//////////////////////////////////////////////////////////////////////////////
1331
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001332void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001333 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1334 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001335}
1336
1337void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001338#ifdef SK_ENABLE_CLIP_QUICKREJECT
1339 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001340 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001341 return false;
1342 }
1343
reed@google.com3b3e8952012-08-16 20:53:31 +00001344 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001345 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001346 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001347
1348 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001349 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001350 }
1351 }
1352#endif
1353
reed@google.com5c3d1472011-02-22 19:12:23 +00001354 AutoValidateClip avc(this);
1355
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001357 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001358 if (!fAllowSoftClip) {
1359 edgeStyle = kHard_ClipEdgeStyle;
1360 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361
reed1f836ee2014-07-07 07:49:34 -07001362 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001363 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001364 // the matrix. This means we don't have to a) make a path, and b) tell
1365 // the region code to scan-convert the path, only to discover that it
1366 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368
reed1f836ee2014-07-07 07:49:34 -07001369 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001370 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001371 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001373 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001374 // and clip against that, since it can handle any matrix. However, to
1375 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1376 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 SkPath path;
1378
1379 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381 }
1382}
1383
reed73e714e2014-09-04 09:02:23 -07001384static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1385 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001386 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001387}
1388
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001389void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001390 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001391 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001392 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1393 } else {
1394 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001395 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001396}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001397
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001399 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001400 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001401 AutoValidateClip avc(this);
1402
1403 fDeviceCMDirty = true;
1404 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001405 if (!fAllowSoftClip) {
1406 edgeStyle = kHard_ClipEdgeStyle;
1407 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001408
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001409 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001410
1411 SkPath devPath;
1412 devPath.addRRect(transformedRRect);
1413
reed73e714e2014-09-04 09:02:23 -07001414 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001415 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001416 }
1417
1418 SkPath path;
1419 path.addRRect(rrect);
1420 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001421 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001422}
1423
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001424void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001425 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1426 SkRect r;
1427 if (!path.isInverseFillType() && path.isRect(&r)) {
1428 this->onClipRect(r, op, edgeStyle);
1429 } else {
1430 this->onClipPath(path, op, edgeStyle);
1431 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001432}
1433
1434void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001435#ifdef SK_ENABLE_CLIP_QUICKREJECT
1436 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001437 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001438 return false;
1439 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001440
reed@google.com3b3e8952012-08-16 20:53:31 +00001441 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001442 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001443 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001444
reed@google.comda17f752012-08-16 18:27:05 +00001445 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001446 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001447 }
1448 }
1449#endif
1450
reed@google.com5c3d1472011-02-22 19:12:23 +00001451 AutoValidateClip avc(this);
1452
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001454 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001455 if (!fAllowSoftClip) {
1456 edgeStyle = kHard_ClipEdgeStyle;
1457 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458
1459 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001460 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461
reed@google.comfe701122011-11-08 19:41:23 +00001462 // Check if the transfomation, or the original path itself
1463 // made us empty. Note this can also happen if we contained NaN
1464 // values. computing the bounds detects this, and will set our
1465 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1466 if (devPath.getBounds().isEmpty()) {
1467 // resetting the path will remove any NaN or other wanky values
1468 // that might upset our scan converter.
1469 devPath.reset();
1470 }
1471
reed@google.com5c3d1472011-02-22 19:12:23 +00001472 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001473 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001474
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001475 if (fAllowSimplifyClip) {
1476 devPath.reset();
1477 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1478 const SkClipStack* clipStack = getClipStack();
1479 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1480 const SkClipStack::Element* element;
1481 while ((element = iter.next())) {
1482 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001483 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001484 if (type != SkClipStack::Element::kEmpty_Type) {
1485 element->asPath(&operand);
1486 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001487 SkRegion::Op elementOp = element->getOp();
1488 if (elementOp == SkRegion::kReplace_Op) {
1489 devPath = operand;
1490 } else {
1491 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1492 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001493 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1494 // perhaps we need an API change to avoid this sort of mixed-signals about
1495 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001496 if (element->isAA()) {
1497 edgeStyle = kSoft_ClipEdgeStyle;
1498 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001499 }
1500 op = SkRegion::kReplace_Op;
1501 }
1502
reed73e714e2014-09-04 09:02:23 -07001503 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504}
1505
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001506void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001507 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508}
1509
1510void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001511 AutoValidateClip avc(this);
1512
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001514 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515
reed@google.com5c3d1472011-02-22 19:12:23 +00001516 // todo: signal fClipStack that we have a region, and therefore (I guess)
1517 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001518 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001519
reed1f836ee2014-07-07 07:49:34 -07001520 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521}
1522
reed@google.com819c9212011-02-23 18:56:55 +00001523#ifdef SK_DEBUG
1524void SkCanvas::validateClip() const {
1525 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001526 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001527 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001528 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001529 return;
1530 }
1531
reed@google.com819c9212011-02-23 18:56:55 +00001532 SkIRect ir;
1533 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001534 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001535
robertphillips@google.com80214e22012-07-20 15:33:18 +00001536 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001537 const SkClipStack::Element* element;
1538 while ((element = iter.next()) != NULL) {
1539 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001540 case SkClipStack::Element::kRect_Type:
1541 element->getRect().round(&ir);
1542 tmpClip.op(ir, element->getOp());
1543 break;
1544 case SkClipStack::Element::kEmpty_Type:
1545 tmpClip.setEmpty();
1546 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001547 default: {
1548 SkPath path;
1549 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001550 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001551 break;
1552 }
reed@google.com819c9212011-02-23 18:56:55 +00001553 }
1554 }
reed@google.com819c9212011-02-23 18:56:55 +00001555}
1556#endif
1557
reed@google.com90c07ea2012-04-13 13:50:27 +00001558void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001559 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001560 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001561
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001562 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001563 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001564 }
1565}
1566
reed@google.com5c3d1472011-02-22 19:12:23 +00001567///////////////////////////////////////////////////////////////////////////////
1568
reed@google.com754de5f2014-02-24 19:38:20 +00001569bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001570 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001571}
1572
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001573bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001574 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001575}
1576
reed@google.com3b3e8952012-08-16 20:53:31 +00001577bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001578 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001579 return true;
1580
reed1f836ee2014-07-07 07:49:34 -07001581 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582 return true;
1583 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584
reed1f836ee2014-07-07 07:49:34 -07001585 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001586 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001587 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001588 SkIRect idst;
1589 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001590 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001591 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001592 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001593
reed@android.coma380ae42009-07-21 01:17:02 +00001594 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001595 // TODO: should we use | instead, or compare all 4 at once?
1596 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001597 return true;
1598 }
reed@google.comc0784db2013-12-13 21:16:12 +00001599 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001600 return true;
1601 }
1602 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604}
1605
reed@google.com3b3e8952012-08-16 20:53:31 +00001606bool SkCanvas::quickReject(const SkPath& path) const {
1607 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
reed@google.com3b3e8952012-08-16 20:53:31 +00001610bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001611 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001612 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 return false;
1614 }
1615
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001616 SkMatrix inverse;
1617 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001618 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001619 if (bounds) {
1620 bounds->setEmpty();
1621 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001622 return false;
1623 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624
bsalomon49f085d2014-09-05 13:34:00 -07001625 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001626 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001627 // adjust it outwards in case we are antialiasing
1628 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001629
reed@google.com8f4d2302013-12-17 16:44:46 +00001630 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1631 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 inverse.mapRect(bounds, r);
1633 }
1634 return true;
1635}
1636
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001637bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001638 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001639 if (clip.isEmpty()) {
1640 if (bounds) {
1641 bounds->setEmpty();
1642 }
1643 return false;
1644 }
1645
bsalomon49f085d2014-09-05 13:34:00 -07001646 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001647 *bounds = clip.getBounds();
1648 }
1649 return true;
1650}
1651
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001653 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654}
1655
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001656const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001657 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001658}
1659
reed@google.com9c135db2014-03-12 18:28:35 +00001660GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1661 SkBaseDevice* dev = this->getTopDevice();
1662 return dev ? dev->accessRenderTarget() : NULL;
1663}
1664
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001665SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001666 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001667 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668}
1669
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001670GrContext* SkCanvas::getGrContext() {
1671#if SK_SUPPORT_GPU
1672 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001673 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001674 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001675 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001676 return renderTarget->getContext();
1677 }
1678 }
1679#endif
1680
1681 return NULL;
1682
1683}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001684
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001685void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1686 const SkPaint& paint) {
1687 if (outer.isEmpty()) {
1688 return;
1689 }
1690 if (inner.isEmpty()) {
1691 this->drawRRect(outer, paint);
1692 return;
1693 }
1694
1695 // We don't have this method (yet), but technically this is what we should
1696 // be able to assert...
1697 // SkASSERT(outer.contains(inner));
1698 //
1699 // For now at least check for containment of bounds
1700 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1701
1702 this->onDrawDRRect(outer, inner, paint);
1703}
1704
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705//////////////////////////////////////////////////////////////////////////////
1706// These are the virtual drawing methods
1707//////////////////////////////////////////////////////////////////////////////
1708
reed@google.com2a981812011-04-14 18:59:28 +00001709void SkCanvas::clear(SkColor color) {
1710 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001711 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001712 while (iter.next()) {
1713 iter.fDevice->clear(color);
1714 }
1715}
1716
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001717void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001718 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001719 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1720 }
1721}
1722
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001724 this->internalDrawPaint(paint);
1725}
1726
1727void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001728 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729
1730 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001731 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 }
1733
reed@google.com4e2b3d32011-04-07 14:18:59 +00001734 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735}
1736
1737void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1738 const SkPaint& paint) {
1739 if ((long)count <= 0) {
1740 return;
1741 }
1742
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001743 SkRect r, storage;
1744 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001745 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001746 // special-case 2 points (common for drawing a single line)
1747 if (2 == count) {
1748 r.set(pts[0], pts[1]);
1749 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001750 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001751 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001752 bounds = &paint.computeFastStrokeBounds(r, &storage);
1753 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001754 return;
1755 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001756 }
reed@google.coma584aed2012-05-16 14:06:02 +00001757
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758 SkASSERT(pts != NULL);
1759
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001760 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001761
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001763 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 }
reed@google.com4b226022011-01-11 18:32:13 +00001765
reed@google.com4e2b3d32011-04-07 14:18:59 +00001766 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767}
1768
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001769void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
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) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001789 SkRect storage;
1790 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001791 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001792 bounds = &paint.computeFastBounds(oval, &storage);
1793 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001794 return;
1795 }
1796 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001797
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001798 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001799
1800 while (iter.next()) {
1801 iter.fDevice->drawOval(iter, oval, looper.paint());
1802 }
1803
1804 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001805}
1806
1807void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001808 SkRect storage;
1809 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001810 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001811 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1812 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001813 return;
1814 }
1815 }
1816
1817 if (rrect.isRect()) {
1818 // call the non-virtual version
1819 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001820 return;
1821 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001822 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001823 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1824 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001825 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001826
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001827 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001828
1829 while (iter.next()) {
1830 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1831 }
1832
1833 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001834}
1835
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001836void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1837 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001838 SkRect storage;
1839 const SkRect* bounds = NULL;
1840 if (paint.canComputeFastBounds()) {
1841 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1842 if (this->quickReject(*bounds)) {
1843 return;
1844 }
1845 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001846
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001847 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001848
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001849 while (iter.next()) {
1850 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1851 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001852
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001853 LOOPER_END
1854}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001855
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001856void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001857 if (!path.isFinite()) {
1858 return;
1859 }
1860
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001861 SkRect storage;
1862 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001863 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001864 const SkRect& pathBounds = path.getBounds();
1865 bounds = &paint.computeFastBounds(pathBounds, &storage);
1866 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 return;
1868 }
1869 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001870
1871 const SkRect& r = path.getBounds();
1872 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001873 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001874 this->internalDrawPaint(paint);
1875 }
1876 return;
1877 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001879 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880
1881 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001882 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883 }
1884
reed@google.com4e2b3d32011-04-07 14:18:59 +00001885 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886}
1887
1888void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1889 const SkPaint* paint) {
1890 SkDEBUGCODE(bitmap.validate();)
1891
reed@google.com3d608122011-11-21 15:16:16 +00001892 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001893 SkRect bounds = {
1894 x, y,
1895 x + SkIntToScalar(bitmap.width()),
1896 y + SkIntToScalar(bitmap.height())
1897 };
1898 if (paint) {
1899 (void)paint->computeFastBounds(bounds, &bounds);
1900 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001901 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001902 return;
1903 }
1904 }
reed@google.com4b226022011-01-11 18:32:13 +00001905
reed@android.com8a1c16f2008-12-17 15:59:43 +00001906 SkMatrix matrix;
1907 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001908 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909}
1910
reed@google.com9987ec32011-09-07 11:57:52 +00001911// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001912void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001913 const SkRect& dst, const SkPaint* paint,
1914 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001915 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001916 return;
1917 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001918
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001919 SkRect storage;
1920 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001921 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001922 if (paint) {
1923 bounds = &paint->computeFastBounds(dst, &storage);
1924 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001925 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001926 return;
1927 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 }
reed@google.com3d608122011-11-21 15:16:16 +00001929
reed@google.com33535f32012-09-25 15:37:50 +00001930 SkLazyPaint lazy;
1931 if (NULL == paint) {
1932 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001933 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001934
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001935 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001936
reed@google.com33535f32012-09-25 15:37:50 +00001937 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001938 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001939 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001940
reed@google.com33535f32012-09-25 15:37:50 +00001941 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001942}
1943
reed@google.com71121732012-09-18 15:14:33 +00001944void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001945 const SkRect& dst, const SkPaint* paint,
1946 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001947 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001948 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001949}
1950
reed@android.com8a1c16f2008-12-17 15:59:43 +00001951void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1952 const SkPaint* paint) {
1953 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001954 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001955}
1956
reed@google.com9987ec32011-09-07 11:57:52 +00001957void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1958 const SkIRect& center, const SkRect& dst,
1959 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001960 if (bitmap.drawsNothing()) {
1961 return;
1962 }
reed@google.com3d608122011-11-21 15:16:16 +00001963 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001964 SkRect storage;
1965 const SkRect* bounds = &dst;
1966 if (paint) {
1967 bounds = &paint->computeFastBounds(dst, &storage);
1968 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001969 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001970 return;
1971 }
1972 }
1973
reed@google.com9987ec32011-09-07 11:57:52 +00001974 const int32_t w = bitmap.width();
1975 const int32_t h = bitmap.height();
1976
1977 SkIRect c = center;
1978 // pin center to the bounds of the bitmap
1979 c.fLeft = SkMax32(0, center.fLeft);
1980 c.fTop = SkMax32(0, center.fTop);
1981 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1982 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1983
reed@google.com71121732012-09-18 15:14:33 +00001984 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001985 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001986 };
1987 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001988 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00001989 };
reed@google.com9987ec32011-09-07 11:57:52 +00001990 SkScalar dstX[4] = {
1991 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
1992 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
1993 };
1994 SkScalar dstY[4] = {
1995 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
1996 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
1997 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001998
reed@google.com9987ec32011-09-07 11:57:52 +00001999 if (dstX[1] > dstX[2]) {
2000 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2001 dstX[2] = dstX[1];
2002 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002003
reed@google.com9987ec32011-09-07 11:57:52 +00002004 if (dstY[1] > dstY[2]) {
2005 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2006 dstY[2] = dstY[1];
2007 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002008
reed@google.com9987ec32011-09-07 11:57:52 +00002009 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002010 SkRect s, d;
2011
reed@google.com9987ec32011-09-07 11:57:52 +00002012 s.fTop = srcY[y];
2013 s.fBottom = srcY[y+1];
2014 d.fTop = dstY[y];
2015 d.fBottom = dstY[y+1];
2016 for (int x = 0; x < 3; x++) {
2017 s.fLeft = srcX[x];
2018 s.fRight = srcX[x+1];
2019 d.fLeft = dstX[x];
2020 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002021 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002022 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002023 }
2024 }
2025}
2026
2027void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2028 const SkRect& dst, const SkPaint* paint) {
2029 SkDEBUGCODE(bitmap.validate();)
2030
2031 // Need a device entry-point, so gpu can use a mesh
2032 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2033}
2034
reed@google.comf67e4cf2011-03-15 20:56:58 +00002035class SkDeviceFilteredPaint {
2036public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002037 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2038 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002039 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002040 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002041 newPaint->setFlags(flags.fFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002042 fPaint = newPaint;
2043 } else {
2044 fPaint = &paint;
2045 }
2046 }
2047
reed@google.comf67e4cf2011-03-15 20:56:58 +00002048 const SkPaint& paint() const { return *fPaint; }
2049
2050private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002051 const SkPaint* fPaint;
2052 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002053};
2054
bungeman@google.com52c748b2011-08-22 21:30:43 +00002055void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2056 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002057 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002058 draw.fDevice->drawRect(draw, r, paint);
2059 } else {
2060 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002061 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002062 draw.fDevice->drawRect(draw, r, p);
2063 }
2064}
2065
2066void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2067 const char text[], size_t byteLength,
2068 SkScalar x, SkScalar y) {
2069 SkASSERT(byteLength == 0 || text != NULL);
2070
2071 // nothing to draw
2072 if (text == NULL || byteLength == 0 ||
2073 draw.fClip->isEmpty() ||
2074 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2075 return;
2076 }
2077
2078 SkScalar width = 0;
2079 SkPoint start;
2080
2081 start.set(0, 0); // to avoid warning
2082 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2083 SkPaint::kStrikeThruText_Flag)) {
2084 width = paint.measureText(text, byteLength);
2085
2086 SkScalar offsetX = 0;
2087 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2088 offsetX = SkScalarHalf(width);
2089 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2090 offsetX = width;
2091 }
2092 start.set(x - offsetX, y);
2093 }
2094
2095 if (0 == width) {
2096 return;
2097 }
2098
2099 uint32_t flags = paint.getFlags();
2100
2101 if (flags & (SkPaint::kUnderlineText_Flag |
2102 SkPaint::kStrikeThruText_Flag)) {
2103 SkScalar textSize = paint.getTextSize();
2104 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2105 SkRect r;
2106
2107 r.fLeft = start.fX;
2108 r.fRight = start.fX + width;
2109
2110 if (flags & SkPaint::kUnderlineText_Flag) {
2111 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2112 start.fY);
2113 r.fTop = offset;
2114 r.fBottom = offset + height;
2115 DrawRect(draw, paint, r, textSize);
2116 }
2117 if (flags & SkPaint::kStrikeThruText_Flag) {
2118 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2119 start.fY);
2120 r.fTop = offset;
2121 r.fBottom = offset + height;
2122 DrawRect(draw, paint, r, textSize);
2123 }
2124 }
2125}
2126
reed@google.come0d9ce82014-04-23 04:00:17 +00002127void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2128 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002129 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002130
2131 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002132 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002133 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002134 DrawTextDecorations(iter, dfp.paint(),
2135 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002136 }
2137
reed@google.com4e2b3d32011-04-07 14:18:59 +00002138 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139}
2140
reed@google.come0d9ce82014-04-23 04:00:17 +00002141void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2142 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002143 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002144
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002146 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002148 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002150
reed@google.com4e2b3d32011-04-07 14:18:59 +00002151 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002152}
2153
reed@google.come0d9ce82014-04-23 04:00:17 +00002154void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2155 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002156 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002157
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002159 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002160 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002161 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002163
reed@google.com4e2b3d32011-04-07 14:18:59 +00002164 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002165}
2166
reed@google.come0d9ce82014-04-23 04:00:17 +00002167void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2168 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002169 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002170
reed@android.com8a1c16f2008-12-17 15:59:43 +00002171 while (iter.next()) {
2172 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002173 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002175
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002176 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002177}
2178
fmalita00d5c2c2014-08-21 08:53:26 -07002179void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2180 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002181
2182 // FIXME: temporarily disable quickreject for empty bounds,
2183 // pending implicit blob bounds implementation.
2184 if (!blob->bounds().isEmpty() && paint.canComputeFastBounds()) {
2185 SkRect storage;
2186
2187 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2188 return;
2189 }
2190 }
2191
fmalitaaa1b9122014-08-28 14:32:24 -07002192 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002193
fmalitaaa1b9122014-08-28 14:32:24 -07002194 while (iter.next()) {
2195 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2196 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002197 }
2198
fmalitaaa1b9122014-08-28 14:32:24 -07002199 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002200}
2201
reed@google.come0d9ce82014-04-23 04:00:17 +00002202// These will become non-virtual, so they always call the (virtual) onDraw... method
2203void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2204 const SkPaint& paint) {
2205 this->onDrawText(text, byteLength, x, y, paint);
2206}
2207void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2208 const SkPaint& paint) {
2209 this->onDrawPosText(text, byteLength, pos, paint);
2210}
2211void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2212 SkScalar constY, const SkPaint& paint) {
2213 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2214}
2215void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2216 const SkMatrix* matrix, const SkPaint& paint) {
2217 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2218}
fmalita00d5c2c2014-08-21 08:53:26 -07002219void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2220 const SkPaint& paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002221 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002222 this->onDrawTextBlob(blob, x, y, paint);
2223 }
2224}
reed@google.come0d9ce82014-04-23 04:00:17 +00002225
reed@android.com8a1c16f2008-12-17 15:59:43 +00002226void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2227 const SkPoint verts[], const SkPoint texs[],
2228 const SkColor colors[], SkXfermode* xmode,
2229 const uint16_t indices[], int indexCount,
2230 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002231 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002232
reed@android.com8a1c16f2008-12-17 15:59:43 +00002233 while (iter.next()) {
2234 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002235 colors, xmode, indices, indexCount,
2236 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002237 }
reed@google.com4b226022011-01-11 18:32:13 +00002238
reed@google.com4e2b3d32011-04-07 14:18:59 +00002239 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240}
2241
dandovb3c9d1c2014-08-12 08:34:29 -07002242void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2243 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2244 if (NULL == cubics) {
2245 return;
2246 }
mtklein6cfa73a2014-08-13 13:33:49 -07002247
dandovecfff212014-08-04 10:02:00 -07002248 // Since a patch is always within the convex hull of the control points, we discard it when its
2249 // bounding rectangle is completely outside the current clip.
2250 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002251 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002252 if (this->quickReject(bounds)) {
2253 return;
2254 }
mtklein6cfa73a2014-08-13 13:33:49 -07002255
dandovb3c9d1c2014-08-12 08:34:29 -07002256 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2257}
2258
2259void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2260 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2261
dandovecfff212014-08-04 10:02:00 -07002262 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002263
dandovecfff212014-08-04 10:02:00 -07002264 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002265 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002266 }
mtklein6cfa73a2014-08-13 13:33:49 -07002267
dandovecfff212014-08-04 10:02:00 -07002268 LOOPER_END
2269}
2270
reed@android.com8a1c16f2008-12-17 15:59:43 +00002271//////////////////////////////////////////////////////////////////////////////
2272// These methods are NOT virtual, and therefore must call back into virtual
2273// methods, rather than actually drawing themselves.
2274//////////////////////////////////////////////////////////////////////////////
2275
2276void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002277 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002278 SkPaint paint;
2279
2280 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002281 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002282 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283 }
2284 this->drawPaint(paint);
2285}
2286
reed@android.com845fdac2009-06-23 03:01:32 +00002287void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288 SkPaint paint;
2289
2290 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002291 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002292 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002293 }
2294 this->drawPaint(paint);
2295}
2296
2297void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2298 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002299
reed@android.com8a1c16f2008-12-17 15:59:43 +00002300 pt.set(x, y);
2301 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2302}
2303
2304void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2305 SkPoint pt;
2306 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002307
reed@android.com8a1c16f2008-12-17 15:59:43 +00002308 pt.set(x, y);
2309 paint.setColor(color);
2310 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2311}
2312
2313void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2314 const SkPaint& paint) {
2315 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002316
reed@android.com8a1c16f2008-12-17 15:59:43 +00002317 pts[0].set(x0, y0);
2318 pts[1].set(x1, y1);
2319 this->drawPoints(kLines_PointMode, 2, pts, paint);
2320}
2321
2322void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2323 SkScalar right, SkScalar bottom,
2324 const SkPaint& paint) {
2325 SkRect r;
2326
2327 r.set(left, top, right, bottom);
2328 this->drawRect(r, paint);
2329}
2330
2331void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2332 const SkPaint& paint) {
2333 if (radius < 0) {
2334 radius = 0;
2335 }
2336
2337 SkRect r;
2338 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002339 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340}
2341
2342void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2343 const SkPaint& paint) {
2344 if (rx > 0 && ry > 0) {
2345 if (paint.canComputeFastBounds()) {
2346 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002347 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002348 return;
2349 }
2350 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002351 SkRRect rrect;
2352 rrect.setRectXY(r, rx, ry);
2353 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002354 } else {
2355 this->drawRect(r, paint);
2356 }
2357}
2358
reed@android.com8a1c16f2008-12-17 15:59:43 +00002359void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2360 SkScalar sweepAngle, bool useCenter,
2361 const SkPaint& paint) {
2362 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2363 this->drawOval(oval, paint);
2364 } else {
2365 SkPath path;
2366 if (useCenter) {
2367 path.moveTo(oval.centerX(), oval.centerY());
2368 }
2369 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2370 if (useCenter) {
2371 path.close();
2372 }
2373 this->drawPath(path, paint);
2374 }
2375}
2376
2377void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2378 const SkPath& path, SkScalar hOffset,
2379 SkScalar vOffset, const SkPaint& paint) {
2380 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002381
reed@android.com8a1c16f2008-12-17 15:59:43 +00002382 matrix.setTranslate(hOffset, vOffset);
2383 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2384}
2385
reed@android.comf76bacf2009-05-13 14:00:33 +00002386///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002387void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002388 SkBaseDevice* device = this->getDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002389 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002390 device->EXPERIMENTAL_optimize(picture);
2391 }
2392}
reed@android.comf76bacf2009-05-13 14:00:33 +00002393
robertphillips9b14f262014-06-04 05:40:44 -07002394void SkCanvas::drawPicture(const SkPicture* picture) {
bsalomon49f085d2014-09-05 13:34:00 -07002395 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002396 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002397 }
2398}
2399
reedd5fa1a42014-08-09 11:08:05 -07002400void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002401 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002402 if (matrix && matrix->isIdentity()) {
2403 matrix = NULL;
2404 }
2405 this->onDrawPicture(picture, matrix, paint);
2406 }
2407}
robertphillips9b14f262014-06-04 05:40:44 -07002408
reedd5fa1a42014-08-09 11:08:05 -07002409void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2410 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002411 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002412 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002413 // Canvas has to first give the device the opportunity to render
2414 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002415 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002416 return; // the device has rendered the entire picture
2417 }
2418 }
2419
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002420 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002421
robertphillipsc5ba71d2014-09-04 08:42:50 -07002422 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002423}
2424
reed@android.com8a1c16f2008-12-17 15:59:43 +00002425///////////////////////////////////////////////////////////////////////////////
2426///////////////////////////////////////////////////////////////////////////////
2427
2428SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002429 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002430
2431 SkASSERT(canvas);
2432
2433 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2434 fDone = !fImpl->next();
2435}
2436
2437SkCanvas::LayerIter::~LayerIter() {
2438 fImpl->~SkDrawIter();
2439}
2440
2441void SkCanvas::LayerIter::next() {
2442 fDone = !fImpl->next();
2443}
2444
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002445SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002446 return fImpl->getDevice();
2447}
2448
2449const SkMatrix& SkCanvas::LayerIter::matrix() const {
2450 return fImpl->getMatrix();
2451}
2452
2453const SkPaint& SkCanvas::LayerIter::paint() const {
2454 const SkPaint* paint = fImpl->getPaint();
2455 if (NULL == paint) {
2456 paint = &fDefaultPaint;
2457 }
2458 return *paint;
2459}
2460
2461const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2462int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2463int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002464
2465///////////////////////////////////////////////////////////////////////////////
2466
fmalitac3b589a2014-06-05 12:40:07 -07002467SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002468
2469///////////////////////////////////////////////////////////////////////////////
2470
2471static bool supported_for_raster_canvas(const SkImageInfo& info) {
2472 switch (info.alphaType()) {
2473 case kPremul_SkAlphaType:
2474 case kOpaque_SkAlphaType:
2475 break;
2476 default:
2477 return false;
2478 }
2479
2480 switch (info.colorType()) {
2481 case kAlpha_8_SkColorType:
2482 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002483 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002484 break;
2485 default:
2486 return false;
2487 }
2488
2489 return true;
2490}
2491
2492SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2493 if (!supported_for_raster_canvas(info)) {
2494 return NULL;
2495 }
2496
2497 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -07002498 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002499 return NULL;
2500 }
2501
2502 // should this functionality be moved into allocPixels()?
2503 if (!bitmap.info().isOpaque()) {
2504 bitmap.eraseColor(0);
2505 }
2506 return SkNEW_ARGS(SkCanvas, (bitmap));
2507}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002508
2509SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2510 if (!supported_for_raster_canvas(info)) {
2511 return NULL;
2512 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002513
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002514 SkBitmap bitmap;
2515 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2516 return NULL;
2517 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002518 return SkNEW_ARGS(SkCanvas, (bitmap));
2519}
reedd5fa1a42014-08-09 11:08:05 -07002520
2521///////////////////////////////////////////////////////////////////////////////
2522
2523SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002524 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002525 : fCanvas(canvas)
2526 , fSaveCount(canvas->getSaveCount())
2527{
bsalomon49f085d2014-09-05 13:34:00 -07002528 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002529 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002530 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002531 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002532 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002533 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002534 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002535 canvas->save();
2536 }
mtklein6cfa73a2014-08-13 13:33:49 -07002537
bsalomon49f085d2014-09-05 13:34:00 -07002538 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002539 canvas->concat(*matrix);
2540 }
2541}
2542
2543SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2544 fCanvas->restoreToCount(fSaveCount);
2545}