blob: 709cb4c0be3ece59c86a5873c28d12b8c1f12a37 [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) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001207 paint = &tmp;
1208 }
reed@google.com4b226022011-01-11 18:32:13 +00001209
reed@google.com8926b162012-03-23 15:36:36 +00001210 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001211 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001212 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001213 paint = &looper.paint();
1214 SkImageFilter* filter = paint->getImageFilter();
1215 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001216 if (filter && !dstDev->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001217 SkDeviceImageFilterProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001218 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001219 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001220 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001221 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001222 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001223 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001224 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001225 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001226 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001227 SkPaint tmpUnfiltered(*paint);
1228 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001229 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1230 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001231 }
1232 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001233 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001234 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001235 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001236 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001237}
1238
reed@google.com8926b162012-03-23 15:36:36 +00001239void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
1240 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001241 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001242 return;
1243 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001244 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001245
reed@google.com8926b162012-03-23 15:36:36 +00001246 SkPaint tmp;
1247 if (NULL == paint) {
1248 paint = &tmp;
1249 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001250
reed@google.com8926b162012-03-23 15:36:36 +00001251 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001252
reed@google.com8926b162012-03-23 15:36:36 +00001253 while (iter.next()) {
1254 paint = &looper.paint();
1255 SkImageFilter* filter = paint->getImageFilter();
1256 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1257 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
senorblanco@chromium.org9c397442012-09-27 21:57:45 +00001258 SkDeviceImageFilterProxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001259 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001260 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001261 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001262 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001263 SkIRect clipBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
senorblancobe129b22014-08-08 07:14:35 -07001264 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001265 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001266 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001267 SkPaint tmpUnfiltered(*paint);
1268 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001269 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001270 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001271 }
1272 } else {
1273 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1274 }
1275 }
1276 LOOPER_END
1277}
1278
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001280void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001281 SkMatrix m;
1282 m.setTranslate(dx, dy);
1283 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001284}
1285
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001286void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001287 SkMatrix m;
1288 m.setScale(sx, sy);
1289 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001290}
1291
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001292void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001293 SkMatrix m;
1294 m.setRotate(degrees);
1295 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296}
1297
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001298void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001299 SkMatrix m;
1300 m.setSkew(sx, sy);
1301 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001302}
1303
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001304void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001305 if (matrix.isIdentity()) {
1306 return;
1307 }
1308
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001310 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001311 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001312
1313 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001314}
1315
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316void SkCanvas::setMatrix(const SkMatrix& matrix) {
1317 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001318 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001319 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001320 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321}
1322
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323void SkCanvas::resetMatrix() {
1324 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001325
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 matrix.reset();
1327 this->setMatrix(matrix);
1328}
1329
1330//////////////////////////////////////////////////////////////////////////////
1331
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001332void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001333 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1334 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001335}
1336
1337void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001338#ifdef SK_ENABLE_CLIP_QUICKREJECT
1339 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001340 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001341 return false;
1342 }
1343
reed@google.com3b3e8952012-08-16 20:53:31 +00001344 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001345 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001346 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001347
1348 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001349 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001350 }
1351 }
1352#endif
1353
reed@google.com5c3d1472011-02-22 19:12:23 +00001354 AutoValidateClip avc(this);
1355
reed@android.com8a1c16f2008-12-17 15:59:43 +00001356 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001357 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001358 if (!fAllowSoftClip) {
1359 edgeStyle = kHard_ClipEdgeStyle;
1360 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001361
reed1f836ee2014-07-07 07:49:34 -07001362 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001363 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001364 // the matrix. This means we don't have to a) make a path, and b) tell
1365 // the region code to scan-convert the path, only to discover that it
1366 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001367 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001368
reed1f836ee2014-07-07 07:49:34 -07001369 fMCRec->fMatrix.mapRect(&r, rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001370 fClipStack.clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001371 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001372 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001373 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001374 // and clip against that, since it can handle any matrix. However, to
1375 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1376 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001377 SkPath path;
1378
1379 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001380 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001381 }
1382}
1383
reed73e714e2014-09-04 09:02:23 -07001384static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1385 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001386 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001387}
1388
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001389void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001390 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001391 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001392 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1393 } else {
1394 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001395 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001396}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001397
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001398void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001399 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001400 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001401 AutoValidateClip avc(this);
1402
1403 fDeviceCMDirty = true;
1404 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001405 if (!fAllowSoftClip) {
1406 edgeStyle = kHard_ClipEdgeStyle;
1407 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001408
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001409 fClipStack.clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001410
1411 SkPath devPath;
1412 devPath.addRRect(transformedRRect);
1413
reed73e714e2014-09-04 09:02:23 -07001414 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001415 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001416 }
1417
1418 SkPath path;
1419 path.addRRect(rrect);
1420 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001421 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001422}
1423
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001424void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001425 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1426 SkRect r;
1427 if (!path.isInverseFillType() && path.isRect(&r)) {
1428 this->onClipRect(r, op, edgeStyle);
1429 } else {
1430 this->onClipPath(path, op, edgeStyle);
1431 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001432}
1433
1434void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001435#ifdef SK_ENABLE_CLIP_QUICKREJECT
1436 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001437 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001438 return false;
1439 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001440
reed@google.com3b3e8952012-08-16 20:53:31 +00001441 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001442 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001443 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001444
reed@google.comda17f752012-08-16 18:27:05 +00001445 fClipStack.clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001446 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001447 }
1448 }
1449#endif
1450
reed@google.com5c3d1472011-02-22 19:12:23 +00001451 AutoValidateClip avc(this);
1452
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001454 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001455 if (!fAllowSoftClip) {
1456 edgeStyle = kHard_ClipEdgeStyle;
1457 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458
1459 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001460 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001461
reed@google.comfe701122011-11-08 19:41:23 +00001462 // Check if the transfomation, or the original path itself
1463 // made us empty. Note this can also happen if we contained NaN
1464 // values. computing the bounds detects this, and will set our
1465 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1466 if (devPath.getBounds().isEmpty()) {
1467 // resetting the path will remove any NaN or other wanky values
1468 // that might upset our scan converter.
1469 devPath.reset();
1470 }
1471
reed@google.com5c3d1472011-02-22 19:12:23 +00001472 // if we called path.swap() we could avoid a deep copy of this path
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001473 fClipStack.clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001474
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001475 if (fAllowSimplifyClip) {
1476 devPath.reset();
1477 devPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1478 const SkClipStack* clipStack = getClipStack();
1479 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
1480 const SkClipStack::Element* element;
1481 while ((element = iter.next())) {
1482 SkClipStack::Element::Type type = element->getType();
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001483 SkPath operand;
commit-bot@chromium.org2a67e122014-05-19 13:53:10 +00001484 if (type != SkClipStack::Element::kEmpty_Type) {
1485 element->asPath(&operand);
1486 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001487 SkRegion::Op elementOp = element->getOp();
1488 if (elementOp == SkRegion::kReplace_Op) {
1489 devPath = operand;
1490 } else {
1491 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
1492 }
caryclark@google.com96fd3442013-05-07 19:48:31 +00001493 // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
1494 // perhaps we need an API change to avoid this sort of mixed-signals about
1495 // clipping.
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001496 if (element->isAA()) {
1497 edgeStyle = kSoft_ClipEdgeStyle;
1498 }
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001499 }
1500 op = SkRegion::kReplace_Op;
1501 }
1502
reed73e714e2014-09-04 09:02:23 -07001503 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504}
1505
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001506void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001507 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001508}
1509
1510void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001511 AutoValidateClip avc(this);
1512
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001514 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515
reed@google.com5c3d1472011-02-22 19:12:23 +00001516 // todo: signal fClipStack that we have a region, and therefore (I guess)
1517 // we have to ignore it, and use the region directly?
reed@google.com115d9312012-05-16 18:50:40 +00001518 fClipStack.clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001519
reed1f836ee2014-07-07 07:49:34 -07001520 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001521}
1522
reed@google.com819c9212011-02-23 18:56:55 +00001523#ifdef SK_DEBUG
1524void SkCanvas::validateClip() const {
1525 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001526 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001527 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001528 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001529 return;
1530 }
1531
reed@google.com819c9212011-02-23 18:56:55 +00001532 SkIRect ir;
1533 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001534 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001535
robertphillips@google.com80214e22012-07-20 15:33:18 +00001536 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001537 const SkClipStack::Element* element;
1538 while ((element = iter.next()) != NULL) {
1539 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001540 case SkClipStack::Element::kRect_Type:
1541 element->getRect().round(&ir);
1542 tmpClip.op(ir, element->getOp());
1543 break;
1544 case SkClipStack::Element::kEmpty_Type:
1545 tmpClip.setEmpty();
1546 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001547 default: {
1548 SkPath path;
1549 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001550 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001551 break;
1552 }
reed@google.com819c9212011-02-23 18:56:55 +00001553 }
1554 }
reed@google.com819c9212011-02-23 18:56:55 +00001555}
1556#endif
1557
reed@google.com90c07ea2012-04-13 13:50:27 +00001558void SkCanvas::replayClips(ClipVisitor* visitor) const {
robertphillips@google.com80214e22012-07-20 15:33:18 +00001559 SkClipStack::B2TIter iter(fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001560 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001561
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001562 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001563 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001564 }
1565}
1566
reed@google.com5c3d1472011-02-22 19:12:23 +00001567///////////////////////////////////////////////////////////////////////////////
1568
reed@google.com754de5f2014-02-24 19:38:20 +00001569bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001570 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001571}
1572
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001573bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001574 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001575}
1576
reed@google.com3b3e8952012-08-16 20:53:31 +00001577bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001578 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001579 return true;
1580
reed1f836ee2014-07-07 07:49:34 -07001581 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001582 return true;
1583 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584
reed1f836ee2014-07-07 07:49:34 -07001585 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001586 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001587 fMCRec->fMatrix.mapRect(&dst, rect);
reed@android.coma380ae42009-07-21 01:17:02 +00001588 SkIRect idst;
1589 dst.roundOut(&idst);
reed1f836ee2014-07-07 07:49:34 -07001590 return !SkIRect::Intersects(idst, fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001591 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001592 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001593
reed@android.coma380ae42009-07-21 01:17:02 +00001594 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001595 // TODO: should we use | instead, or compare all 4 at once?
1596 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001597 return true;
1598 }
reed@google.comc0784db2013-12-13 21:16:12 +00001599 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001600 return true;
1601 }
1602 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604}
1605
reed@google.com3b3e8952012-08-16 20:53:31 +00001606bool SkCanvas::quickReject(const SkPath& path) const {
1607 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608}
1609
reed@google.com3b3e8952012-08-16 20:53:31 +00001610bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001611 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001612 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 return false;
1614 }
1615
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001616 SkMatrix inverse;
1617 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001618 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001619 if (bounds) {
1620 bounds->setEmpty();
1621 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001622 return false;
1623 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624
bsalomon49f085d2014-09-05 13:34:00 -07001625 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001626 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001627 // adjust it outwards in case we are antialiasing
1628 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001629
reed@google.com8f4d2302013-12-17 16:44:46 +00001630 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1631 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 inverse.mapRect(bounds, r);
1633 }
1634 return true;
1635}
1636
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001637bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001638 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001639 if (clip.isEmpty()) {
1640 if (bounds) {
1641 bounds->setEmpty();
1642 }
1643 return false;
1644 }
1645
bsalomon49f085d2014-09-05 13:34:00 -07001646 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001647 *bounds = clip.getBounds();
1648 }
1649 return true;
1650}
1651
reed@android.com8a1c16f2008-12-17 15:59:43 +00001652const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001653 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001654}
1655
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001656const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001657 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001658}
1659
reed@google.com9c135db2014-03-12 18:28:35 +00001660GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1661 SkBaseDevice* dev = this->getTopDevice();
1662 return dev ? dev->accessRenderTarget() : NULL;
1663}
1664
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001665SkBaseDevice* SkCanvas::createLayerDevice(const SkImageInfo& info) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001666 SkBaseDevice* device = this->getTopDevice();
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001667 return device ? device->createCompatibleDeviceForSaveLayer(info) : NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001668}
1669
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001670GrContext* SkCanvas::getGrContext() {
1671#if SK_SUPPORT_GPU
1672 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001673 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001674 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001675 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001676 return renderTarget->getContext();
1677 }
1678 }
1679#endif
1680
1681 return NULL;
1682
1683}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001684
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001685void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1686 const SkPaint& paint) {
1687 if (outer.isEmpty()) {
1688 return;
1689 }
1690 if (inner.isEmpty()) {
1691 this->drawRRect(outer, paint);
1692 return;
1693 }
1694
1695 // We don't have this method (yet), but technically this is what we should
1696 // be able to assert...
1697 // SkASSERT(outer.contains(inner));
1698 //
1699 // For now at least check for containment of bounds
1700 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1701
1702 this->onDrawDRRect(outer, inner, paint);
1703}
1704
reed@android.com8a1c16f2008-12-17 15:59:43 +00001705//////////////////////////////////////////////////////////////////////////////
1706// These are the virtual drawing methods
1707//////////////////////////////////////////////////////////////////////////////
1708
reed@google.com2a981812011-04-14 18:59:28 +00001709void SkCanvas::clear(SkColor color) {
1710 SkDrawIter iter(this);
junov@chromium.org995beb62013-03-28 13:49:22 +00001711 this->predrawNotify();
reed@google.com2a981812011-04-14 18:59:28 +00001712 while (iter.next()) {
1713 iter.fDevice->clear(color);
1714 }
1715}
1716
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001717void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001718 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001719 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1720 }
1721}
1722
reed@android.com8a1c16f2008-12-17 15:59:43 +00001723void SkCanvas::drawPaint(const SkPaint& paint) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001724 this->internalDrawPaint(paint);
1725}
1726
1727void SkCanvas::internalDrawPaint(const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001728 LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729
1730 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001731 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001732 }
1733
reed@google.com4e2b3d32011-04-07 14:18:59 +00001734 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735}
1736
1737void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
1738 const SkPaint& paint) {
1739 if ((long)count <= 0) {
1740 return;
1741 }
1742
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001743 SkRect r, storage;
1744 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00001745 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00001746 // special-case 2 points (common for drawing a single line)
1747 if (2 == count) {
1748 r.set(pts[0], pts[1]);
1749 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00001750 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00001751 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001752 bounds = &paint.computeFastStrokeBounds(r, &storage);
1753 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00001754 return;
1755 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001756 }
reed@google.coma584aed2012-05-16 14:06:02 +00001757
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758 SkASSERT(pts != NULL);
1759
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001760 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00001761
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001763 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764 }
reed@google.com4b226022011-01-11 18:32:13 +00001765
reed@google.com4e2b3d32011-04-07 14:18:59 +00001766 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767}
1768
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001769void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001770 SkRect storage;
1771 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001773 bounds = &paint.computeFastBounds(r, &storage);
1774 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 return;
1776 }
1777 }
reed@google.com4b226022011-01-11 18:32:13 +00001778
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001779 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780
1781 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001782 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001783 }
1784
reed@google.com4e2b3d32011-04-07 14:18:59 +00001785 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001786}
1787
reed@google.com4ed0fb72012-12-12 20:48:18 +00001788void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001789 SkRect storage;
1790 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001791 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001792 bounds = &paint.computeFastBounds(oval, &storage);
1793 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001794 return;
1795 }
1796 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00001797
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001798 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00001799
1800 while (iter.next()) {
1801 iter.fDevice->drawOval(iter, oval, looper.paint());
1802 }
1803
1804 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001805}
1806
1807void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001808 SkRect storage;
1809 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001810 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001811 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
1812 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001813 return;
1814 }
1815 }
1816
1817 if (rrect.isRect()) {
1818 // call the non-virtual version
1819 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001820 return;
1821 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00001822 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001823 this->SkCanvas::drawOval(rrect.getBounds(), paint);
1824 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001825 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001826
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001827 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00001828
1829 while (iter.next()) {
1830 iter.fDevice->drawRRect(iter, rrect, looper.paint());
1831 }
1832
1833 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00001834}
1835
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001836void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
1837 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001838 SkRect storage;
1839 const SkRect* bounds = NULL;
1840 if (paint.canComputeFastBounds()) {
1841 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
1842 if (this->quickReject(*bounds)) {
1843 return;
1844 }
1845 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001846
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001847 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001848
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001849 while (iter.next()) {
1850 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
1851 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00001852
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001853 LOOPER_END
1854}
reed@google.com4ed0fb72012-12-12 20:48:18 +00001855
bsalomon@google.com7ce564c2013-10-22 16:54:15 +00001856void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
reed@google.com93645112012-07-26 16:11:47 +00001857 if (!path.isFinite()) {
1858 return;
1859 }
1860
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001861 SkRect storage;
1862 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001863 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001864 const SkRect& pathBounds = path.getBounds();
1865 bounds = &paint.computeFastBounds(pathBounds, &storage);
1866 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001867 return;
1868 }
1869 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00001870
1871 const SkRect& r = path.getBounds();
1872 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00001873 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001874 this->internalDrawPaint(paint);
1875 }
1876 return;
1877 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001878
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001879 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001880
1881 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00001882 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001883 }
1884
reed@google.com4e2b3d32011-04-07 14:18:59 +00001885 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001886}
1887
piotaixrb5fae932014-09-24 13:03:30 -07001888void SkCanvas::drawImage(const SkImage* image, SkScalar left, SkScalar top,
1889 const SkPaint* paint) {
1890 image->draw(this, left, top, paint);
1891}
1892
1893void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src,
1894 const SkRect& dst,
1895 const SkPaint* paint) {
piotaixr5ceff912014-09-26 07:36:26 -07001896 image->drawRect(this, src, dst, paint);
piotaixrb5fae932014-09-24 13:03:30 -07001897}
1898
reed@android.com8a1c16f2008-12-17 15:59:43 +00001899void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
1900 const SkPaint* paint) {
1901 SkDEBUGCODE(bitmap.validate();)
1902
reed@google.com3d608122011-11-21 15:16:16 +00001903 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001904 SkRect bounds = {
1905 x, y,
1906 x + SkIntToScalar(bitmap.width()),
1907 y + SkIntToScalar(bitmap.height())
1908 };
1909 if (paint) {
1910 (void)paint->computeFastBounds(bounds, &bounds);
1911 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001912 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001913 return;
1914 }
1915 }
reed@google.com4b226022011-01-11 18:32:13 +00001916
reed@android.com8a1c16f2008-12-17 15:59:43 +00001917 SkMatrix matrix;
1918 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001919 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920}
1921
reed@google.com9987ec32011-09-07 11:57:52 +00001922// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00001923void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001924 const SkRect& dst, const SkPaint* paint,
1925 DrawBitmapRectFlags flags) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001926 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001927 return;
1928 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00001929
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001930 SkRect storage;
1931 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00001932 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00001933 if (paint) {
1934 bounds = &paint->computeFastBounds(dst, &storage);
1935 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001936 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001937 return;
1938 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001939 }
reed@google.com3d608122011-11-21 15:16:16 +00001940
reed@google.com33535f32012-09-25 15:37:50 +00001941 SkLazyPaint lazy;
1942 if (NULL == paint) {
1943 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001944 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001945
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001946 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001947
reed@google.com33535f32012-09-25 15:37:50 +00001948 while (iter.next()) {
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001949 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), flags);
reed@android.comf2b98d62010-12-20 18:26:13 +00001950 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00001951
reed@google.com33535f32012-09-25 15:37:50 +00001952 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001953}
1954
reed@google.com71121732012-09-18 15:14:33 +00001955void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001956 const SkRect& dst, const SkPaint* paint,
1957 DrawBitmapRectFlags flags) {
reed@google.com9987ec32011-09-07 11:57:52 +00001958 SkDEBUGCODE(bitmap.validate();)
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00001959 this->internalDrawBitmapRect(bitmap, src, dst, paint, flags);
reed@google.com9987ec32011-09-07 11:57:52 +00001960}
1961
reed@android.com8a1c16f2008-12-17 15:59:43 +00001962void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
1963 const SkPaint* paint) {
1964 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001965 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001966}
1967
reed@google.com9987ec32011-09-07 11:57:52 +00001968void SkCanvas::internalDrawBitmapNine(const SkBitmap& bitmap,
1969 const SkIRect& center, const SkRect& dst,
1970 const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001971 if (bitmap.drawsNothing()) {
1972 return;
1973 }
reed@google.com3d608122011-11-21 15:16:16 +00001974 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00001975 SkRect storage;
1976 const SkRect* bounds = &dst;
1977 if (paint) {
1978 bounds = &paint->computeFastBounds(dst, &storage);
1979 }
reed@google.com3b3e8952012-08-16 20:53:31 +00001980 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00001981 return;
1982 }
1983 }
1984
reed@google.com9987ec32011-09-07 11:57:52 +00001985 const int32_t w = bitmap.width();
1986 const int32_t h = bitmap.height();
1987
1988 SkIRect c = center;
1989 // pin center to the bounds of the bitmap
1990 c.fLeft = SkMax32(0, center.fLeft);
1991 c.fTop = SkMax32(0, center.fTop);
1992 c.fRight = SkPin32(center.fRight, c.fLeft, w);
1993 c.fBottom = SkPin32(center.fBottom, c.fTop, h);
1994
reed@google.com71121732012-09-18 15:14:33 +00001995 const SkScalar srcX[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001996 0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
reed@google.com71121732012-09-18 15:14:33 +00001997 };
1998 const SkScalar srcY[4] = {
rmistry@google.com7d474f82013-01-02 22:03:54 +00001999 0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
reed@google.com71121732012-09-18 15:14:33 +00002000 };
reed@google.com9987ec32011-09-07 11:57:52 +00002001 SkScalar dstX[4] = {
2002 dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
2003 dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
2004 };
2005 SkScalar dstY[4] = {
2006 dst.fTop, dst.fTop + SkIntToScalar(c.fTop),
2007 dst.fBottom - SkIntToScalar(h - c.fBottom), dst.fBottom
2008 };
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002009
reed@google.com9987ec32011-09-07 11:57:52 +00002010 if (dstX[1] > dstX[2]) {
2011 dstX[1] = dstX[0] + (dstX[3] - dstX[0]) * c.fLeft / (w - c.width());
2012 dstX[2] = dstX[1];
2013 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002014
reed@google.com9987ec32011-09-07 11:57:52 +00002015 if (dstY[1] > dstY[2]) {
2016 dstY[1] = dstY[0] + (dstY[3] - dstY[0]) * c.fTop / (h - c.height());
2017 dstY[2] = dstY[1];
2018 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002019
reed@google.com9987ec32011-09-07 11:57:52 +00002020 for (int y = 0; y < 3; y++) {
reed@google.com71121732012-09-18 15:14:33 +00002021 SkRect s, d;
2022
reed@google.com9987ec32011-09-07 11:57:52 +00002023 s.fTop = srcY[y];
2024 s.fBottom = srcY[y+1];
2025 d.fTop = dstY[y];
2026 d.fBottom = dstY[y+1];
2027 for (int x = 0; x < 3; x++) {
2028 s.fLeft = srcX[x];
2029 s.fRight = srcX[x+1];
2030 d.fLeft = dstX[x];
2031 d.fRight = dstX[x+1];
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002032 this->internalDrawBitmapRect(bitmap, &s, d, paint,
robertphillips@google.com31acc112013-08-20 12:13:48 +00002033 kNone_DrawBitmapRectFlag);
reed@google.com9987ec32011-09-07 11:57:52 +00002034 }
2035 }
2036}
2037
2038void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
2039 const SkRect& dst, const SkPaint* paint) {
2040 SkDEBUGCODE(bitmap.validate();)
2041
2042 // Need a device entry-point, so gpu can use a mesh
2043 this->internalDrawBitmapNine(bitmap, center, dst, paint);
2044}
2045
reed@google.comf67e4cf2011-03-15 20:56:58 +00002046class SkDeviceFilteredPaint {
2047public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002048 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
2049 SkBaseDevice::TextFlags flags;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002050 if (device->filterTextFlags(paint, &flags)) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002051 SkPaint* newPaint = fLazy.set(paint);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002052 newPaint->setFlags(flags.fFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002053 fPaint = newPaint;
2054 } else {
2055 fPaint = &paint;
2056 }
2057 }
2058
reed@google.comf67e4cf2011-03-15 20:56:58 +00002059 const SkPaint& paint() const { return *fPaint; }
2060
2061private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002062 const SkPaint* fPaint;
2063 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002064};
2065
bungeman@google.com52c748b2011-08-22 21:30:43 +00002066void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2067 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002068 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002069 draw.fDevice->drawRect(draw, r, paint);
2070 } else {
2071 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002072 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002073 draw.fDevice->drawRect(draw, r, p);
2074 }
2075}
2076
2077void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2078 const char text[], size_t byteLength,
2079 SkScalar x, SkScalar y) {
2080 SkASSERT(byteLength == 0 || text != NULL);
2081
2082 // nothing to draw
2083 if (text == NULL || byteLength == 0 ||
2084 draw.fClip->isEmpty() ||
2085 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2086 return;
2087 }
2088
2089 SkScalar width = 0;
2090 SkPoint start;
2091
2092 start.set(0, 0); // to avoid warning
2093 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2094 SkPaint::kStrikeThruText_Flag)) {
2095 width = paint.measureText(text, byteLength);
2096
2097 SkScalar offsetX = 0;
2098 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2099 offsetX = SkScalarHalf(width);
2100 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2101 offsetX = width;
2102 }
2103 start.set(x - offsetX, y);
2104 }
2105
2106 if (0 == width) {
2107 return;
2108 }
2109
2110 uint32_t flags = paint.getFlags();
2111
2112 if (flags & (SkPaint::kUnderlineText_Flag |
2113 SkPaint::kStrikeThruText_Flag)) {
2114 SkScalar textSize = paint.getTextSize();
2115 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2116 SkRect r;
2117
2118 r.fLeft = start.fX;
2119 r.fRight = start.fX + width;
2120
2121 if (flags & SkPaint::kUnderlineText_Flag) {
2122 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2123 start.fY);
2124 r.fTop = offset;
2125 r.fBottom = offset + height;
2126 DrawRect(draw, paint, r, textSize);
2127 }
2128 if (flags & SkPaint::kStrikeThruText_Flag) {
2129 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2130 start.fY);
2131 r.fTop = offset;
2132 r.fBottom = offset + height;
2133 DrawRect(draw, paint, r, textSize);
2134 }
2135 }
2136}
2137
reed@google.come0d9ce82014-04-23 04:00:17 +00002138void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2139 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002140 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141
2142 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002143 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002144 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002145 DrawTextDecorations(iter, dfp.paint(),
2146 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002147 }
2148
reed@google.com4e2b3d32011-04-07 14:18:59 +00002149 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002150}
2151
reed@google.come0d9ce82014-04-23 04:00:17 +00002152void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2153 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002154 SkPoint textOffset = SkPoint::Make(0, 0);
2155
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002156 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002157
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002159 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002160 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002161 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002163
reed@google.com4e2b3d32011-04-07 14:18:59 +00002164 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002165}
2166
reed@google.come0d9ce82014-04-23 04:00:17 +00002167void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2168 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002169
2170 SkPoint textOffset = SkPoint::Make(0, constY);
2171
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002172 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002173
reed@android.com8a1c16f2008-12-17 15:59:43 +00002174 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002175 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002176 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002177 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002178 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002179
reed@google.com4e2b3d32011-04-07 14:18:59 +00002180 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002181}
2182
reed@google.come0d9ce82014-04-23 04:00:17 +00002183void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2184 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002185 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002186
reed@android.com8a1c16f2008-12-17 15:59:43 +00002187 while (iter.next()) {
2188 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002189 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002191
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002192 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002193}
2194
fmalita00d5c2c2014-08-21 08:53:26 -07002195void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2196 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002197
fmalita19653d12014-10-16 11:53:30 -07002198 if (paint.canComputeFastBounds()) {
fmalita7ba7aa72014-08-29 09:46:36 -07002199 SkRect storage;
2200
2201 if (this->quickReject(paint.computeFastBounds(blob->bounds().makeOffset(x, y), &storage))) {
2202 return;
2203 }
2204 }
2205
fmalitaaa1b9122014-08-28 14:32:24 -07002206 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
fmalita00d5c2c2014-08-21 08:53:26 -07002207
fmalitaaa1b9122014-08-28 14:32:24 -07002208 while (iter.next()) {
2209 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
2210 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint());
fmalita00d5c2c2014-08-21 08:53:26 -07002211 }
2212
fmalitaaa1b9122014-08-28 14:32:24 -07002213 LOOPER_END
fmalita00d5c2c2014-08-21 08:53:26 -07002214}
2215
reed@google.come0d9ce82014-04-23 04:00:17 +00002216// These will become non-virtual, so they always call the (virtual) onDraw... method
2217void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2218 const SkPaint& paint) {
2219 this->onDrawText(text, byteLength, x, y, paint);
2220}
2221void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2222 const SkPaint& paint) {
2223 this->onDrawPosText(text, byteLength, pos, paint);
2224}
2225void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2226 SkScalar constY, const SkPaint& paint) {
2227 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2228}
2229void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2230 const SkMatrix* matrix, const SkPaint& paint) {
2231 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2232}
fmalita00d5c2c2014-08-21 08:53:26 -07002233void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2234 const SkPaint& paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002235 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002236 this->onDrawTextBlob(blob, x, y, paint);
2237 }
2238}
reed@google.come0d9ce82014-04-23 04:00:17 +00002239
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
2241 const SkPoint verts[], const SkPoint texs[],
2242 const SkColor colors[], SkXfermode* xmode,
2243 const uint16_t indices[], int indexCount,
2244 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002245 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002246
reed@android.com8a1c16f2008-12-17 15:59:43 +00002247 while (iter.next()) {
2248 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002249 colors, xmode, indices, indexCount,
2250 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002251 }
reed@google.com4b226022011-01-11 18:32:13 +00002252
reed@google.com4e2b3d32011-04-07 14:18:59 +00002253 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254}
2255
dandovb3c9d1c2014-08-12 08:34:29 -07002256void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2257 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2258 if (NULL == cubics) {
2259 return;
2260 }
mtklein6cfa73a2014-08-13 13:33:49 -07002261
dandovecfff212014-08-04 10:02:00 -07002262 // Since a patch is always within the convex hull of the control points, we discard it when its
2263 // bounding rectangle is completely outside the current clip.
2264 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002265 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002266 if (this->quickReject(bounds)) {
2267 return;
2268 }
mtklein6cfa73a2014-08-13 13:33:49 -07002269
dandovb3c9d1c2014-08-12 08:34:29 -07002270 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2271}
2272
2273void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2274 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2275
dandovecfff212014-08-04 10:02:00 -07002276 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002277
dandovecfff212014-08-04 10:02:00 -07002278 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002279 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002280 }
mtklein6cfa73a2014-08-13 13:33:49 -07002281
dandovecfff212014-08-04 10:02:00 -07002282 LOOPER_END
2283}
2284
reed@android.com8a1c16f2008-12-17 15:59:43 +00002285//////////////////////////////////////////////////////////////////////////////
2286// These methods are NOT virtual, and therefore must call back into virtual
2287// methods, rather than actually drawing themselves.
2288//////////////////////////////////////////////////////////////////////////////
2289
2290void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002291 SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292 SkPaint paint;
2293
2294 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002295 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002296 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002297 }
2298 this->drawPaint(paint);
2299}
2300
reed@android.com845fdac2009-06-23 03:01:32 +00002301void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002302 SkPaint paint;
2303
2304 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002305 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002306 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002307 }
2308 this->drawPaint(paint);
2309}
2310
2311void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
2312 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002313
reed@android.com8a1c16f2008-12-17 15:59:43 +00002314 pt.set(x, y);
2315 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2316}
2317
2318void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
2319 SkPoint pt;
2320 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002321
reed@android.com8a1c16f2008-12-17 15:59:43 +00002322 pt.set(x, y);
2323 paint.setColor(color);
2324 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2325}
2326
2327void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2328 const SkPaint& paint) {
2329 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002330
reed@android.com8a1c16f2008-12-17 15:59:43 +00002331 pts[0].set(x0, y0);
2332 pts[1].set(x1, y1);
2333 this->drawPoints(kLines_PointMode, 2, pts, paint);
2334}
2335
2336void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2337 SkScalar right, SkScalar bottom,
2338 const SkPaint& paint) {
2339 SkRect r;
2340
2341 r.set(left, top, right, bottom);
2342 this->drawRect(r, paint);
2343}
2344
2345void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2346 const SkPaint& paint) {
2347 if (radius < 0) {
2348 radius = 0;
2349 }
2350
2351 SkRect r;
2352 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002353 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002354}
2355
2356void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2357 const SkPaint& paint) {
2358 if (rx > 0 && ry > 0) {
2359 if (paint.canComputeFastBounds()) {
2360 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002361 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002362 return;
2363 }
2364 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002365 SkRRect rrect;
2366 rrect.setRectXY(r, rx, ry);
2367 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002368 } else {
2369 this->drawRect(r, paint);
2370 }
2371}
2372
reed@android.com8a1c16f2008-12-17 15:59:43 +00002373void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2374 SkScalar sweepAngle, bool useCenter,
2375 const SkPaint& paint) {
2376 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2377 this->drawOval(oval, paint);
2378 } else {
2379 SkPath path;
2380 if (useCenter) {
2381 path.moveTo(oval.centerX(), oval.centerY());
2382 }
2383 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2384 if (useCenter) {
2385 path.close();
2386 }
2387 this->drawPath(path, paint);
2388 }
2389}
2390
2391void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2392 const SkPath& path, SkScalar hOffset,
2393 SkScalar vOffset, const SkPaint& paint) {
2394 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002395
reed@android.com8a1c16f2008-12-17 15:59:43 +00002396 matrix.setTranslate(hOffset, vOffset);
2397 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2398}
2399
reed@android.comf76bacf2009-05-13 14:00:33 +00002400///////////////////////////////////////////////////////////////////////////////
robertphillips9b14f262014-06-04 05:40:44 -07002401void SkCanvas::EXPERIMENTAL_optimize(const SkPicture* picture) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002402 SkBaseDevice* device = this->getDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002403 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002404 device->EXPERIMENTAL_optimize(picture);
2405 }
2406}
reed@android.comf76bacf2009-05-13 14:00:33 +00002407
robertphillips9b14f262014-06-04 05:40:44 -07002408void SkCanvas::drawPicture(const SkPicture* picture) {
bsalomon49f085d2014-09-05 13:34:00 -07002409 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002410 this->onDrawPicture(picture, NULL, NULL);
robertphillips9b14f262014-06-04 05:40:44 -07002411 }
2412}
2413
reedd5fa1a42014-08-09 11:08:05 -07002414void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
bsalomon49f085d2014-09-05 13:34:00 -07002415 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002416 if (matrix && matrix->isIdentity()) {
2417 matrix = NULL;
2418 }
2419 this->onDrawPicture(picture, matrix, paint);
2420 }
2421}
robertphillips9b14f262014-06-04 05:40:44 -07002422
reedd5fa1a42014-08-09 11:08:05 -07002423void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2424 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002425 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002426 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002427 // Canvas has to first give the device the opportunity to render
2428 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002429 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002430 return; // the device has rendered the entire picture
2431 }
2432 }
2433
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002434 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
reedd5fa1a42014-08-09 11:08:05 -07002435
robertphillipsc5ba71d2014-09-04 08:42:50 -07002436 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002437}
2438
reed@android.com8a1c16f2008-12-17 15:59:43 +00002439///////////////////////////////////////////////////////////////////////////////
2440///////////////////////////////////////////////////////////////////////////////
2441
2442SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002443 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444
2445 SkASSERT(canvas);
2446
2447 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2448 fDone = !fImpl->next();
2449}
2450
2451SkCanvas::LayerIter::~LayerIter() {
2452 fImpl->~SkDrawIter();
2453}
2454
2455void SkCanvas::LayerIter::next() {
2456 fDone = !fImpl->next();
2457}
2458
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002459SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002460 return fImpl->getDevice();
2461}
2462
2463const SkMatrix& SkCanvas::LayerIter::matrix() const {
2464 return fImpl->getMatrix();
2465}
2466
2467const SkPaint& SkCanvas::LayerIter::paint() const {
2468 const SkPaint* paint = fImpl->getPaint();
2469 if (NULL == paint) {
2470 paint = &fDefaultPaint;
2471 }
2472 return *paint;
2473}
2474
2475const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2476int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2477int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002478
2479///////////////////////////////////////////////////////////////////////////////
2480
fmalitac3b589a2014-06-05 12:40:07 -07002481SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002482
2483///////////////////////////////////////////////////////////////////////////////
2484
2485static bool supported_for_raster_canvas(const SkImageInfo& info) {
2486 switch (info.alphaType()) {
2487 case kPremul_SkAlphaType:
2488 case kOpaque_SkAlphaType:
2489 break;
2490 default:
2491 return false;
2492 }
2493
2494 switch (info.colorType()) {
2495 case kAlpha_8_SkColorType:
2496 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002497 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002498 break;
2499 default:
2500 return false;
2501 }
2502
2503 return true;
2504}
2505
2506SkCanvas* SkCanvas::NewRaster(const SkImageInfo& info) {
2507 if (!supported_for_raster_canvas(info)) {
2508 return NULL;
2509 }
2510
2511 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -07002512 if (!bitmap.tryAllocPixels(info)) {
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002513 return NULL;
2514 }
2515
2516 // should this functionality be moved into allocPixels()?
2517 if (!bitmap.info().isOpaque()) {
2518 bitmap.eraseColor(0);
2519 }
2520 return SkNEW_ARGS(SkCanvas, (bitmap));
2521}
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002522
2523SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2524 if (!supported_for_raster_canvas(info)) {
2525 return NULL;
2526 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002527
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002528 SkBitmap bitmap;
2529 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2530 return NULL;
2531 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002532 return SkNEW_ARGS(SkCanvas, (bitmap));
2533}
reedd5fa1a42014-08-09 11:08:05 -07002534
2535///////////////////////////////////////////////////////////////////////////////
2536
2537SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002538 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002539 : fCanvas(canvas)
2540 , fSaveCount(canvas->getSaveCount())
2541{
bsalomon49f085d2014-09-05 13:34:00 -07002542 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002543 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002544 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002545 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002546 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002547 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002548 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002549 canvas->save();
2550 }
mtklein6cfa73a2014-08-13 13:33:49 -07002551
bsalomon49f085d2014-09-05 13:34:00 -07002552 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002553 canvas->concat(*matrix);
2554 }
2555}
2556
2557SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2558 fCanvas->restoreToCount(fSaveCount);
2559}