blob: afd7fbef88bd82c97f7b9b6ced77e259209417cc [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"
piotaixrb5fae932014-09-24 13:03:30 -070015#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000016#include "SkMetaData.h"
caryclark@google.com45a75fb2013-04-25 13:34:40 +000017#include "SkPathOps.h"
dandovb3c9d1c2014-08-12 08:34:29 -070018#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000020#include "SkRasterClip.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000021#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000022#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000023#include "SkSurface_Base.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkTemplates.h"
fmalita7ba7aa72014-08-29 09:46:36 -070025#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000026#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000027#include "SkTLazy.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000030#if SK_SUPPORT_GPU
31#include "GrRenderTarget.h"
32#endif
33
reed@google.comda17f752012-08-16 18:27:05 +000034// experimental for faster tiled drawing...
35//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000036
reed@android.com8a1c16f2008-12-17 15:59:43 +000037//#define SK_TRACE_SAVERESTORE
38
39#ifdef SK_TRACE_SAVERESTORE
40 static int gLayerCounter;
41 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
42 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
43
44 static int gRecCounter;
45 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
46 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
47
48 static int gCanvasCounter;
49 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
50 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
51#else
52 #define inc_layer()
53 #define dec_layer()
54 #define inc_rec()
55 #define dec_rec()
56 #define inc_canvas()
57 #define dec_canvas()
58#endif
59
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +000060typedef SkTLazy<SkPaint> SkLazyPaint;
61
reed@google.com97af1a62012-08-28 12:19:02 +000062void SkCanvas::predrawNotify() {
63 if (fSurfaceBase) {
commit-bot@chromium.orgc4c98702013-04-22 14:28:01 +000064 fSurfaceBase->aboutToDraw(SkSurface::kRetain_ContentChangeMode);
reed@google.com97af1a62012-08-28 12:19:02 +000065 }
66}
67
reed@android.com8a1c16f2008-12-17 15:59:43 +000068///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000069
reed4a8126e2014-09-22 07:29:03 -070070static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
71 const uint32_t propFlags = props.flags();
72 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
73 flags &= ~SkPaint::kDither_Flag;
74 }
75 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
76 flags &= ~SkPaint::kAntiAlias_Flag;
77 }
78 return flags;
79}
80
81///////////////////////////////////////////////////////////////////////////////
82
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000083/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +000084 The clip/matrix/proc are fields that reflect the top of the save/restore
85 stack. Whenever the canvas changes, it marks a dirty flag, and then before
86 these are used (assuming we're not on a layer) we rebuild these cache
87 values: they reflect the top of the save stack, but translated and clipped
88 by the device's XY offset and bitmap-bounds.
89*/
90struct DeviceCM {
91 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000092 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +000093 SkRasterClip fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +000094 const SkMatrix* fMatrix;
reed@google.com6f8f2922011-03-04 22:27:10 +000095 SkPaint* fPaint; // may be null (in the future)
reed@android.com8a1c16f2008-12-17 15:59:43 +000096
reedd9544982014-09-09 18:46:22 -070097 DeviceCM(SkBaseDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas,
98 bool conservativeRasterClip)
99 : fNext(NULL)
100 , fClip(conservativeRasterClip)
101 {
102 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000104 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105 }
reed@google.com4b226022011-01-11 18:32:13 +0000106 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000107 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000108 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000110 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700111 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000112 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000113 fDevice->unref();
114 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000115 SkDELETE(fPaint);
116 }
reed@google.com4b226022011-01-11 18:32:13 +0000117
reed@google.com045e62d2011-10-24 12:19:46 +0000118 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
119 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000120 int x = fDevice->getOrigin().x();
121 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000122 int width = fDevice->width();
123 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000124
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 if ((x | y) == 0) {
126 fMatrix = &totalMatrix;
127 fClip = totalClip;
128 } else {
129 fMatrixStorage = totalMatrix;
130 fMatrixStorage.postTranslate(SkIntToScalar(-x),
131 SkIntToScalar(-y));
132 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000133
reed@android.com8a1c16f2008-12-17 15:59:43 +0000134 totalClip.translate(-x, -y, &fClip);
135 }
136
reed@google.com045e62d2011-10-24 12:19:46 +0000137 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138
139 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000140
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000142 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000143 SkRegion::kDifference_Op);
144 }
reed@google.com4b226022011-01-11 18:32:13 +0000145
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000146 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
147
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148#ifdef SK_DEBUG
149 if (!fClip.isEmpty()) {
150 SkIRect deviceR;
151 deviceR.set(0, 0, width, height);
152 SkASSERT(deviceR.contains(fClip.getBounds()));
153 }
154#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000155 }
156
reed@android.com8a1c16f2008-12-17 15:59:43 +0000157private:
bsalomon@google.com0e354aa2012-10-08 20:44:25 +0000158 SkMatrix fMatrixStorage;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000159};
160
161/* This is the record we keep for each save/restore level in the stack.
162 Since a level optionally copies the matrix and/or stack, we have pointers
163 for these fields. If the value is copied for this level, the copy is
164 stored in the ...Storage field, and the pointer points to that. If the
165 value is not copied for this level, we ignore ...Storage, and just point
166 at the corresponding value in the previous level in the stack.
167*/
168class SkCanvas::MCRec {
169public:
reed6f097092014-09-09 12:51:10 -0700170 SkRasterClip fRasterClip;
reedd9544982014-09-09 18:46:22 -0700171 SkMatrix fMatrix;
reed1f836ee2014-07-07 07:49:34 -0700172 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700173 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174 /* If there are any layers in the stack, this points to the top-most
175 one that is at or below this level in the stack (so we know what
176 bitmap/device to draw into from this level. This value is NOT
177 reference counted, since the real owner is either our fLayer field,
178 or a previous one in a lower level.)
179 */
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000180 DeviceCM* fTopLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181
reedd9544982014-09-09 18:46:22 -0700182 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
183 fMatrix.reset();
184 fFilter = NULL;
185 fLayer = NULL;
186 fTopLayer = NULL;
piotaixrb5fae932014-09-24 13:03:30 -0700187
reedd9544982014-09-09 18:46:22 -0700188 // don't bother initializing fNext
189 inc_rec();
190 }
191 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip) {
192 fMatrix = prev.fMatrix;
193 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700195 fTopLayer = prev.fTopLayer;
piotaixrb5fae932014-09-24 13:03:30 -0700196
reed@android.com8a1c16f2008-12-17 15:59:43 +0000197 // don't bother initializing fNext
198 inc_rec();
199 }
200 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000201 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 SkDELETE(fLayer);
203 dec_rec();
204 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000205};
206
207class SkDrawIter : public SkDraw {
208public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000209 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000210 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000211 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 canvas->updateDeviceCMCache();
213
reed@google.com90c07ea2012-04-13 13:50:27 +0000214 fClipStack = &canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000215 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000216 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217 }
reed@google.com4b226022011-01-11 18:32:13 +0000218
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 bool next() {
220 // skip over recs with empty clips
221 if (fSkipEmptyClips) {
222 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
223 fCurrLayer = fCurrLayer->fNext;
224 }
225 }
226
reed@google.comf68c5e22012-02-24 16:38:58 +0000227 const DeviceCM* rec = fCurrLayer;
228 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229
230 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000231 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
232 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 fDevice = rec->fDevice;
234 fBitmap = &fDevice->accessBitmap(true);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000236 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
238 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000240
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 return true;
242 }
243 return false;
244 }
reed@google.com4b226022011-01-11 18:32:13 +0000245
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000246 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000247 int getX() const { return fDevice->getOrigin().x(); }
248 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 const SkMatrix& getMatrix() const { return *fMatrix; }
250 const SkRegion& getClip() const { return *fClip; }
251 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253private:
254 SkCanvas* fCanvas;
255 const DeviceCM* fCurrLayer;
256 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000257 SkBool8 fSkipEmptyClips;
258
259 typedef SkDraw INHERITED;
260};
261
262/////////////////////////////////////////////////////////////////////////////
263
264class AutoDrawLooper {
265public:
reed4a8126e2014-09-22 07:29:03 -0700266 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000267 bool skipLayerForImageFilter = false,
268 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000269 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700271 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000272 fSaveCount = canvas->getSaveCount();
reed@google.com8926b162012-03-23 15:36:36 +0000273 fDoClearImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000274 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275
reed@google.com8926b162012-03-23 15:36:36 +0000276 if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
277 SkPaint tmp;
278 tmp.setImageFilter(fOrigPaint.getImageFilter());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000279 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
280 true, SkCanvas::kFullLayer_SaveLayerStrategy);
reed@google.com8926b162012-03-23 15:36:36 +0000281 // we'll clear the imageFilter for the actual draws in next(), so
282 // it will only be applied during the restore().
283 fDoClearImageFilter = true;
284 }
285
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000286 if (SkDrawLooper* looper = paint.getLooper()) {
287 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
288 looper->contextSize());
289 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000290 fIsSimple = false;
291 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000292 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000293 // can we be marked as simple?
294 fIsSimple = !fFilter && !fDoClearImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000295 }
piotaixrb5fae932014-09-24 13:03:30 -0700296
reed4a8126e2014-09-22 07:29:03 -0700297 uint32_t oldFlags = paint.getFlags();
298 fNewPaintFlags = filter_paint_flags(props, oldFlags);
299 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
300 SkPaint* paint = fLazyPaint.set(fOrigPaint);
301 paint->setFlags(fNewPaintFlags);
302 fPaint = paint;
303 // if we're not simple, doNext() will take care of calling setFlags()
304 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000305 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000306
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 ~AutoDrawLooper() {
reed@google.com8926b162012-03-23 15:36:36 +0000308 if (fDoClearImageFilter) {
309 fCanvas->internalRestore();
310 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000311 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000313
reed@google.com4e2b3d32011-04-07 14:18:59 +0000314 const SkPaint& paint() const {
315 SkASSERT(fPaint);
316 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000318
reed@google.com129ec222012-05-15 13:24:09 +0000319 bool next(SkDrawFilter::Type drawType) {
320 if (fDone) {
321 return false;
322 } else if (fIsSimple) {
323 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000324 return !fPaint->nothingToDraw();
325 } else {
326 return this->doNext(drawType);
327 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000328 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000329
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000331 SkLazyPaint fLazyPaint;
332 SkCanvas* fCanvas;
333 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000334 SkDrawFilter* fFilter;
335 const SkPaint* fPaint;
336 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700337 uint32_t fNewPaintFlags;
reed@google.com8926b162012-03-23 15:36:36 +0000338 bool fDoClearImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000339 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000340 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000341 SkDrawLooper::Context* fLooperContext;
342 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000343
344 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000345};
346
reed@google.com129ec222012-05-15 13:24:09 +0000347bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000348 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000349 SkASSERT(!fIsSimple);
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000350 SkASSERT(fLooperContext || fFilter || fDoClearImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000351
352 SkPaint* paint = fLazyPaint.set(fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700353 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000354
355 if (fDoClearImageFilter) {
356 paint->setImageFilter(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000357 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000358
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000359 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000360 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000361 return false;
362 }
363 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000364 if (!fFilter->filter(paint, drawType)) {
365 fDone = true;
366 return false;
367 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000368 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000369 // no looper means we only draw once
370 fDone = true;
371 }
372 }
373 fPaint = paint;
374
375 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000376 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000377 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000378 }
379
380 // call this after any possible paint modifiers
381 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000382 fPaint = NULL;
383 return false;
384 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000385 return true;
386}
387
reed@android.com8a1c16f2008-12-17 15:59:43 +0000388////////// macros to place around the internal draw calls //////////////////
389
reed@google.com8926b162012-03-23 15:36:36 +0000390#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000391 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700392 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000393 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000394 SkDrawIter iter(this);
395
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000396#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000397 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700398 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000399 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000400 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000401
reed@google.com4e2b3d32011-04-07 14:18:59 +0000402#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403
404////////////////////////////////////////////////////////////////////////////
405
reed4a8126e2014-09-22 07:29:03 -0700406void SkCanvas::setupDevice(SkBaseDevice* device) {
407 device->setPixelGeometry(fProps.pixelGeometry());
408}
409
reedd9544982014-09-09 18:46:22 -0700410SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
411 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000412 fCachedLocalClipBounds.setEmpty();
413 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000414 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000415 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700416 fDeviceCMDirty = true;
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000417 fSaveLayerCount = 0;
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +0000418 fCullCount = 0;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000419 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000420
421 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700422 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000423
reedd9544982014-09-09 18:46:22 -0700424 fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000425 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426
reed@google.com97af1a62012-08-28 12:19:02 +0000427 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000428
reedf92c8662014-08-18 08:02:43 -0700429 if (device) {
reed4a8126e2014-09-22 07:29:03 -0700430 this->setupDevice(device);
431 if (device->forceConservativeRasterClip()) {
432 fConservativeRasterClip = true;
433 }
reedf92c8662014-08-18 08:02:43 -0700434 device->onAttachToCanvas(this);
435 fMCRec->fLayer->fDevice = SkRef(device);
436 fMCRec->fRasterClip.setRect(SkIRect::MakeWH(device->width(), device->height()));
437 }
438 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000439}
440
reed@google.comcde92112011-07-06 20:00:52 +0000441SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000442 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700443 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000444{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000445 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000446
reedd9544982014-09-09 18:46:22 -0700447 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000448}
449
reedd9544982014-09-09 18:46:22 -0700450static SkBitmap make_nopixels(int width, int height) {
451 SkBitmap bitmap;
452 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
453 return bitmap;
454}
455
456class SkNoPixelsBitmapDevice : public SkBitmapDevice {
457public:
458 SkNoPixelsBitmapDevice(int width, int height) : INHERITED(make_nopixels(width, height)) {}
459
460private:
piotaixrb5fae932014-09-24 13:03:30 -0700461
reedd9544982014-09-09 18:46:22 -0700462 typedef SkBitmapDevice INHERITED;
463};
464
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000465SkCanvas::SkCanvas(int width, int height)
466 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700467 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000468{
469 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700470
reedd9544982014-09-09 18:46:22 -0700471 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), kDefault_InitFlags)->unref();
472}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000473
reedd9544982014-09-09 18:46:22 -0700474SkCanvas::SkCanvas(int width, int height, InitFlags flags)
475 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700476 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700477{
478 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700479
reedd9544982014-09-09 18:46:22 -0700480 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (width, height)), flags)->unref();
481}
482
reed4a8126e2014-09-22 07:29:03 -0700483SkCanvas::SkCanvas(SkBaseDevice* device, const SkSurfaceProps* props, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700484 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700485 , fProps(SkSurfacePropsCopyOrDefault(props))
reedd9544982014-09-09 18:46:22 -0700486{
487 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700488
reedd9544982014-09-09 18:46:22 -0700489 this->init(device, flags);
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000490}
491
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000492SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000493 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700494 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000495{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000496 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700497
reedd9544982014-09-09 18:46:22 -0700498 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000499}
500
reed4a8126e2014-09-22 07:29:03 -0700501SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700502 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700503 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700504{
505 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700506
reed4a8126e2014-09-22 07:29:03 -0700507 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
508 this->init(device, kDefault_InitFlags);
509}
reed29c857d2014-09-21 10:25:07 -0700510
reed4a8126e2014-09-22 07:29:03 -0700511SkCanvas::SkCanvas(const SkBitmap& bitmap)
512 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
513 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
514{
515 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700516
reed4a8126e2014-09-22 07:29:03 -0700517 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
518 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519}
520
521SkCanvas::~SkCanvas() {
522 // free up the contents of our deque
523 this->restoreToCount(1); // restore everything but the last
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000524 SkASSERT(0 == fSaveLayerCount);
reed@google.com7c202932011-12-14 18:48:05 +0000525
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 this->internalRestore(); // restore the last, since we're going away
527
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000528 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000529
reed@android.com8a1c16f2008-12-17 15:59:43 +0000530 dec_canvas();
531}
532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533SkDrawFilter* SkCanvas::getDrawFilter() const {
534 return fMCRec->fFilter;
535}
536
537SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
538 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
539 return filter;
540}
541
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000542SkMetaData& SkCanvas::getMetaData() {
543 // metadata users are rare, so we lazily allocate it. If that changes we
544 // can decide to just make it a field in the device (rather than a ptr)
545 if (NULL == fMetaData) {
546 fMetaData = new SkMetaData;
547 }
548 return *fMetaData;
549}
550
reed@android.com8a1c16f2008-12-17 15:59:43 +0000551///////////////////////////////////////////////////////////////////////////////
552
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000553void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000554 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000555 if (device) {
556 device->flush();
557 }
558}
559
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000560SkISize SkCanvas::getTopLayerSize() const {
561 SkBaseDevice* d = this->getTopDevice();
562 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
563}
564
565SkIPoint SkCanvas::getTopLayerOrigin() const {
566 SkBaseDevice* d = this->getTopDevice();
567 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
568}
569
570SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000571 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000572 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
573}
574
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000575SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000576 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000577 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000578 SkASSERT(rec && rec->fLayer);
579 return rec->fLayer->fDevice;
580}
581
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000582SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000583 if (updateMatrixClip) {
584 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
585 }
reed@google.com9266fed2011-03-30 00:18:03 +0000586 return fMCRec->fTopLayer->fDevice;
587}
588
commit-bot@chromium.org403f8d72014-02-17 15:24:26 +0000589SkBaseDevice* SkCanvas::setRootDevice(SkBaseDevice* device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000590 // return root device
reed@google.com4c09d5c2011-02-22 13:16:38 +0000591 SkDeque::F2BIter iter(fMCStack);
592 MCRec* rec = (MCRec*)iter.next();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593 SkASSERT(rec && rec->fLayer);
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000594 SkBaseDevice* rootDevice = rec->fLayer->fDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595
596 if (rootDevice == device) {
597 return device;
598 }
reed@google.com4b226022011-01-11 18:32:13 +0000599
reed@android.com8a1c16f2008-12-17 15:59:43 +0000600 if (device) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000601 device->onAttachToCanvas(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000602 }
603 if (rootDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000604 rootDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605 }
606
607 SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
608 rootDevice = device;
reed4a8126e2014-09-22 07:29:03 -0700609 this->setupDevice(device);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610
611 fDeviceCMDirty = true;
reed@google.com4b226022011-01-11 18:32:13 +0000612
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 /* Now we update our initial region to have the bounds of the new device,
614 and then intersect all of the clips in our stack with these bounds,
615 to ensure that we can't draw outside of the device's bounds (and trash
616 memory).
reed@google.com4b226022011-01-11 18:32:13 +0000617
reed@android.com8a1c16f2008-12-17 15:59:43 +0000618 NOTE: this is only a partial-fix, since if the new device is larger than
619 the previous one, we don't know how to "enlarge" the clips in our stack,
reed@google.com4b226022011-01-11 18:32:13 +0000620 so drawing may be artificially restricted. Without keeping a history of
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621 all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
622 reconstruct the correct clips, so this approximation will have to do.
623 The caller really needs to restore() back to the base if they want to
624 accurately take advantage of the new device bounds.
625 */
626
reed@google.com42aea282012-03-28 16:19:15 +0000627 SkIRect bounds;
628 if (device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 bounds.set(0, 0, device->width(), device->height());
reed@google.com42aea282012-03-28 16:19:15 +0000630 } else {
631 bounds.setEmpty();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000632 }
reed@google.com42aea282012-03-28 16:19:15 +0000633 // now jam our 1st clip to be bounds, and intersect the rest with that
reed1f836ee2014-07-07 07:49:34 -0700634 rec->fRasterClip.setRect(bounds);
reed@google.com42aea282012-03-28 16:19:15 +0000635 while ((rec = (MCRec*)iter.next()) != NULL) {
reed1f836ee2014-07-07 07:49:34 -0700636 (void)rec->fRasterClip.op(bounds, SkRegion::kIntersect_Op);
reed@google.com42aea282012-03-28 16:19:15 +0000637 }
638
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 return device;
640}
641
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000642bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
643 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
644 return false;
645 }
646
647 bool weAllocated = false;
648 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700649 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000650 return false;
651 }
652 weAllocated = true;
653 }
654
655 SkBitmap bm(*bitmap);
656 bm.lockPixels();
657 if (bm.getPixels() && this->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y)) {
658 return true;
659 }
660
661 if (weAllocated) {
662 bitmap->setPixelRef(NULL);
663 }
664 return false;
665}
reed@google.com51df9e32010-12-23 19:29:18 +0000666
bsalomon@google.comc6980972011-11-02 19:57:21 +0000667bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000668 SkIRect r = srcRect;
669 const SkISize size = this->getBaseLayerSize();
670 if (!r.intersect(0, 0, size.width(), size.height())) {
671 bitmap->reset();
672 return false;
673 }
674
reed84825042014-09-02 12:50:45 -0700675 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000676 // bitmap will already be reset.
677 return false;
678 }
679 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
680 bitmap->reset();
681 return false;
682 }
683 return true;
684}
685
686bool SkCanvas::readPixels(const SkImageInfo& origInfo, void* dstP, size_t rowBytes, int x, int y) {
687 switch (origInfo.colorType()) {
688 case kUnknown_SkColorType:
689 case kIndex_8_SkColorType:
690 return false;
691 default:
692 break;
693 }
694 if (NULL == dstP || rowBytes < origInfo.minRowBytes()) {
695 return false;
696 }
697 if (0 == origInfo.width() || 0 == origInfo.height()) {
698 return false;
699 }
700
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000701 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000702 if (!device) {
703 return false;
704 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000705
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000706 const SkISize size = this->getBaseLayerSize();
707 SkIRect srcR = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
708 if (!srcR.intersect(0, 0, size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000709 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000710 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000711
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000712 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700713 const SkImageInfo info = origInfo.makeWH(srcR.width(), srcR.height());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000714
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000715 // if x or y are negative, then we have to adjust pixels
716 if (x > 0) {
717 x = 0;
reed@google.com51df9e32010-12-23 19:29:18 +0000718 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000719 if (y > 0) {
720 y = 0;
721 }
722 // here x,y are either 0 or negative
723 dstP = ((char*)dstP - y * rowBytes - x * info.bytesPerPixel());
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000724
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000725 // The device can assert that the requested area is always contained in its bounds
726 return device->readPixels(info, dstP, rowBytes, srcR.x(), srcR.y());
reed@google.com51df9e32010-12-23 19:29:18 +0000727}
728
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000729bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
730 if (bitmap.getTexture()) {
731 return false;
732 }
733 SkBitmap bm(bitmap);
734 bm.lockPixels();
735 if (bm.getPixels()) {
736 return this->writePixels(bm.info(), bm.getPixels(), bm.rowBytes(), x, y);
737 }
738 return false;
739}
740
741bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
742 int x, int y) {
743 switch (origInfo.colorType()) {
744 case kUnknown_SkColorType:
745 case kIndex_8_SkColorType:
746 return false;
747 default:
748 break;
749 }
750 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
751 return false;
752 }
753
754 const SkISize size = this->getBaseLayerSize();
755 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
756 if (!target.intersect(0, 0, size.width(), size.height())) {
757 return false;
758 }
759
760 SkBaseDevice* device = this->getDevice();
761 if (!device) {
762 return false;
763 }
764
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000765 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700766 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000767
768 // if x or y are negative, then we have to adjust pixels
769 if (x > 0) {
770 x = 0;
771 }
772 if (y > 0) {
773 y = 0;
774 }
775 // here x,y are either 0 or negative
776 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
777
reed4af35f32014-06-27 17:47:49 -0700778 // Tell our owning surface to bump its generation ID
779 this->predrawNotify();
780
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000781 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000782 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000783}
reed@google.com51df9e32010-12-23 19:29:18 +0000784
junov@google.com4370aed2012-01-18 16:21:08 +0000785SkCanvas* SkCanvas::canvasForDrawIter() {
786 return this;
787}
788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789//////////////////////////////////////////////////////////////////////////////
790
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791void SkCanvas::updateDeviceCMCache() {
792 if (fDeviceCMDirty) {
793 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700794 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000796
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797 if (NULL == layer->fNext) { // only one layer
reed@google.com46799cd2011-02-22 20:56:26 +0000798 layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000800 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 do {
reed@google.com46799cd2011-02-22 20:56:26 +0000802 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 } while ((layer = layer->fNext) != NULL);
804 }
805 fDeviceCMDirty = false;
806 }
807}
808
reed@android.com8a1c16f2008-12-17 15:59:43 +0000809///////////////////////////////////////////////////////////////////////////////
810
Florin Malita5f6102d2014-06-30 10:13:28 -0400811int SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812 int saveCount = this->getSaveCount(); // record this before the actual save
reed@google.com4b226022011-01-11 18:32:13 +0000813
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700815 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +0000816 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +0000817
Florin Malita5f6102d2014-06-30 10:13:28 -0400818 fClipStack.save();
reed@google.com5c3d1472011-02-22 19:12:23 +0000819
reed@android.com8a1c16f2008-12-17 15:59:43 +0000820 return saveCount;
821}
822
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000823int SkCanvas::save() {
Florin Malita5f6102d2014-06-30 10:13:28 -0400824 this->willSave();
825 return this->internalSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826}
827
reed@android.com8a1c16f2008-12-17 15:59:43 +0000828static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +0000829#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000830 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +0000831#else
832 return true;
833#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000834}
835
junov@chromium.orga907ac32012-02-24 21:54:07 +0000836bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000837 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000838 SkIRect clipBounds;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000839 SkRegion::Op op = SkRegion::kIntersect_Op;
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000840 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +0000841 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +0000842 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000843
844 if (imageFilter) {
reed1f836ee2014-07-07 07:49:34 -0700845 imageFilter->filterBounds(clipBounds, fMCRec->fMatrix, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000846 // Filters may grow the bounds beyond the device bounds.
847 op = SkRegion::kReplace_Op;
848 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000849 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -0700850 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000851 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +0000852
reed@android.com8a1c16f2008-12-17 15:59:43 +0000853 this->getTotalMatrix().mapRect(&r, *bounds);
854 r.roundOut(&ir);
855 // early exit if the layer's bounds are clipped out
856 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000857 if (bounds_affects_clip(flags)) {
reed1f836ee2014-07-07 07:49:34 -0700858 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +0000859 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000860 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000861 }
862 } else { // no user bounds, so just use the clip
863 ir = clipBounds;
864 }
865
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000866 if (bounds_affects_clip(flags)) {
867 fClipStack.clipDevRect(ir, op);
868 // early exit if the clip is now empty
reed1f836ee2014-07-07 07:49:34 -0700869 if (!fMCRec->fRasterClip.op(ir, op)) {
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +0000870 return false;
871 }
junov@chromium.orga907ac32012-02-24 21:54:07 +0000872 }
873
874 if (intersection) {
875 *intersection = ir;
876 }
877 return true;
878}
879
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000880int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
881 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
882 return this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, false, strategy);
883}
884
junov@chromium.orga907ac32012-02-24 21:54:07 +0000885int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
886 SaveFlags flags) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000887 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
888 return this->internalSaveLayer(bounds, paint, flags, false, strategy);
reed@google.com8926b162012-03-23 15:36:36 +0000889}
890
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000891int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
892 bool justForImageFilter, SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +0000893#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +0000894 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +0000895#endif
896
junov@chromium.orga907ac32012-02-24 21:54:07 +0000897 // do this before we create the layer. We don't call the public save() since
898 // that would invoke a possibly overridden virtual
Florin Malita5f6102d2014-06-30 10:13:28 -0400899 int count = this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +0000900
901 fDeviceCMDirty = true;
902
903 SkIRect ir;
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000904 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000905 return count;
906 }
907
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000908 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
909 // the clipRectBounds() call above?
910 if (kNoLayer_SaveLayerStrategy == strategy) {
911 return count;
912 }
913
reed@google.comb55deeb2012-01-06 14:43:09 +0000914 // Kill the imagefilter if our device doesn't allow it
915 SkLazyPaint lazyP;
916 if (paint && paint->getImageFilter()) {
917 if (!this->getTopDevice()->allowImageFilter(paint->getImageFilter())) {
reed@google.com8926b162012-03-23 15:36:36 +0000918 if (justForImageFilter) {
919 // early exit if the layer was just for the imageFilter
920 return count;
921 }
reed@google.comb55deeb2012-01-06 14:43:09 +0000922 SkPaint* p = lazyP.set(*paint);
923 p->setImageFilter(NULL);
924 paint = p;
925 }
926 }
927
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000928 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
929 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
930 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000931
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000932 SkBaseDevice* device;
reed@google.com76dd2772012-01-05 21:15:07 +0000933 if (paint && paint->getImageFilter()) {
reed52d9ac62014-06-30 09:05:34 -0700934 device = this->getDevice();
935 if (device) {
936 device = device->createCompatibleDevice(info);
937 }
reed@google.com76dd2772012-01-05 21:15:07 +0000938 } else {
commit-bot@chromium.org15a14052014-02-16 00:59:25 +0000939 device = this->createLayerDevice(info);
reed@google.com76dd2772012-01-05 21:15:07 +0000940 }
bungeman@google.come25c6842011-08-17 14:53:54 +0000941 if (NULL == device) {
942 SkDebugf("Unable to create device for layer.");
943 return count;
944 }
reed4a8126e2014-09-22 07:29:03 -0700945 this->setupDevice(device);
bsalomon@google.come97f0852011-06-17 13:10:25 +0000946
reed@google.com6f8f2922011-03-04 22:27:10 +0000947 device->setOrigin(ir.fLeft, ir.fTop);
reedd9544982014-09-09 18:46:22 -0700948 DeviceCM* layer = SkNEW_ARGS(DeviceCM,
949 (device, ir.fLeft, ir.fTop, paint, this, fConservativeRasterClip));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000950 device->unref();
951
952 layer->fNext = fMCRec->fTopLayer;
953 fMCRec->fLayer = layer;
954 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
955
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +0000956 fSaveLayerCount += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000957 return count;
958}
959
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +0000960int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
961 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
962}
963
reed@android.com8a1c16f2008-12-17 15:59:43 +0000964int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
965 SaveFlags flags) {
966 if (0xFF == alpha) {
967 return this->saveLayer(bounds, NULL, flags);
968 } else {
969 SkPaint tmpPaint;
970 tmpPaint.setAlpha(alpha);
971 return this->saveLayer(bounds, &tmpPaint, flags);
972 }
973}
974
975void SkCanvas::restore() {
976 // check for underflow
977 if (fMCStack.count() > 1) {
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000978 this->willRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 this->internalRestore();
mtklein6cfa73a2014-08-13 13:33:49 -0700980 this->didRestore();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 }
982}
983
984void SkCanvas::internalRestore() {
985 SkASSERT(fMCStack.count() != 0);
986
987 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +0000988 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989
Florin Malita5f6102d2014-06-30 10:13:28 -0400990 fClipStack.restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +0000991
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000992 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 DeviceCM* layer = fMCRec->fLayer; // may be null
994 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
995 fMCRec->fLayer = NULL;
996
997 // now do the normal restore()
998 fMCRec->~MCRec(); // balanced in save()
999 fMCStack.pop_back();
1000 fMCRec = (MCRec*)fMCStack.back();
1001
1002 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1003 since if we're being recorded, we don't want to record this (the
1004 recorder will have already recorded the restore).
1005 */
bsalomon49f085d2014-09-05 13:34:00 -07001006 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001008 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001009 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
1010 layer->fPaint);
1011 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001012 fDeviceCMDirty = true;
reed@google.com7c202932011-12-14 18:48:05 +00001013
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001014 SkASSERT(fSaveLayerCount > 0);
1015 fSaveLayerCount -= 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001016 }
1017 SkDELETE(layer);
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001018 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001019}
1020
1021int SkCanvas::getSaveCount() const {
1022 return fMCStack.count();
1023}
1024
1025void SkCanvas::restoreToCount(int count) {
1026 // sanity check
1027 if (count < 1) {
1028 count = 1;
1029 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001030
reed@google.comb9d1c6a2011-11-28 16:09:24 +00001031 int n = this->getSaveCount() - count;
1032 for (int i = 0; i < n; ++i) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001033 this->restore();
1034 }
1035}
1036
reed@google.com7c202932011-12-14 18:48:05 +00001037bool SkCanvas::isDrawingToLayer() const {
junov@chromium.orgb0a7ace2012-04-05 18:33:23 +00001038 return fSaveLayerCount > 0;
reed@google.com7c202932011-12-14 18:48:05 +00001039}
1040
reed4a8126e2014-09-22 07:29:03 -07001041SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1042 if (NULL == props) {
1043 props = &fProps;
1044 }
1045 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001046}
1047
reed4a8126e2014-09-22 07:29:03 -07001048SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001049 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001050 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001051}
1052
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001053SkImageInfo SkCanvas::imageInfo() const {
1054 SkBaseDevice* dev = this->getDevice();
1055 if (dev) {
1056 return dev->imageInfo();
1057 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001058 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001059 }
1060}
1061
1062const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
1063 return this->onPeekPixels(info, rowBytes);
1064}
1065
1066const void* SkCanvas::onPeekPixels(SkImageInfo* info, size_t* rowBytes) {
1067 SkBaseDevice* dev = this->getDevice();
1068 return dev ? dev->peekPixels(info, rowBytes) : NULL;
1069}
1070
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001071void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
1072 void* pixels = this->onAccessTopLayerPixels(info, rowBytes);
1073 if (pixels && origin) {
1074 *origin = this->getTopDevice(false)->getOrigin();
1075 }
1076 return pixels;
reed@google.com9c135db2014-03-12 18:28:35 +00001077}
1078
1079void* SkCanvas::onAccessTopLayerPixels(SkImageInfo* info, size_t* rowBytes) {
1080 SkBaseDevice* dev = this->getTopDevice();
1081 return dev ? dev->accessPixels(info, rowBytes) : NULL;
1082}
1083
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001084SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1085 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1086 if (NULL == fAddr) {
1087 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001088 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001089 return; // failure, fAddr is NULL
1090 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001091 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1092 return; // failure, fAddr is NULL
1093 }
1094 fAddr = fBitmap.getPixels();
1095 fRowBytes = fBitmap.rowBytes();
1096 }
1097 SkASSERT(fAddr); // success
1098}
1099
1100bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1101 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001102 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001103 } else {
1104 bitmap->reset();
1105 return false;
1106 }
1107}
1108
commit-bot@chromium.org210ae2a2014-02-27 17:40:13 +00001109void SkCanvas::onPushCull(const SkRect& cullRect) {
1110 // do nothing. Subclasses may do something
1111}
1112
1113void SkCanvas::onPopCull() {
1114 // do nothing. Subclasses may do something
1115}
1116
reed@android.com8a1c16f2008-12-17 15:59:43 +00001117/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org520cf8b2014-03-20 20:25:14 +00001118#ifdef SK_DEBUG
1119// Ensure that cull rects are monotonically nested in device space.
1120void SkCanvas::validateCull(const SkIRect& devCull) {
1121 if (fCullStack.isEmpty()
1122 || devCull.isEmpty()
1123 || fCullStack.top().contains(devCull)) {
1124 return;
1125 }
1126
1127 SkDEBUGF(("Invalid cull: [%d %d %d %d] (previous cull: [%d %d %d %d])\n",
1128 devCull.x(), devCull.y(), devCull.right(), devCull.bottom(),
1129 fCullStack.top().x(), fCullStack.top().y(),
1130 fCullStack.top().right(), fCullStack.top().bottom()));
1131
1132#ifdef ASSERT_NESTED_CULLING
1133 SkDEBUGFAIL("Invalid cull.");
1134#endif
1135}
1136#endif
1137
1138void SkCanvas::pushCull(const SkRect& cullRect) {
1139 ++fCullCount;
1140 this->onPushCull(cullRect);
1141
1142#ifdef SK_DEBUG
1143 // Map the cull rect into device space.
1144 SkRect mappedCull;
1145 this->getTotalMatrix().mapRect(&mappedCull, cullRect);
1146
1147 // Take clipping into account.
1148 SkIRect devClip, devCull;
1149 mappedCull.roundOut(&devCull);
1150 this->getClipDeviceBounds(&devClip);
1151 if (!devCull.intersect(devClip)) {
1152 devCull.setEmpty();
1153 }
1154
1155 this->validateCull(devCull);
1156 fCullStack.push(devCull); // balanced in popCull
1157#endif
1158}
1159
1160void SkCanvas::popCull() {
1161 SkASSERT(fCullStack.count() == fCullCount);
1162
1163 if (fCullCount > 0) {
1164 --fCullCount;
1165 this->onPopCull();
1166
1167 SkDEBUGCODE(fCullStack.pop());
1168 }
1169}
1170
1171/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001172
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001173void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001174 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001175 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001176 return;
1177 }
1178
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001179 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001180 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001181 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001182 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001183
1184 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001185
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001186 SkRect storage;
1187 const SkRect* bounds = NULL;
1188 if (paint && paint->canComputeFastBounds()) {
1189 bitmap.getBounds(&storage);
1190 matrix.mapRect(&storage);
1191 bounds = &paint->computeFastBounds(storage, &storage);
1192 }
1193
1194 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001195
1196 while (iter.next()) {
1197 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1198 }
1199
1200 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001201}
1202
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001203void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed@google.com8926b162012-03-23 15:36:36 +00001204 const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001205 SkPaint tmp;
1206 if (NULL == paint) {
1207 tmp.setDither(true);
1208 paint = &tmp;
1209 }
reed@google.com4b226022011-01-11 18:32:13 +00001210
reed@google.com8926b162012-03-23 15:36:36 +00001211 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001212 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001213 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001214 paint = &looper.paint();
1215 SkImageFilter* filter = paint->getImageFilter();
1216 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001217 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001218 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001219 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001220 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001221 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001222 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001223 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001224 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001225 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001226 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001227 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001228 SkPaint tmpUnfiltered(*paint);
1229 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001230 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1231 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001232 }
1233 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001234 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001235 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001236 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001237 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001238}
1239
reed@google.com8926b162012-03-23 15:36:36 +00001240void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1241 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001242 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001243 return;
1244 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001245 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001246
reed@google.com8926b162012-03-23 15:36:36 +00001247 SkPaint tmp;
1248 if (NULL == paint) {
1249 paint = &tmp;
1250 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001251
reed@google.com8926b162012-03-23 15:36:36 +00001252 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001253
reed@google.com8926b162012-03-23 15:36:36 +00001254 while (iter.next()) {
1255 paint = &looper.paint();
1256 SkImageFilter* filter = paint->getImageFilter();
1257 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1258 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001259 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001260 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001261 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001262 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001263 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001264 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001265 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001266 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001267 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001268 SkPaint tmpUnfiltered(*paint);
1269 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001270 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001271 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001272 }
1273 } else {
1274 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1275 }
1276 }
1277 LOOPER_END
1278}
1279
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001281void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001282 SkMatrix m;
1283 m.setTranslate(dx, dy);
1284 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285}
1286
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001287void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001288 SkMatrix m;
1289 m.setScale(sx, sy);
1290 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291}
1292
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001293void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001294 SkMatrix m;
1295 m.setRotate(degrees);
1296 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297}
1298
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001299void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001300 SkMatrix m;
1301 m.setSkew(sx, sy);
1302 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001303}
1304
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001305void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001306 if (matrix.isIdentity()) {
1307 return;
1308 }
1309
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001311 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001312 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001313
1314 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001315}
1316
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317void SkCanvas::setMatrix(const SkMatrix& matrix) {
1318 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001319 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001320 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001321 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322}
1323
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324void SkCanvas::resetMatrix() {
1325 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001326
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 matrix.reset();
1328 this->setMatrix(matrix);
1329}
1330
1331//////////////////////////////////////////////////////////////////////////////
1332
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001333void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001334 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1335 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001336}
1337
1338void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001339#ifdef SK_ENABLE_CLIP_QUICKREJECT
1340 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001341 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001342 return false;
1343 }
1344
reed@google.com3b3e8952012-08-16 20:53:31 +00001345 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001346 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001347 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001348
1349 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001350 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001351 }
1352 }
1353#endif
1354
reed@google.com5c3d1472011-02-22 19:12:23 +00001355 AutoValidateClip avc(this);
1356
reed@android.com8a1c16f2008-12-17 15:59:43 +00001357 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001358 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001359 if (!fAllowSoftClip) {
1360 edgeStyle = kHard_ClipEdgeStyle;
1361 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001362
reed1f836ee2014-07-07 07:49:34 -07001363 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001364 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001365 // the matrix. This means we don't have to a) make a path, and b) tell
1366 // the region code to scan-convert the path, only to discover that it
1367 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001369
reed1f836ee2014-07-07 07:49:34 -07001370 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001371 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001372 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001373 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001374 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001375 // and clip against that, since it can handle any matrix. However, to
1376 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1377 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001378 SkPath path;
1379
1380 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001381 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001382 }
1383}
1384
reed73e714e2014-09-04 09:02:23 -07001385static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1386 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001387 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001388}
1389
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001390void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001391 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001392 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001393 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1394 } else {
1395 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001396 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001397}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001398
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001399void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001400 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001401 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001402 AutoValidateClip avc(this);
1403
1404 fDeviceCMDirty = true;
1405 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001406 if (!fAllowSoftClip) {
1407 edgeStyle = kHard_ClipEdgeStyle;
1408 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001409
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001410 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001411
1412 SkPath devPath;
1413 devPath.addRRect(transformedRRect);
1414
reed73e714e2014-09-04 09:02:23 -07001415 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001416 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001417 }
1418
1419 SkPath path;
1420 path.addRRect(rrect);
1421 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001422 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001423}
1424
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001425void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001426 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1427 SkRect r;
1428 if (!path.isInverseFillType() && path.isRect(&r)) {
1429 this->onClipRect(r, op, edgeStyle);
1430 } else {
1431 this->onClipPath(path, op, edgeStyle);
1432 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001433}
1434
1435void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001436#ifdef SK_ENABLE_CLIP_QUICKREJECT
1437 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001438 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001439 return false;
1440 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001441
reed@google.com3b3e8952012-08-16 20:53:31 +00001442 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001443 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001444 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001445
reed@google.comda17f752012-08-16 18:27:05 +00001446 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001447 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001448 }
1449 }
1450#endif
1451
reed@google.com5c3d1472011-02-22 19:12:23 +00001452 AutoValidateClip avc(this);
1453
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001455 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001456 if (!fAllowSoftClip) {
1457 edgeStyle = kHard_ClipEdgeStyle;
1458 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001459
1460 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001461 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462
reed@google.comfe701122011-11-08 19:41:23 +00001463 // Check if the transfomation, or the original path itself
1464 // made us empty. Note this can also happen if we contained NaN
1465 // values. computing the bounds detects this, and will set our
1466 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1467 if (devPath.getBounds().isEmpty()) {
1468 // resetting the path will remove any NaN or other wanky values
1469 // that might upset our scan converter.
1470 devPath.reset();
1471 }
1472
reed@google.com5c3d1472011-02-22 19:12:23 +00001473 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001474 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001475
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001476 if (fAllowSimplifyClip) {
1477 devPath.reset();
1478 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1479 const SkClipStack* clipStack = getClipStack();
1480 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1481 const SkClipStack::Element* element;
1482 while ((element = iter.next())) {
1483 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001484 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001485 if (type != SkClipStack::Element::kEmpty_Type) {
1486 element->asPath(&operand);
1487 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001488 SkRegion::Op elementOp = element->getOp();
1489 if (elementOp == SkRegion::kReplace_Op) {
1490 devPath = operand;
1491 } else {
1492 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1493 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001494 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1495 // perhaps we need an API change to avoid this sort of mixed-signals about
1496 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001497 if (element->isAA()) {
1498 edgeStyle = kSoft_ClipEdgeStyle;
1499 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001500 }
1501 op = SkRegion::kReplace_Op;
1502 }
1503
reed73e714e2014-09-04 09:02:23 -07001504 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505}
1506
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001507void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001509}
1510
1511void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001512 AutoValidateClip avc(this);
1513
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001515 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001516
reed@google.com5c3d1472011-02-22 19:12:23 +00001517 // todo: signal fClipStack that we have a region, and therefore (I guess)
1518 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001519 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001520
reed1f836ee2014-07-07 07:49:34 -07001521 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522}
1523
reed@google.com819c9212011-02-23 18:56:55 +00001524#ifdef SK_DEBUG
1525void SkCanvas::validateClip() const {
1526 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001527 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001528 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001529 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001530 return;
1531 }
1532
reed@google.com819c9212011-02-23 18:56:55 +00001533 SkIRect ir;
1534 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001535 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001536
robertphillips@google.com80214e22012-07-20 15:33:18 +00001537 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001538 const SkClipStack::Element* element;
1539 while ((element = iter.next()) != NULL) {
1540 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001541 case SkClipStack::Element::kRect_Type:
1542 element->getRect().round(&ir);
1543 tmpClip.op(ir, element->getOp());
1544 break;
1545 case SkClipStack::Element::kEmpty_Type:
1546 tmpClip.setEmpty();
1547 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001548 default: {
1549 SkPath path;
1550 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001551 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001552 break;
1553 }
reed@google.com819c9212011-02-23 18:56:55 +00001554 }
1555 }
reed@google.com819c9212011-02-23 18:56:55 +00001556}
1557#endif
1558
reed@google.com90c07ea2012-04-13 13:50:27 +00001559void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001560 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001561 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001562
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001563 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001564 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001565 }
1566}
1567
reed@google.com5c3d1472011-02-22 19:12:23 +00001568///////////////////////////////////////////////////////////////////////////////
1569
reed@google.com754de5f2014-02-24 19:38:20 +00001570bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001571 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001572}
1573
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001574bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001575 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001576}
1577
reed@google.com3b3e8952012-08-16 20:53:31 +00001578bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001579 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001580 return true;
1581
reed1f836ee2014-07-07 07:49:34 -07001582 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001583 return true;
1584 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001585
reed1f836ee2014-07-07 07:49:34 -07001586 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001587 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001588 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001589 SkIRect idst;
1590 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001591 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001592 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001593 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001594
reed@android.coma380ae42009-07-21 01:17:02 +00001595 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001596 // TODO: should we use | instead, or compare all 4 at once?
1597 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001598 return true;
1599 }
reed@google.comc0784db2013-12-13 21:16:12 +00001600 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001601 return true;
1602 }
1603 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605}
1606
reed@google.com3b3e8952012-08-16 20:53:31 +00001607bool SkCanvas::quickReject(const SkPath& path) const {
1608 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609}
1610
reed@google.com3b3e8952012-08-16 20:53:31 +00001611bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001612 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001613 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614 return false;
1615 }
1616
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001617 SkMatrix inverse;
1618 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001619 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001620 if (bounds) {
1621 bounds->setEmpty();
1622 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001623 return false;
1624 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625
bsalomon49f085d2014-09-05 13:34:00 -07001626 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001627 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001628 // adjust it outwards in case we are antialiasing
1629 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001630
reed@google.com8f4d2302013-12-17 16:44:46 +00001631 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1632 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633 inverse.mapRect(bounds, r);
1634 }
1635 return true;
1636}
1637
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001638bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001639 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001640 if (clip.isEmpty()) {
1641 if (bounds) {
1642 bounds->setEmpty();
1643 }
1644 return false;
1645 }
1646
bsalomon49f085d2014-09-05 13:34:00 -07001647 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001648 *bounds = clip.getBounds();
1649 }
1650 return true;
1651}
1652
reed@android.com8a1c16f2008-12-17 15:59:43 +00001653const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001654 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001655}
1656
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001657const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001658 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001659}
1660
reed@google.com9c135db2014-03-12 18:28:35 +00001661GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1662 SkBaseDevice* dev = this->getTopDevice();
1663 return dev ? dev->accessRenderTarget() : NULL;
1664}
1665
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001666SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001667 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001668 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001669}
1670
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001671GrContext* SkCanvas::getGrContext() {
1672#if SK_SUPPORT_GPU
1673 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001674 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001675 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001676 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001677 return renderTarget->getContext();
1678 }
1679 }
1680#endif
1681
1682 return NULL;
1683
1684}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001685
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001686void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1687 const SkPaint& paint) {
1688 if (outer.isEmpty()) {
1689 return;
1690 }
1691 if (inner.isEmpty()) {
1692 this->drawRRect(outer, paint);
1693 return;
1694 }
1695
1696 // We don't have this method (yet), but technically this is what we should
1697 // be able to assert...
1698 // SkASSERT(outer.contains(inner));
1699 //
1700 // For now at least check for containment of bounds
1701 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1702
1703 this->onDrawDRRect(outer, inner, paint);
1704}
1705
reed@android.com8a1c16f2008-12-17 15:59:43 +00001706//////////////////////////////////////////////////////////////////////////////
1707// These are the virtual drawing methods
1708//////////////////////////////////////////////////////////////////////////////
1709
reed@google.com2a981812011-04-14 18:59:28 +00001710void SkCanvas::clear(SkColor color) {
1711 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001712 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001713 while (iter.next()) {
1714 iter.fDevice->clear(color);
1715 }
1716}
1717
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001718void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001719 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001720 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1721 }
1722}
1723
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001725 this->internalDrawPaint(paint);
1726}
1727
1728void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001729 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730
1731 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001732 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733 }
1734
reed@google.com4e2b3d32011-04-07 14:18:59 +00001735 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001736}
1737
1738void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1739 const SkPaint& paint) {
1740 if ((long)count <= 0) {
1741 return;
1742 }
1743
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001744 SkRect r, storage;
1745 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001746 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001747 // special-case 2 points (common for drawing a single line)
1748 if (2 == count) {
1749 r.set(pts[0], pts[1]);
1750 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001751 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001752 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001753 bounds = &paint.computeFastStrokeBounds(r, &storage);
1754 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001755 return;
1756 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001757 }
reed@google.coma584aed2012-05-16 14:06:02 +00001758
reed@android.com8a1c16f2008-12-17 15:59:43 +00001759 SkASSERT(pts != NULL);
1760
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001761 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001762
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001764 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765 }
reed@google.com4b226022011-01-11 18:32:13 +00001766
reed@google.com4e2b3d32011-04-07 14:18:59 +00001767 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768}
1769
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001770void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001771 SkRect storage;
1772 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001774 bounds = &paint.computeFastBounds(r, &storage);
1775 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776 return;
1777 }
1778 }
reed@google.com4b226022011-01-11 18:32:13 +00001779
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001780 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781
1782 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001783 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001784 }
1785
reed@google.com4e2b3d32011-04-07 14:18:59 +00001786 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001787}
1788
reed@google.com4ed0fb72012-12-12 20:48:18 +00001789void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001790 SkRect storage;
1791 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001792 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001793 bounds = &paint.computeFastBounds(oval, &storage);
1794 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001795 return;
1796 }
1797 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001798
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001799 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001800
1801 while (iter.next()) {
1802 iter.fDevice->drawOval(iter, oval, looper.paint());
1803 }
1804
1805 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001806}
1807
1808void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001809 SkRect storage;
1810 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001811 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001812 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1813 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001814 return;
1815 }
1816 }
1817
1818 if (rrect.isRect()) {
1819 // call the non-virtual version
1820 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001821 return;
1822 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001823 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001824 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1825 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001826 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001827
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001828 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001829
1830 while (iter.next()) {
1831 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1832 }
1833
1834 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001835}
1836
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001837void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1838 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001839 SkRect storage;
1840 const SkRect* bounds = NULL;
1841 if (paint.canComputeFastBounds()) {
1842 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1843 if (this->quickReject(*bounds)) {
1844 return;
1845 }
1846 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001847
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001848 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001849
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001850 while (iter.next()) {
1851 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1852 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001853
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001854 LOOPER_END
1855}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001856
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001857void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001858 if (!path.isFinite()) {
1859 return;
1860 }
1861
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001862 SkRect storage;
1863 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001864 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001865 const SkRect& pathBounds = path.getBounds();
1866 bounds = &paint.computeFastBounds(pathBounds, &storage);
1867 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001868 return;
1869 }
1870 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001871
1872 const SkRect& r = path.getBounds();
1873 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001874 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001875 this->internalDrawPaint(paint);
1876 }
1877 return;
1878 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001879
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001880 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001881
1882 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001883 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001884 }
1885
reed@google.com4e2b3d32011-04-07 14:18:59 +00001886 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001887}
1888
piotaixrb5fae932014-09-24 13:03:30 -07001889void SkCanvas::drawImage(const SkImage* image, SkScalar left, SkScalar top,
1890 const SkPaint* paint) {
1891 image->draw(this, left, top, paint);
1892}
1893
1894void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src,
1895 const SkRect& dst,
1896 const SkPaint* paint) {
1897 image->draw(this, src, dst, paint);
1898}
1899
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1901 const SkPaint* paint) {
1902 SkDEBUGCODE(bitmap.validate();)
1903
reed@google.com3d608122011-11-21 15:16:16 +00001904 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001905 SkRect bounds = {
1906 x, y,
1907 x + SkIntToScalar(bitmap.width()),
1908 y + SkIntToScalar(bitmap.height())
1909 };
1910 if (paint) {
1911 (void)paint->computeFastBounds(bounds, &bounds);
1912 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001913 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001914 return;
1915 }
1916 }
reed@google.com4b226022011-01-11 18:32:13 +00001917
reed@android.com8a1c16f2008-12-17 15:59:43 +00001918 SkMatrix matrix;
1919 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001920 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001921}
1922
reed@google.com9987ec32011-09-07 11:57:52 +00001923// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001924void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001925 const SkRect& dst, const SkPaint* paint,
1926 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001927 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 return;
1929 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001930
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001931 SkRect storage;
1932 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001933 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001934 if (paint) {
1935 bounds = &paint->computeFastBounds(dst, &storage);
1936 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001937 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001938 return;
1939 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001940 }
reed@google.com3d608122011-11-21 15:16:16 +00001941
reed@google.com33535f32012-09-25 15:37:50 +00001942 SkLazyPaint lazy;
1943 if (NULL == paint) {
1944 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001945 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001946
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001947 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001948
reed@google.com33535f32012-09-25 15:37:50 +00001949 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001950 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001951 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001952
reed@google.com33535f32012-09-25 15:37:50 +00001953 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001954}
1955
reed@google.com71121732012-09-18 15:14:33 +00001956void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001957 const SkRect& dst, const SkPaint* paint,
1958 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001959 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001960 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001961}
1962
reed@android.com8a1c16f2008-12-17 15:59:43 +00001963void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1964 const SkPaint* paint) {
1965 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001966 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001967}
1968
reed@google.com9987ec32011-09-07 11:57:52 +00001969void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1970 const SkIRect& center, const SkRect& dst,
1971 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001972 if (bitmap.drawsNothing()) {
1973 return;
1974 }
reed@google.com3d608122011-11-21 15:16:16 +00001975 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001976 SkRect storage;
1977 const SkRect* bounds = &dst;
1978 if (paint) {
1979 bounds = &paint->computeFastBounds(dst, &storage);
1980 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001981 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001982 return;
1983 }
1984 }
1985
reed@google.com9987ec32011-09-07 11:57:52 +00001986 const int32_t w = bitmap.width();
1987 const int32_t h = bitmap.height();
1988
1989 SkIRect c = center;
1990 // pin center to the bounds of the bitmap
1991 c.fLeft = SkMax32(0, center.fLeft);
1992 c.fTop = SkMax32(0, center.fTop);
1993 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1994 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1995
reed@google.com71121732012-09-18 15:14:33 +00001996 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001997 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001998 };
1999 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00002000 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002001 };
reed@google.com9987ec32011-09-07 11:57:52 +00002002 SkScalar dstX[4] = {
2003 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2004 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2005 };
2006 SkScalar dstY[4] = {
2007 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2008 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2009 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002010
reed@google.com9987ec32011-09-07 11:57:52 +00002011 if (dstX[1] > dstX[2]) {
2012 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2013 dstX[2] = dstX[1];
2014 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002015
reed@google.com9987ec32011-09-07 11:57:52 +00002016 if (dstY[1] > dstY[2]) {
2017 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2018 dstY[2] = dstY[1];
2019 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002020
reed@google.com9987ec32011-09-07 11:57:52 +00002021 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002022 SkRect s, d;
2023
reed@google.com9987ec32011-09-07 11:57:52 +00002024 s.fTop = srcY[y];
2025 s.fBottom = srcY[y+1];
2026 d.fTop = dstY[y];
2027 d.fBottom = dstY[y+1];
2028 for (int x = 0; x < 3; x++) {
2029 s.fLeft = srcX[x];
2030 s.fRight = srcX[x+1];
2031 d.fLeft = dstX[x];
2032 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002033 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002034 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002035 }
2036 }
2037}
2038
2039void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2040 const SkRect& dst, const SkPaint* paint) {
2041 SkDEBUGCODE(bitmap.validate();)
2042
2043 // Need a device entry-point, so gpu can use a mesh
2044 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2045}
2046
reed@google.comf67e4cf2011-03-15 20:56:58 +00002047class SkDeviceFilteredPaint {
2048public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002049 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2050 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002051 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002052 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002053 newPaint->setFlags(flags.fFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002054 fPaint = newPaint;
2055 } else {
2056 fPaint = &paint;
2057 }
2058 }
2059
reed@google.comf67e4cf2011-03-15 20:56:58 +00002060 const SkPaint& paint() const { return *fPaint; }
2061
2062private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002063 const SkPaint* fPaint;
2064 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002065};
2066
bungeman@google.com52c748b2011-08-22 21:30:43 +00002067void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2068 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002069 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002070 draw.fDevice->drawRect(draw, r, paint);
2071 } else {
2072 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002073 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002074 draw.fDevice->drawRect(draw, r, p);
2075 }
2076}
2077
2078void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2079 const char text[], size_t byteLength,
2080 SkScalar x, SkScalar y) {
2081 SkASSERT(byteLength == 0 || text != NULL);
2082
2083 // nothing to draw
2084 if (text == NULL || byteLength == 0 ||
2085 draw.fClip->isEmpty() ||
2086 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2087 return;
2088 }
2089
2090 SkScalar width = 0;
2091 SkPoint start;
2092
2093 start.set(0, 0); // to avoid warning
2094 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2095 SkPaint::kStrikeThruText_Flag)) {
2096 width = paint.measureText(text, byteLength);
2097
2098 SkScalar offsetX = 0;
2099 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2100 offsetX = SkScalarHalf(width);
2101 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2102 offsetX = width;
2103 }
2104 start.set(x - offsetX, y);
2105 }
2106
2107 if (0 == width) {
2108 return;
2109 }
2110
2111 uint32_t flags = paint.getFlags();
2112
2113 if (flags & (SkPaint::kUnderlineText_Flag |
2114 SkPaint::kStrikeThruText_Flag)) {
2115 SkScalar textSize = paint.getTextSize();
2116 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2117 SkRect r;
2118
2119 r.fLeft = start.fX;
2120 r.fRight = start.fX + width;
2121
2122 if (flags & SkPaint::kUnderlineText_Flag) {
2123 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2124 start.fY);
2125 r.fTop = offset;
2126 r.fBottom = offset + height;
2127 DrawRect(draw, paint, r, textSize);
2128 }
2129 if (flags & SkPaint::kStrikeThruText_Flag) {
2130 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2131 start.fY);
2132 r.fTop = offset;
2133 r.fBottom = offset + height;
2134 DrawRect(draw, paint, r, textSize);
2135 }
2136 }
2137}
2138
reed@google.come0d9ce82014-04-23 04:00:17 +00002139void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2140 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002141 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142
2143 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002144 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002145 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002146 DrawTextDecorations(iter, dfp.paint(),
2147 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002148 }
2149
reed@google.com4e2b3d32011-04-07 14:18:59 +00002150 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002151}
2152
reed@google.come0d9ce82014-04-23 04:00:17 +00002153void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2154 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002155 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002156
reed@android.com8a1c16f2008-12-17 15:59:43 +00002157 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002158 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002160 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002162
reed@google.com4e2b3d32011-04-07 14:18:59 +00002163 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164}
2165
reed@google.come0d9ce82014-04-23 04:00:17 +00002166void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2167 SkScalar constY, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002168 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002169
reed@android.com8a1c16f2008-12-17 15:59:43 +00002170 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002171 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002172 iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002173 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002175
reed@google.com4e2b3d32011-04-07 14:18:59 +00002176 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002177}
2178
reed@google.come0d9ce82014-04-23 04:00:17 +00002179void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2180 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002181 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002182
reed@android.com8a1c16f2008-12-17 15:59:43 +00002183 while (iter.next()) {
2184 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002185 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002186 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002187
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002188 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002189}
2190
fmalita00d5c2c2014-08-21 08:53:26 -07002191void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2192 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002193
2194 // FIXME: temporarily disable quickreject for empty bounds,
2195 // pending implicit blob bounds implementation.
2196 if (!blob->bounds().isEmpty() && paint.canComputeFastBounds()) {
2197 SkRect storage;
2198
2199 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2200 return;
2201 }
2202 }
2203
fmalitaaa1b9122014-08-28 14:32:24 -07002204 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002205
fmalitaaa1b9122014-08-28 14:32:24 -07002206 while (iter.next()) {
2207 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2208 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002209 }
2210
fmalitaaa1b9122014-08-28 14:32:24 -07002211 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002212}
2213
reed@google.come0d9ce82014-04-23 04:00:17 +00002214// These will become non-virtual, so they always call the (virtual) onDraw... method
2215void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2216 const SkPaint& paint) {
2217 this->onDrawText(text, byteLength, x, y, paint);
2218}
2219void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2220 const SkPaint& paint) {
2221 this->onDrawPosText(text, byteLength, pos, paint);
2222}
2223void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2224 SkScalar constY, const SkPaint& paint) {
2225 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2226}
2227void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2228 const SkMatrix* matrix, const SkPaint& paint) {
2229 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2230}
fmalita00d5c2c2014-08-21 08:53:26 -07002231void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2232 const SkPaint& paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002233 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002234 this->onDrawTextBlob(blob, x, y, paint);
2235 }
2236}
reed@google.come0d9ce82014-04-23 04:00:17 +00002237
reed@android.com8a1c16f2008-12-17 15:59:43 +00002238void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2239 const SkPoint verts[], const SkPoint texs[],
2240 const SkColor colors[], SkXfermode* xmode,
2241 const uint16_t indices[], int indexCount,
2242 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002243 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002244
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245 while (iter.next()) {
2246 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002247 colors, xmode, indices, indexCount,
2248 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249 }
reed@google.com4b226022011-01-11 18:32:13 +00002250
reed@google.com4e2b3d32011-04-07 14:18:59 +00002251 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252}
2253
dandovb3c9d1c2014-08-12 08:34:29 -07002254void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2255 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2256 if (NULL == cubics) {
2257 return;
2258 }
mtklein6cfa73a2014-08-13 13:33:49 -07002259
dandovecfff212014-08-04 10:02:00 -07002260 // Since a patch is always within the convex hull of the control points, we discard it when its
2261 // bounding rectangle is completely outside the current clip.
2262 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002263 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002264 if (this->quickReject(bounds)) {
2265 return;
2266 }
mtklein6cfa73a2014-08-13 13:33:49 -07002267
dandovb3c9d1c2014-08-12 08:34:29 -07002268 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2269}
2270
2271void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2272 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2273
dandovecfff212014-08-04 10:02:00 -07002274 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002275
dandovecfff212014-08-04 10:02:00 -07002276 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002277 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002278 }
mtklein6cfa73a2014-08-13 13:33:49 -07002279
dandovecfff212014-08-04 10:02:00 -07002280 LOOPER_END
2281}
2282
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283//////////////////////////////////////////////////////////////////////////////
2284// These methods are NOT virtual, and therefore must call back into virtual
2285// methods, rather than actually drawing themselves.
2286//////////////////////////////////////////////////////////////////////////////
2287
2288void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002289 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290 SkPaint paint;
2291
2292 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002293 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002294 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295 }
2296 this->drawPaint(paint);
2297}
2298
reed@android.com845fdac2009-06-23 03:01:32 +00002299void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002300 SkPaint paint;
2301
2302 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002303 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002304 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002305 }
2306 this->drawPaint(paint);
2307}
2308
2309void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2310 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002311
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312 pt.set(x, y);
2313 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2314}
2315
2316void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2317 SkPoint pt;
2318 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002319
reed@android.com8a1c16f2008-12-17 15:59:43 +00002320 pt.set(x, y);
2321 paint.setColor(color);
2322 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2323}
2324
2325void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2326 const SkPaint& paint) {
2327 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002328
reed@android.com8a1c16f2008-12-17 15:59:43 +00002329 pts[0].set(x0, y0);
2330 pts[1].set(x1, y1);
2331 this->drawPoints(kLines_PointMode, 2, pts, paint);
2332}
2333
2334void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2335 SkScalar right, SkScalar bottom,
2336 const SkPaint& paint) {
2337 SkRect r;
2338
2339 r.set(left, top, right, bottom);
2340 this->drawRect(r, paint);
2341}
2342
2343void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2344 const SkPaint& paint) {
2345 if (radius < 0) {
2346 radius = 0;
2347 }
2348
2349 SkRect r;
2350 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002351 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352}
2353
2354void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2355 const SkPaint& paint) {
2356 if (rx > 0 && ry > 0) {
2357 if (paint.canComputeFastBounds()) {
2358 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002359 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360 return;
2361 }
2362 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002363 SkRRect rrect;
2364 rrect.setRectXY(r, rx, ry);
2365 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002366 } else {
2367 this->drawRect(r, paint);
2368 }
2369}
2370
reed@android.com8a1c16f2008-12-17 15:59:43 +00002371void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2372 SkScalar sweepAngle, bool useCenter,
2373 const SkPaint& paint) {
2374 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2375 this->drawOval(oval, paint);
2376 } else {
2377 SkPath path;
2378 if (useCenter) {
2379 path.moveTo(oval.centerX(), oval.centerY());
2380 }
2381 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2382 if (useCenter) {
2383 path.close();
2384 }
2385 this->drawPath(path, paint);
2386 }
2387}
2388
2389void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2390 const SkPath& path, SkScalar hOffset,
2391 SkScalar vOffset, const SkPaint& paint) {
2392 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002393
reed@android.com8a1c16f2008-12-17 15:59:43 +00002394 matrix.setTranslate(hOffset, vOffset);
2395 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2396}
2397
reed@android.comf76bacf2009-05-13 14:00:33 +00002398///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002399void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002400 SkBaseDevice* device = this->getDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002401 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002402 device->EXPERIMENTAL_optimize(picture);
2403 }
2404}
reed@android.comf76bacf2009-05-13 14:00:33 +00002405
robertphillips9b14f262014-06-04 05:40:44 -07002406void SkCanvas::drawPicture(const SkPicture* picture) {
bsalomon49f085d2014-09-05 13:34:00 -07002407 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002408 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002409 }
2410}
2411
reedd5fa1a42014-08-09 11:08:05 -07002412void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002413 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002414 if (matrix && matrix->isIdentity()) {
2415 matrix = NULL;
2416 }
2417 this->onDrawPicture(picture, matrix, paint);
2418 }
2419}
robertphillips9b14f262014-06-04 05:40:44 -07002420
reedd5fa1a42014-08-09 11:08:05 -07002421void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2422 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002423 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002424 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002425 // Canvas has to first give the device the opportunity to render
2426 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002427 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002428 return; // the device has rendered the entire picture
2429 }
2430 }
2431
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002432 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002433
robertphillipsc5ba71d2014-09-04 08:42:50 -07002434 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002435}
2436
reed@android.com8a1c16f2008-12-17 15:59:43 +00002437///////////////////////////////////////////////////////////////////////////////
2438///////////////////////////////////////////////////////////////////////////////
2439
2440SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002441 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442
2443 SkASSERT(canvas);
2444
2445 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2446 fDone = !fImpl->next();
2447}
2448
2449SkCanvas::LayerIter::~LayerIter() {
2450 fImpl->~SkDrawIter();
2451}
2452
2453void SkCanvas::LayerIter::next() {
2454 fDone = !fImpl->next();
2455}
2456
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002457SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002458 return fImpl->getDevice();
2459}
2460
2461const SkMatrix& SkCanvas::LayerIter::matrix() const {
2462 return fImpl->getMatrix();
2463}
2464
2465const SkPaint& SkCanvas::LayerIter::paint() const {
2466 const SkPaint* paint = fImpl->getPaint();
2467 if (NULL == paint) {
2468 paint = &fDefaultPaint;
2469 }
2470 return *paint;
2471}
2472
2473const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2474int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2475int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002476
2477///////////////////////////////////////////////////////////////////////////////
2478
fmalitac3b589a2014-06-05 12:40:07 -07002479SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002480
2481///////////////////////////////////////////////////////////////////////////////
2482
2483static bool supported_for_raster_canvas(const SkImageInfo& info) {
2484 switch (info.alphaType()) {
2485 case kPremul_SkAlphaType:
2486 case kOpaque_SkAlphaType:
2487 break;
2488 default:
2489 return false;
2490 }
2491
2492 switch (info.colorType()) {
2493 case kAlpha_8_SkColorType:
2494 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002495 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002496 break;
2497 default:
2498 return false;
2499 }
2500
2501 return true;
2502}
2503
2504SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2505 if (!supported_for_raster_canvas(info)) {
2506 return NULL;
2507 }
2508
2509 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -07002510 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002511 return NULL;
2512 }
2513
2514 // should this functionality be moved into allocPixels()?
2515 if (!bitmap.info().isOpaque()) {
2516 bitmap.eraseColor(0);
2517 }
2518 return SkNEW_ARGS(SkCanvas, (bitmap));
2519}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002520
2521SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2522 if (!supported_for_raster_canvas(info)) {
2523 return NULL;
2524 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002525
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002526 SkBitmap bitmap;
2527 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2528 return NULL;
2529 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002530 return SkNEW_ARGS(SkCanvas, (bitmap));
2531}
reedd5fa1a42014-08-09 11:08:05 -07002532
2533///////////////////////////////////////////////////////////////////////////////
2534
2535SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002536 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002537 : fCanvas(canvas)
2538 , fSaveCount(canvas->getSaveCount())
2539{
bsalomon49f085d2014-09-05 13:34:00 -07002540 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002541 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002542 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002543 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002544 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002545 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002546 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002547 canvas->save();
2548 }
mtklein6cfa73a2014-08-13 13:33:49 -07002549
bsalomon49f085d2014-09-05 13:34:00 -07002550 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002551 canvas->concat(*matrix);
2552 }
2553}
2554
2555SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2556 fCanvas->restoreToCount(fSaveCount);
2557}