blob: 4f5e15911d3ad1c3b7d2b60a00edc5f69c02c93b [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
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
bungemand3ebb482015-08-05 13:57:49 -070013#include "SkDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000014#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080015#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016#include "SkDrawFilter.h"
17#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080018#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070019#include "SkImage.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000020#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070021#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070022#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070023#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000024#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000025#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080026#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000027#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000028#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000029#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070030#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000031#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000032#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080033#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070034
35#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000036
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000037#if SK_SUPPORT_GPU
38#include "GrRenderTarget.h"
39#endif
40
reedc83a2972015-07-16 07:40:45 -070041/*
42 * Return true if the drawing this rect would hit every pixels in the canvas.
43 *
44 * Returns false if
45 * - rect does not contain the canvas' bounds
46 * - paint is not fill
47 * - paint would blur or otherwise change the coverage of the rect
48 */
49bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
50 ShaderOverrideOpacity overrideOpacity) const {
51 SK_COMPILE_ASSERT((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
52 (int)kNone_ShaderOverrideOpacity,
53 need_matching_enums0);
54 SK_COMPILE_ASSERT((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
55 (int)kOpaque_ShaderOverrideOpacity,
56 need_matching_enums1);
57 SK_COMPILE_ASSERT((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
58 (int)kNotOpaque_ShaderOverrideOpacity,
59 need_matching_enums2);
60
61 const SkISize size = this->getBaseLayerSize();
62 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
63 if (!this->getClipStack()->quickContains(bounds)) {
64 return false;
65 }
66
67 if (rect) {
68 if (!this->getTotalMatrix().rectStaysRect()) {
69 return false; // conservative
70 }
71
72 SkRect devRect;
73 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070074 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070075 return false;
76 }
77 }
78
79 if (paint) {
80 SkPaint::Style paintStyle = paint->getStyle();
81 if (!(paintStyle == SkPaint::kFill_Style ||
82 paintStyle == SkPaint::kStrokeAndFill_Style)) {
83 return false;
84 }
85 if (paint->getMaskFilter() || paint->getLooper()
86 || paint->getPathEffect() || paint->getImageFilter()) {
87 return false; // conservative
88 }
89 }
90 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
91}
92
93///////////////////////////////////////////////////////////////////////////////////////////////////
94
reedd990e2f2014-12-22 11:58:30 -080095static bool gIgnoreSaveLayerBounds;
96void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
97 gIgnoreSaveLayerBounds = ignore;
98}
99bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
100 return gIgnoreSaveLayerBounds;
101}
102
reed0acf1b42014-12-22 16:12:38 -0800103static bool gTreatSpriteAsBitmap;
104void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
105 gTreatSpriteAsBitmap = spriteAsBitmap;
106}
107bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
108 return gTreatSpriteAsBitmap;
109}
110
reed@google.comda17f752012-08-16 18:27:05 +0000111// experimental for faster tiled drawing...
112//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +0000113
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114//#define SK_TRACE_SAVERESTORE
115
116#ifdef SK_TRACE_SAVERESTORE
117 static int gLayerCounter;
118 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
119 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
120
121 static int gRecCounter;
122 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
123 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
124
125 static int gCanvasCounter;
126 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
127 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
128#else
129 #define inc_layer()
130 #define dec_layer()
131 #define inc_rec()
132 #define dec_rec()
133 #define inc_canvas()
134 #define dec_canvas()
135#endif
136
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000137typedef SkTLazy<SkPaint> SkLazyPaint;
138
reedc83a2972015-07-16 07:40:45 -0700139void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000140 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700141 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
142 ? SkSurface::kDiscard_ContentChangeMode
143 : SkSurface::kRetain_ContentChangeMode);
144 }
145}
146
147void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
148 ShaderOverrideOpacity overrideOpacity) {
149 if (fSurfaceBase) {
150 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
151 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
152 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
153 // and therefore we don't care which mode we're in.
154 //
155 if (fSurfaceBase->outstandingImageSnapshot()) {
156 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
157 mode = SkSurface::kDiscard_ContentChangeMode;
158 }
159 }
160 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000161 }
162}
163
reed@android.com8a1c16f2008-12-17 15:59:43 +0000164///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
reed4a8126e2014-09-22 07:29:03 -0700166static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
167 const uint32_t propFlags = props.flags();
168 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
169 flags &= ~SkPaint::kDither_Flag;
170 }
171 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
172 flags &= ~SkPaint::kAntiAlias_Flag;
173 }
174 return flags;
175}
176
177///////////////////////////////////////////////////////////////////////////////
178
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000179/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 The clip/matrix/proc are fields that reflect the top of the save/restore
181 stack. Whenever the canvas changes, it marks a dirty flag, and then before
182 these are used (assuming we're not on a layer) we rebuild these cache
183 values: they reflect the top of the save stack, but translated and clipped
184 by the device's XY offset and bitmap-bounds.
185*/
186struct DeviceCM {
187 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000188 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000189 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000190 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700191 const SkMatrix* fMatrix;
192 SkMatrix fMatrixStorage;
193 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194
reed96e657d2015-03-10 17:30:07 -0700195 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700196 bool conservativeRasterClip, bool deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700197 : fNext(NULL)
198 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700199 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700200 {
201 if (NULL != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000203 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 }
reed@google.com4b226022011-01-11 18:32:13 +0000205 fDevice = device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000207 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000209 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700210 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000211 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212 fDevice->unref();
213 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000214 SkDELETE(fPaint);
215 }
reed@google.com4b226022011-01-11 18:32:13 +0000216
mtkleinfeaadee2015-04-08 11:25:48 -0700217 void reset(const SkIRect& bounds) {
218 SkASSERT(!fPaint);
219 SkASSERT(!fNext);
220 SkASSERT(fDevice);
221 fClip.setRect(bounds);
222 }
223
reed@google.com045e62d2011-10-24 12:19:46 +0000224 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
225 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000226 int x = fDevice->getOrigin().x();
227 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000228 int width = fDevice->width();
229 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000230
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 if ((x | y) == 0) {
232 fMatrix = &totalMatrix;
233 fClip = totalClip;
234 } else {
235 fMatrixStorage = totalMatrix;
236 fMatrixStorage.postTranslate(SkIntToScalar(-x),
237 SkIntToScalar(-y));
238 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 totalClip.translate(-x, -y, &fClip);
241 }
242
reed@google.com045e62d2011-10-24 12:19:46 +0000243 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244
245 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000246
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000248 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 SkRegion::kDifference_Op);
250 }
reed@google.com4b226022011-01-11 18:32:13 +0000251
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000252 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
253
reed@android.com8a1c16f2008-12-17 15:59:43 +0000254#ifdef SK_DEBUG
255 if (!fClip.isEmpty()) {
256 SkIRect deviceR;
257 deviceR.set(0, 0, width, height);
258 SkASSERT(deviceR.contains(fClip.getBounds()));
259 }
260#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000261 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262};
263
264/* This is the record we keep for each save/restore level in the stack.
265 Since a level optionally copies the matrix and/or stack, we have pointers
266 for these fields. If the value is copied for this level, the copy is
267 stored in the ...Storage field, and the pointer points to that. If the
268 value is not copied for this level, we ignore ...Storage, and just point
269 at the corresponding value in the previous level in the stack.
270*/
271class SkCanvas::MCRec {
272public:
reed1f836ee2014-07-07 07:49:34 -0700273 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700274 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 /* If there are any layers in the stack, this points to the top-most
276 one that is at or below this level in the stack (so we know what
277 bitmap/device to draw into from this level. This value is NOT
278 reference counted, since the real owner is either our fLayer field,
279 or a previous one in a lower level.)
280 */
reed2ff1fce2014-12-11 07:07:37 -0800281 DeviceCM* fTopLayer;
282 SkRasterClip fRasterClip;
283 SkMatrix fMatrix;
284 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285
reedd9544982014-09-09 18:46:22 -0700286 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
reedd9544982014-09-09 18:46:22 -0700287 fFilter = NULL;
288 fLayer = NULL;
289 fTopLayer = NULL;
reed2ff1fce2014-12-11 07:07:37 -0800290 fMatrix.reset();
291 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700292
reedd9544982014-09-09 18:46:22 -0700293 // don't bother initializing fNext
294 inc_rec();
295 }
reed2ff1fce2014-12-11 07:07:37 -0800296 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700297 fFilter = SkSafeRef(prev.fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 fLayer = NULL;
reedd9544982014-09-09 18:46:22 -0700299 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800300 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700301
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 // don't bother initializing fNext
303 inc_rec();
304 }
305 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000306 SkSafeUnref(fFilter);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000307 SkDELETE(fLayer);
308 dec_rec();
309 }
mtkleinfeaadee2015-04-08 11:25:48 -0700310
311 void reset(const SkIRect& bounds) {
312 SkASSERT(fLayer);
313 SkASSERT(fDeferredSaveCount == 0);
314
315 fMatrix.reset();
316 fRasterClip.setRect(bounds);
317 fLayer->reset(bounds);
318 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319};
320
321class SkDrawIter : public SkDraw {
322public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000323 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000324 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000325 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 canvas->updateDeviceCMCache();
327
reed687fa1c2015-04-07 08:00:56 -0700328 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000329 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000330 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000331 }
reed@google.com4b226022011-01-11 18:32:13 +0000332
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 bool next() {
334 // skip over recs with empty clips
335 if (fSkipEmptyClips) {
336 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
337 fCurrLayer = fCurrLayer->fNext;
338 }
339 }
340
reed@google.comf68c5e22012-02-24 16:38:58 +0000341 const DeviceCM* rec = fCurrLayer;
342 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343
344 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000345 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
346 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700348 if (!fDevice->accessPixels(&fDst)) {
349 fDst.reset(fDevice->imageInfo(), NULL, 0);
350 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000352 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353
354 fCurrLayer = rec->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 // fCurrLayer may be NULL now
reed@android.com199f1082009-06-10 02:12:47 +0000356
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 return true;
358 }
359 return false;
360 }
reed@google.com4b226022011-01-11 18:32:13 +0000361
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000362 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000363 int getX() const { return fDevice->getOrigin().x(); }
364 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365 const SkMatrix& getMatrix() const { return *fMatrix; }
366 const SkRegion& getClip() const { return *fClip; }
367 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369private:
370 SkCanvas* fCanvas;
371 const DeviceCM* fCurrLayer;
372 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373 SkBool8 fSkipEmptyClips;
374
375 typedef SkDraw INHERITED;
376};
377
378/////////////////////////////////////////////////////////////////////////////
379
reeddbc3cef2015-04-29 12:18:57 -0700380static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
381 return lazy->isValid() ? lazy->get() : lazy->set(orig);
382}
383
384/**
385 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
386 * colorfilter, else return NULL.
387 */
388static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700389 SkImageFilter* imgf = paint.getImageFilter();
390 if (!imgf) {
391 return NULL;
392 }
393
394 SkColorFilter* imgCF;
395 if (!imgf->asAColorFilter(&imgCF)) {
396 return NULL;
397 }
398
399 SkColorFilter* paintCF = paint.getColorFilter();
400 if (NULL == paintCF) {
401 // there is no existing paint colorfilter, so we can just return the imagefilter's
402 return imgCF;
403 }
404
405 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
406 // and we need to combine them into a single colorfilter.
407 SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
408 return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
reeddbc3cef2015-04-29 12:18:57 -0700409}
410
reed@android.com8a1c16f2008-12-17 15:59:43 +0000411class AutoDrawLooper {
412public:
reed4a8126e2014-09-22 07:29:03 -0700413 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000414 bool skipLayerForImageFilter = false,
415 const SkRect* bounds = NULL) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000416 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000417 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700418 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000419 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700420 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000421 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000422
reeddbc3cef2015-04-29 12:18:57 -0700423 SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
424 if (simplifiedCF) {
425 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
426 paint->setColorFilter(simplifiedCF)->unref();
427 paint->setImageFilter(NULL);
428 fPaint = paint;
429 }
430
431 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700432 /**
433 * We implement ImageFilters for a given draw by creating a layer, then applying the
434 * imagefilter to the pixels of that layer (its backing surface/image), and then
435 * we call restore() to xfer that layer to the main canvas.
436 *
437 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
438 * 2. Generate the src pixels:
439 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
440 * return (fPaint). We then draw the primitive (using srcover) into a cleared
441 * buffer/surface.
442 * 3. Restore the layer created in #1
443 * The imagefilter is passed the buffer/surface from the layer (now filled with the
444 * src pixels of the primitive). It returns a new "filtered" buffer, which we
445 * draw onto the previous layer using the xfermode from the original paint.
446 */
reed@google.com8926b162012-03-23 15:36:36 +0000447 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700448 tmp.setImageFilter(fPaint->getImageFilter());
449 tmp.setXfermode(fPaint->getXfermode());
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +0000450 (void)canvas->internalSaveLayer(bounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
reed76033be2015-03-14 10:54:31 -0700451 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700452 fTempLayerForImageFilter = true;
453 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000454 }
455
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000456 if (SkDrawLooper* looper = paint.getLooper()) {
457 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
458 looper->contextSize());
459 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000460 fIsSimple = false;
461 } else {
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000462 fLooperContext = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000463 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700464 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000465 }
piotaixrb5fae932014-09-24 13:03:30 -0700466
reed4a8126e2014-09-22 07:29:03 -0700467 uint32_t oldFlags = paint.getFlags();
468 fNewPaintFlags = filter_paint_flags(props, oldFlags);
469 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700470 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700471 paint->setFlags(fNewPaintFlags);
472 fPaint = paint;
473 // if we're not simple, doNext() will take care of calling setFlags()
474 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000475 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000476
reed@android.com8a1c16f2008-12-17 15:59:43 +0000477 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700478 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000479 fCanvas->internalRestore();
480 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000481 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000482 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000483
reed@google.com4e2b3d32011-04-07 14:18:59 +0000484 const SkPaint& paint() const {
485 SkASSERT(fPaint);
486 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000487 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000488
reed@google.com129ec222012-05-15 13:24:09 +0000489 bool next(SkDrawFilter::Type drawType) {
490 if (fDone) {
491 return false;
492 } else if (fIsSimple) {
493 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000494 return !fPaint->nothingToDraw();
495 } else {
496 return this->doNext(drawType);
497 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000498 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000499
reed@android.com8a1c16f2008-12-17 15:59:43 +0000500private:
reeddbc3cef2015-04-29 12:18:57 -0700501 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
502 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000503 SkCanvas* fCanvas;
504 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000505 SkDrawFilter* fFilter;
506 const SkPaint* fPaint;
507 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700508 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700509 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000510 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000511 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000512 SkDrawLooper::Context* fLooperContext;
513 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000514
515 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516};
517
reed@google.com129ec222012-05-15 13:24:09 +0000518bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
reed@google.com632e1a22011-10-06 12:37:00 +0000519 fPaint = NULL;
reed@google.com129ec222012-05-15 13:24:09 +0000520 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700521 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000522
reeddbc3cef2015-04-29 12:18:57 -0700523 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
524 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700525 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000526
reed5c476fb2015-04-20 08:04:21 -0700527 if (fTempLayerForImageFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000528 paint->setImageFilter(NULL);
reed5c476fb2015-04-20 08:04:21 -0700529 paint->setXfermode(NULL);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000530 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000531
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000532 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000533 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000534 return false;
535 }
536 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000537 if (!fFilter->filter(paint, drawType)) {
538 fDone = true;
539 return false;
540 }
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000541 if (NULL == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000542 // no looper means we only draw once
543 fDone = true;
544 }
545 }
546 fPaint = paint;
547
548 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000549 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000550 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000551 }
552
553 // call this after any possible paint modifiers
554 if (fPaint->nothingToDraw()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000555 fPaint = NULL;
556 return false;
557 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000558 return true;
559}
560
reed@android.com8a1c16f2008-12-17 15:59:43 +0000561////////// macros to place around the internal draw calls //////////////////
562
reed@google.com8926b162012-03-23 15:36:36 +0000563#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000564 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700565 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000566 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000567 SkDrawIter iter(this);
568
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000569#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000570 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700571 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000572 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000573 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000574
reedc83a2972015-07-16 07:40:45 -0700575#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
576 this->predrawNotify(bounds, &paint, auxOpaque); \
577 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
578 while (looper.next(type)) { \
579 SkDrawIter iter(this);
580
reed@google.com4e2b3d32011-04-07 14:18:59 +0000581#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000582
583////////////////////////////////////////////////////////////////////////////
584
mtkleinfeaadee2015-04-08 11:25:48 -0700585void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
586 this->restoreToCount(1);
587 fCachedLocalClipBounds.setEmpty();
588 fCachedLocalClipBoundsDirty = true;
589 fClipStack->reset();
590 fMCRec->reset(bounds);
591
592 // We're peering through a lot of structs here. Only at this scope do we
593 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
594 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
595}
596
reedd9544982014-09-09 18:46:22 -0700597SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
598 fConservativeRasterClip = SkToBool(flags & kConservativeRasterClip_InitFlag);
reed@google.comc0784db2013-12-13 21:16:12 +0000599 fCachedLocalClipBounds.setEmpty();
600 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000601 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000602 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700603 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800604 fSaveCount = 1;
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000605 fMetaData = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000606
reed687fa1c2015-04-07 08:00:56 -0700607 fClipStack.reset(SkNEW(SkClipStack));
608
reed@android.com8a1c16f2008-12-17 15:59:43 +0000609 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700610 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611
reeda499f902015-05-01 09:34:31 -0700612 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
613 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed86a17e72015-05-14 12:25:22 -0700614 new (fDeviceCMStorage) DeviceCM(NULL, NULL, NULL, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700615
reed@android.com8a1c16f2008-12-17 15:59:43 +0000616 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000617
reed@google.com97af1a62012-08-28 12:19:02 +0000618 fSurfaceBase = NULL;
reed@android.comf2b98d62010-12-20 18:26:13 +0000619
reedf92c8662014-08-18 08:02:43 -0700620 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700621 // The root device and the canvas should always have the same pixel geometry
622 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reed4a8126e2014-09-22 07:29:03 -0700623 if (device->forceConservativeRasterClip()) {
624 fConservativeRasterClip = true;
625 }
reedf92c8662014-08-18 08:02:43 -0700626 device->onAttachToCanvas(this);
627 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800628 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700629 }
630 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631}
632
reed@google.comcde92112011-07-06 20:00:52 +0000633SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000634 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700635 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000636{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000637 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000638
reedd9544982014-09-09 18:46:22 -0700639 this->init(NULL, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000640}
641
reedd9544982014-09-09 18:46:22 -0700642static SkBitmap make_nopixels(int width, int height) {
643 SkBitmap bitmap;
644 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
645 return bitmap;
646}
647
648class SkNoPixelsBitmapDevice : public SkBitmapDevice {
649public:
robertphillipsfcf78292015-06-19 11:49:52 -0700650 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
651 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800652 {
653 this->setOrigin(bounds.x(), bounds.y());
654 }
reedd9544982014-09-09 18:46:22 -0700655
656private:
piotaixrb5fae932014-09-24 13:03:30 -0700657
reedd9544982014-09-09 18:46:22 -0700658 typedef SkBitmapDevice INHERITED;
659};
660
reed96a857e2015-01-25 10:33:58 -0800661SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000662 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800663 , fProps(SkSurfacePropsCopyOrDefault(props))
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000664{
665 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700666
reed78e27682014-11-19 08:04:34 -0800667 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice,
robertphillipsfcf78292015-06-19 11:49:52 -0700668 (SkIRect::MakeWH(width, height), fProps)), kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700669}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000670
reed78e27682014-11-19 08:04:34 -0800671SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700672 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700673 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reedd9544982014-09-09 18:46:22 -0700674{
675 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700676
robertphillipsfcf78292015-06-19 11:49:52 -0700677 this->init(SkNEW_ARGS(SkNoPixelsBitmapDevice, (bounds, fProps)), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700678}
679
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000680SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000681 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700682 , fProps(device->surfaceProps())
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000683{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000684 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700685
reedd9544982014-09-09 18:46:22 -0700686 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
robertphillipsfcf78292015-06-19 11:49:52 -0700689SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
690 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700691 , fProps(device->surfaceProps())
robertphillipsfcf78292015-06-19 11:49:52 -0700692{
693 inc_canvas();
694
695 this->init(device, flags);
696}
697
reed4a8126e2014-09-22 07:29:03 -0700698SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700699 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700700 , fProps(props)
reed3716fd02014-09-21 09:39:55 -0700701{
702 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700703
robertphillipsfcf78292015-06-19 11:49:52 -0700704 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap, fProps)));
reed4a8126e2014-09-22 07:29:03 -0700705 this->init(device, kDefault_InitFlags);
706}
reed29c857d2014-09-21 10:25:07 -0700707
reed4a8126e2014-09-22 07:29:03 -0700708SkCanvas::SkCanvas(const SkBitmap& bitmap)
709 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
710 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
711{
712 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700713
robertphillipsfcf78292015-06-19 11:49:52 -0700714 SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap, fProps)));
reed4a8126e2014-09-22 07:29:03 -0700715 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000716}
717
718SkCanvas::~SkCanvas() {
719 // free up the contents of our deque
720 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000721
reed@android.com8a1c16f2008-12-17 15:59:43 +0000722 this->internalRestore(); // restore the last, since we're going away
723
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000724 SkDELETE(fMetaData);
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000725
reed@android.com8a1c16f2008-12-17 15:59:43 +0000726 dec_canvas();
727}
728
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729SkDrawFilter* SkCanvas::getDrawFilter() const {
730 return fMCRec->fFilter;
731}
732
733SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700734 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
736 return filter;
737}
738
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000739SkMetaData& SkCanvas::getMetaData() {
740 // metadata users are rare, so we lazily allocate it. If that changes we
741 // can decide to just make it a field in the device (rather than a ptr)
742 if (NULL == fMetaData) {
743 fMetaData = new SkMetaData;
744 }
745 return *fMetaData;
746}
747
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748///////////////////////////////////////////////////////////////////////////////
749
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000750void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000751 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000752 if (device) {
753 device->flush();
754 }
755}
756
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000757SkISize SkCanvas::getTopLayerSize() const {
758 SkBaseDevice* d = this->getTopDevice();
759 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
760}
761
762SkIPoint SkCanvas::getTopLayerOrigin() const {
763 SkBaseDevice* d = this->getTopDevice();
764 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
765}
766
767SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000768 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000769 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
770}
771
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000772SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000774 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000775 SkASSERT(rec && rec->fLayer);
776 return rec->fLayer->fDevice;
777}
778
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000779SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000780 if (updateMatrixClip) {
781 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
782 }
reed@google.com9266fed2011-03-30 00:18:03 +0000783 return fMCRec->fTopLayer->fDevice;
784}
785
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000786bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
787 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
788 return false;
789 }
790
791 bool weAllocated = false;
792 if (NULL == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700793 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000794 return false;
795 }
796 weAllocated = true;
797 }
798
reedcf01e312015-05-23 19:14:51 -0700799 SkAutoPixmapUnlock unlocker;
800 if (bitmap->requestLock(&unlocker)) {
801 const SkPixmap& pm = unlocker.pixmap();
802 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
803 return true;
804 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000805 }
806
807 if (weAllocated) {
808 bitmap->setPixelRef(NULL);
809 }
810 return false;
811}
reed@google.com51df9e32010-12-23 19:29:18 +0000812
bsalomon@google.comc6980972011-11-02 19:57:21 +0000813bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000814 SkIRect r = srcRect;
815 const SkISize size = this->getBaseLayerSize();
816 if (!r.intersect(0, 0, size.width(), size.height())) {
817 bitmap->reset();
818 return false;
819 }
820
reed84825042014-09-02 12:50:45 -0700821 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000822 // bitmap will already be reset.
823 return false;
824 }
825 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
826 bitmap->reset();
827 return false;
828 }
829 return true;
830}
831
reed96472de2014-12-10 09:53:42 -0800832bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000833 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000834 if (!device) {
835 return false;
836 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000837 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800838
reed96472de2014-12-10 09:53:42 -0800839 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
840 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000841 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000842 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000843
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000844 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800845 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000846}
847
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000848bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
849 if (bitmap.getTexture()) {
850 return false;
851 }
reedcf01e312015-05-23 19:14:51 -0700852
853 SkAutoPixmapUnlock unlocker;
854 if (bitmap.requestLock(&unlocker)) {
855 const SkPixmap& pm = unlocker.pixmap();
856 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000857 }
858 return false;
859}
860
861bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
862 int x, int y) {
863 switch (origInfo.colorType()) {
864 case kUnknown_SkColorType:
865 case kIndex_8_SkColorType:
866 return false;
867 default:
868 break;
869 }
870 if (NULL == pixels || rowBytes < origInfo.minRowBytes()) {
871 return false;
872 }
873
874 const SkISize size = this->getBaseLayerSize();
875 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
876 if (!target.intersect(0, 0, size.width(), size.height())) {
877 return false;
878 }
879
880 SkBaseDevice* device = this->getDevice();
881 if (!device) {
882 return false;
883 }
884
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000885 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700886 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000887
888 // if x or y are negative, then we have to adjust pixels
889 if (x > 0) {
890 x = 0;
891 }
892 if (y > 0) {
893 y = 0;
894 }
895 // here x,y are either 0 or negative
896 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
897
reed4af35f32014-06-27 17:47:49 -0700898 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700899 const bool completeOverwrite = info.dimensions() == size;
900 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700901
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000902 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000903 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000904}
reed@google.com51df9e32010-12-23 19:29:18 +0000905
junov@google.com4370aed2012-01-18 16:21:08 +0000906SkCanvas* SkCanvas::canvasForDrawIter() {
907 return this;
908}
909
reed@android.com8a1c16f2008-12-17 15:59:43 +0000910//////////////////////////////////////////////////////////////////////////////
911
reed@android.com8a1c16f2008-12-17 15:59:43 +0000912void SkCanvas::updateDeviceCMCache() {
913 if (fDeviceCMDirty) {
914 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700915 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000916 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000917
reed@android.com8a1c16f2008-12-17 15:59:43 +0000918 if (NULL == layer->fNext) { // only one layer
reed687fa1c2015-04-07 08:00:56 -0700919 layer->updateMC(totalMatrix, totalClip, *fClipStack, NULL);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000920 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000921 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000922 do {
reed687fa1c2015-04-07 08:00:56 -0700923 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000924 } while ((layer = layer->fNext) != NULL);
925 }
926 fDeviceCMDirty = false;
927 }
928}
929
reed@android.com8a1c16f2008-12-17 15:59:43 +0000930///////////////////////////////////////////////////////////////////////////////
931
reed2ff1fce2014-12-11 07:07:37 -0800932void SkCanvas::checkForDeferredSave() {
933 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800934 this->doSave();
935 }
936}
937
reedf0090cb2014-11-26 08:55:51 -0800938int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800939#ifdef SK_DEBUG
940 int count = 0;
941 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
942 for (;;) {
943 const MCRec* rec = (const MCRec*)iter.next();
944 if (!rec) {
945 break;
946 }
947 count += 1 + rec->fDeferredSaveCount;
948 }
949 SkASSERT(count == fSaveCount);
950#endif
951 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -0800952}
953
954int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -0800955 fSaveCount += 1;
956 fMCRec->fDeferredSaveCount += 1;
957 return this->getSaveCount() - 1; // return our prev value
958}
959
960void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -0800961 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -0700962
963 SkASSERT(fMCRec->fDeferredSaveCount > 0);
964 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800965 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -0800966}
967
968void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -0800969 if (fMCRec->fDeferredSaveCount > 0) {
970 SkASSERT(fSaveCount > 1);
971 fSaveCount -= 1;
972 fMCRec->fDeferredSaveCount -= 1;
973 } else {
974 // check for underflow
975 if (fMCStack.count() > 1) {
976 this->willRestore();
977 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -0700978 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -0800979 this->internalRestore();
980 this->didRestore();
981 }
reedf0090cb2014-11-26 08:55:51 -0800982 }
983}
984
985void SkCanvas::restoreToCount(int count) {
986 // sanity check
987 if (count < 1) {
988 count = 1;
989 }
mtkleinf0f14112014-12-12 08:46:25 -0800990
reedf0090cb2014-11-26 08:55:51 -0800991 int n = this->getSaveCount() - count;
992 for (int i = 0; i < n; ++i) {
993 this->restore();
994 }
995}
996
reed2ff1fce2014-12-11 07:07:37 -0800997void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000998 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700999 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001000 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001001
reed687fa1c2015-04-07 08:00:56 -07001002 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001003}
1004
reed@android.com8a1c16f2008-12-17 15:59:43 +00001005static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001006#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +00001008#else
1009 return true;
1010#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001011}
1012
junov@chromium.orga907ac32012-02-24 21:54:07 +00001013bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
reed9b3aa542015-03-11 08:47:12 -07001014 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001015 SkIRect clipBounds;
1016 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001017 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001018 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001019
reed96e657d2015-03-10 17:30:07 -07001020 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1021
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001022 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -07001023 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001024 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001025 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001026 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001027 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001028
reed96e657d2015-03-10 17:30:07 -07001029 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001030 r.roundOut(&ir);
1031 // early exit if the layer's bounds are clipped out
1032 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001033 if (bounds_affects_clip(flags)) {
reed9b3aa542015-03-11 08:47:12 -07001034 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001035 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001036 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001037 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001038 }
1039 } else { // no user bounds, so just use the clip
1040 ir = clipBounds;
1041 }
reed180aec42015-03-11 10:39:04 -07001042 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001043
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +00001044 if (bounds_affects_clip(flags)) {
reed180aec42015-03-11 10:39:04 -07001045 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001046 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001047 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001048 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001049 }
1050
1051 if (intersection) {
1052 *intersection = ir;
1053 }
1054 return true;
1055}
1056
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001057int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -08001058 if (gIgnoreSaveLayerBounds) {
1059 bounds = NULL;
1060 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001061 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reeda6441162015-03-26 13:40:09 -07001062 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001063 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001064 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001065}
1066
reed2ff1fce2014-12-11 07:07:37 -08001067int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -08001068 if (gIgnoreSaveLayerBounds) {
1069 bounds = NULL;
1070 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001071 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reeda6441162015-03-26 13:40:09 -07001072 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001073 this->internalSaveLayer(bounds, paint, flags, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001074 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +00001075}
1076
reed2ff1fce2014-12-11 07:07:37 -08001077void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
reed76033be2015-03-14 10:54:31 -07001078 SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +00001079#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +00001080 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001081#endif
1082
junov@chromium.orga907ac32012-02-24 21:54:07 +00001083 // do this before we create the layer. We don't call the public save() since
1084 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001085 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001086
1087 fDeviceCMDirty = true;
1088
1089 SkIRect ir;
reeddaa57bf2015-05-15 10:39:17 -07001090 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : NULL)) {
reed2ff1fce2014-12-11 07:07:37 -08001091 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 }
1093
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001094 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1095 // the clipRectBounds() call above?
1096 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001097 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001098 }
1099
reed76033be2015-03-14 10:54:31 -07001100 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001101 SkPixelGeometry geo = fProps.pixelGeometry();
1102 if (paint) {
reed76033be2015-03-14 10:54:31 -07001103 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001104 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001105 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001106 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001107 }
1108 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001109 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
1110 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111
reedb2db8982014-11-13 12:41:02 -08001112 SkBaseDevice* device = this->getTopDevice();
1113 if (NULL == device) {
1114 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001115 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001116 }
reedb2db8982014-11-13 12:41:02 -08001117
reed61f501f2015-04-29 08:34:00 -07001118 bool forceSpriteOnRestore = false;
1119 {
reeddaa57bf2015-05-15 10:39:17 -07001120 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed61f501f2015-04-29 08:34:00 -07001121 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo);
1122 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
1123 if (NULL == newDev) {
1124 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001125 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1126 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
reed61f501f2015-04-29 08:34:00 -07001127 if (NULL == newDev) {
1128 SkErrorInternals::SetError(kInternalError_SkError,
1129 "Unable to create device for layer.");
1130 return;
1131 }
1132 forceSpriteOnRestore = true;
1133 }
1134 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001135 }
bsalomon@google.come97f0852011-06-17 13:10:25 +00001136
reed@google.com6f8f2922011-03-04 22:27:10 +00001137 device->setOrigin(ir.fLeft, ir.fTop);
reed61f501f2015-04-29 08:34:00 -07001138 DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, paint, this, fConservativeRasterClip,
reed86a17e72015-05-14 12:25:22 -07001139 forceSpriteOnRestore));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001140 device->unref();
1141
1142 layer->fNext = fMCRec->fTopLayer;
1143 fMCRec->fLayer = layer;
1144 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001145}
1146
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001147int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
1148 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1149}
1150
reed@android.com8a1c16f2008-12-17 15:59:43 +00001151int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1152 SaveFlags flags) {
1153 if (0xFF == alpha) {
1154 return this->saveLayer(bounds, NULL, flags);
1155 } else {
1156 SkPaint tmpPaint;
1157 tmpPaint.setAlpha(alpha);
1158 return this->saveLayer(bounds, &tmpPaint, flags);
1159 }
1160}
1161
reed@android.com8a1c16f2008-12-17 15:59:43 +00001162void SkCanvas::internalRestore() {
1163 SkASSERT(fMCStack.count() != 0);
1164
1165 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001166 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001167
reed687fa1c2015-04-07 08:00:56 -07001168 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001169
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001170 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001171 DeviceCM* layer = fMCRec->fLayer; // may be null
1172 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
1173 fMCRec->fLayer = NULL;
1174
1175 // now do the normal restore()
1176 fMCRec->~MCRec(); // balanced in save()
1177 fMCStack.pop_back();
1178 fMCRec = (MCRec*)fMCStack.back();
1179
1180 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1181 since if we're being recorded, we don't want to record this (the
1182 recorder will have already recorded the restore).
1183 */
bsalomon49f085d2014-09-05 13:34:00 -07001184 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001185 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001186 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001187 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001188 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001189 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001190 fDeviceCMDirty = true;
reedb679ca82015-04-07 04:40:48 -07001191 SkDELETE(layer);
1192 } else {
1193 // we're at the root
reeda499f902015-05-01 09:34:31 -07001194 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001195 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001196 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001197 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001198}
1199
reed4a8126e2014-09-22 07:29:03 -07001200SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
1201 if (NULL == props) {
1202 props = &fProps;
1203 }
1204 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001205}
1206
reed4a8126e2014-09-22 07:29:03 -07001207SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001208 SkBaseDevice* dev = this->getDevice();
reed4a8126e2014-09-22 07:29:03 -07001209 return dev ? dev->newSurface(info, props) : NULL;
reed@google.com76f10a32014-02-05 15:32:21 +00001210}
1211
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001212SkImageInfo SkCanvas::imageInfo() const {
1213 SkBaseDevice* dev = this->getDevice();
1214 if (dev) {
1215 return dev->imageInfo();
1216 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001217 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001218 }
1219}
1220
1221const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001222 SkPixmap pmap;
1223 if (!this->onPeekPixels(&pmap)) {
1224 return NULL;
1225 }
1226 if (info) {
1227 *info = pmap.info();
1228 }
1229 if (rowBytes) {
1230 *rowBytes = pmap.rowBytes();
1231 }
1232 return pmap.addr();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001233}
1234
reed884e97c2015-05-26 11:31:54 -07001235bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001236 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001237 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001238}
1239
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001240void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001241 SkPixmap pmap;
1242 if (!this->onAccessTopLayerPixels(&pmap)) {
1243 return NULL;
1244 }
1245 if (info) {
1246 *info = pmap.info();
1247 }
1248 if (rowBytes) {
1249 *rowBytes = pmap.rowBytes();
1250 }
1251 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001252 *origin = this->getTopDevice(false)->getOrigin();
1253 }
reed884e97c2015-05-26 11:31:54 -07001254 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001255}
1256
reed884e97c2015-05-26 11:31:54 -07001257bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001258 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001259 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001260}
1261
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001262SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1263 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
1264 if (NULL == fAddr) {
1265 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001266 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001267 return; // failure, fAddr is NULL
1268 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001269 if (!canvas->readPixels(&fBitmap, 0, 0)) {
1270 return; // failure, fAddr is NULL
1271 }
1272 fAddr = fBitmap.getPixels();
1273 fRowBytes = fBitmap.rowBytes();
1274 }
1275 SkASSERT(fAddr); // success
1276}
1277
1278bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1279 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001280 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001281 } else {
1282 bitmap->reset();
1283 return false;
1284 }
1285}
1286
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287/////////////////////////////////////////////////////////////////////////////
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001288void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289 const SkMatrix& matrix, const SkPaint* paint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001290 if (bitmap.drawsNothing()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291 return;
1292 }
1293
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001294 SkLazyPaint lazy;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 if (NULL == paint) {
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00001296 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 }
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001298
1299 SkDEBUGCODE(bitmap.validate();)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001300
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00001301 SkRect storage;
1302 const SkRect* bounds = NULL;
1303 if (paint && paint->canComputeFastBounds()) {
1304 bitmap.getBounds(&storage);
1305 matrix.mapRect(&storage);
1306 bounds = &paint->computeFastBounds(storage, &storage);
1307 }
1308
1309 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00001310
1311 while (iter.next()) {
1312 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
1313 }
1314
1315 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316}
1317
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001318void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001319 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 SkPaint tmp;
1321 if (NULL == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 paint = &tmp;
1323 }
reed@google.com4b226022011-01-11 18:32:13 +00001324
reed@google.com8926b162012-03-23 15:36:36 +00001325 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001327 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001328 paint = &looper.paint();
1329 SkImageFilter* filter = paint->getImageFilter();
1330 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001331 if (filter && !dstDev->canHandleImageFilter(filter)) {
robertphillipsefbffed2015-06-22 12:06:08 -07001332 SkImageFilter::Proxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001333 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001334 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001335 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001336 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001337 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001338 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancobe129b22014-08-08 07:14:35 -07001339 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001340 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001341 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001342 SkPaint tmpUnfiltered(*paint);
1343 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001344 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1345 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001346 }
reed61f501f2015-04-29 08:34:00 -07001347 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001348 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001349 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001350 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001351 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001352 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001353 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001354 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001355}
1356
reed41af9662015-01-05 07:49:08 -08001357void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
reed0acf1b42014-12-22 16:12:38 -08001358 if (gTreatSpriteAsBitmap) {
1359 this->save();
1360 this->resetMatrix();
1361 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1362 this->restore();
1363 return;
1364 }
1365
danakj9881d632014-11-26 12:41:06 -08001366 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001367 if (bitmap.drawsNothing()) {
reed@google.com8926b162012-03-23 15:36:36 +00001368 return;
1369 }
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00001370 SkDEBUGCODE(bitmap.validate();)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001371
reed@google.com8926b162012-03-23 15:36:36 +00001372 SkPaint tmp;
1373 if (NULL == paint) {
1374 paint = &tmp;
1375 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001376
reed@google.com8926b162012-03-23 15:36:36 +00001377 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001378
reed@google.com8926b162012-03-23 15:36:36 +00001379 while (iter.next()) {
1380 paint = &looper.paint();
1381 SkImageFilter* filter = paint->getImageFilter();
1382 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1383 if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
robertphillipsefbffed2015-06-22 12:06:08 -07001384 SkImageFilter::Proxy proxy(iter.fDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001385 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001386 SkIPoint offset = SkIPoint::Make(0, 0);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001387 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001388 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
halcanaryf622a6c2014-10-24 12:54:53 -07001389 const SkIRect clipBounds = bitmap.bounds();
senorblancobe129b22014-08-08 07:14:35 -07001390 SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
senorblanco55b6d8b2014-07-30 11:26:46 -07001391 SkImageFilter::Context ctx(matrix, clipBounds, cache.get());
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001392 if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001393 SkPaint tmpUnfiltered(*paint);
1394 tmpUnfiltered.setImageFilter(NULL);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001395 iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001396 tmpUnfiltered);
reed@google.com8926b162012-03-23 15:36:36 +00001397 }
1398 } else {
1399 iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
1400 }
1401 }
1402 LOOPER_END
1403}
1404
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001406void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001407 SkMatrix m;
1408 m.setTranslate(dx, dy);
1409 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410}
1411
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001412void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001413 SkMatrix m;
1414 m.setScale(sx, sy);
1415 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001416}
1417
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001418void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001419 SkMatrix m;
1420 m.setRotate(degrees);
1421 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422}
1423
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001424void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001425 SkMatrix m;
1426 m.setSkew(sx, sy);
1427 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001428}
1429
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001430void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001431 if (matrix.isIdentity()) {
1432 return;
1433 }
1434
reed2ff1fce2014-12-11 07:07:37 -08001435 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001436 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001437 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001438 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001439
1440 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001441}
1442
reed86a17e72015-05-14 12:25:22 -07001443void SkCanvas::setMatrix(const SkMatrix& matrix) {
1444 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001445 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001446 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001447 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001448 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449}
1450
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451void SkCanvas::resetMatrix() {
1452 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001453
reed@android.com8a1c16f2008-12-17 15:59:43 +00001454 matrix.reset();
1455 this->setMatrix(matrix);
1456}
1457
1458//////////////////////////////////////////////////////////////////////////////
1459
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001460void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001461 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001462 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1463 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001464}
1465
1466void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001467#ifdef SK_ENABLE_CLIP_QUICKREJECT
1468 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001469 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001470 return false;
1471 }
1472
reed@google.com3b3e8952012-08-16 20:53:31 +00001473 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001474 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001475 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001476
reed687fa1c2015-04-07 08:00:56 -07001477 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001478 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001479 }
1480 }
1481#endif
1482
reed@google.com5c3d1472011-02-22 19:12:23 +00001483 AutoValidateClip avc(this);
1484
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001486 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001487 if (!fAllowSoftClip) {
1488 edgeStyle = kHard_ClipEdgeStyle;
1489 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490
reed1f836ee2014-07-07 07:49:34 -07001491 if (fMCRec->fMatrix.rectStaysRect()) {
robertphillips@google.com12367192013-10-20 13:11:16 +00001492 // for these simpler matrices, we can stay a rect even after applying
reed@android.com98de2bd2009-03-02 19:41:36 +00001493 // the matrix. This means we don't have to a) make a path, and b) tell
1494 // the region code to scan-convert the path, only to discover that it
1495 // is really just a rect.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496 SkRect r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497
reed1f836ee2014-07-07 07:49:34 -07001498 fMCRec->fMatrix.mapRect(&r, rect);
reed687fa1c2015-04-07 08:00:56 -07001499 fClipStack->clipDevRect(r, op, kSoft_ClipEdgeStyle == edgeStyle);
reedd9544982014-09-09 18:46:22 -07001500 fMCRec->fRasterClip.op(r, this->getBaseLayerSize(), op, kSoft_ClipEdgeStyle == edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001502 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001503 // and clip against that, since it can handle any matrix. However, to
1504 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1505 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001506 SkPath path;
1507
1508 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001509 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510 }
1511}
1512
reed73e714e2014-09-04 09:02:23 -07001513static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1514 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001515 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001516}
1517
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001518void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001519 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001520 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001521 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001522 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1523 } else {
1524 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001525 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001527
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001529 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001530 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001531 AutoValidateClip avc(this);
1532
1533 fDeviceCMDirty = true;
1534 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001535 if (!fAllowSoftClip) {
1536 edgeStyle = kHard_ClipEdgeStyle;
1537 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001538
reed687fa1c2015-04-07 08:00:56 -07001539 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001540
1541 SkPath devPath;
1542 devPath.addRRect(transformedRRect);
1543
reed73e714e2014-09-04 09:02:23 -07001544 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001545 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001546 }
1547
1548 SkPath path;
1549 path.addRRect(rrect);
1550 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001551 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001552}
1553
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001554void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001555 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001556 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1557 SkRect r;
1558 if (!path.isInverseFillType() && path.isRect(&r)) {
1559 this->onClipRect(r, op, edgeStyle);
1560 } else {
1561 this->onClipPath(path, op, edgeStyle);
1562 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563}
1564
1565void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001566#ifdef SK_ENABLE_CLIP_QUICKREJECT
1567 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001568 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001569 return false;
1570 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001571
reed@google.com3b3e8952012-08-16 20:53:31 +00001572 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001573 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001574 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001575
reed687fa1c2015-04-07 08:00:56 -07001576 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001577 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001578 }
1579 }
1580#endif
1581
reed@google.com5c3d1472011-02-22 19:12:23 +00001582 AutoValidateClip avc(this);
1583
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001585 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001586 if (!fAllowSoftClip) {
1587 edgeStyle = kHard_ClipEdgeStyle;
1588 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589
1590 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001591 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001592
reed@google.comfe701122011-11-08 19:41:23 +00001593 // Check if the transfomation, or the original path itself
1594 // made us empty. Note this can also happen if we contained NaN
1595 // values. computing the bounds detects this, and will set our
1596 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1597 if (devPath.getBounds().isEmpty()) {
1598 // resetting the path will remove any NaN or other wanky values
1599 // that might upset our scan converter.
1600 devPath.reset();
1601 }
1602
reed@google.com5c3d1472011-02-22 19:12:23 +00001603 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001604 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001605
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001606 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001607 bool clipIsAA = getClipStack()->asPath(&devPath);
1608 if (clipIsAA) {
1609 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001610 }
fmalita1a481fe2015-02-04 07:39:34 -08001611
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001612 op = SkRegion::kReplace_Op;
1613 }
1614
reed73e714e2014-09-04 09:02:23 -07001615 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616}
1617
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001618void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001619 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001620 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621}
1622
1623void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001624 AutoValidateClip avc(this);
1625
reed@android.com8a1c16f2008-12-17 15:59:43 +00001626 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001627 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628
reed@google.com5c3d1472011-02-22 19:12:23 +00001629 // todo: signal fClipStack that we have a region, and therefore (I guess)
1630 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001631 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001632
reed1f836ee2014-07-07 07:49:34 -07001633 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001634}
1635
reed@google.com819c9212011-02-23 18:56:55 +00001636#ifdef SK_DEBUG
1637void SkCanvas::validateClip() const {
1638 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001639 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001640 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001641 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001642 return;
1643 }
1644
reed@google.com819c9212011-02-23 18:56:55 +00001645 SkIRect ir;
1646 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001647 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001648
reed687fa1c2015-04-07 08:00:56 -07001649 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001650 const SkClipStack::Element* element;
1651 while ((element = iter.next()) != NULL) {
1652 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001653 case SkClipStack::Element::kRect_Type:
1654 element->getRect().round(&ir);
1655 tmpClip.op(ir, element->getOp());
1656 break;
1657 case SkClipStack::Element::kEmpty_Type:
1658 tmpClip.setEmpty();
1659 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001660 default: {
1661 SkPath path;
1662 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001663 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001664 break;
1665 }
reed@google.com819c9212011-02-23 18:56:55 +00001666 }
1667 }
reed@google.com819c9212011-02-23 18:56:55 +00001668}
1669#endif
1670
reed@google.com90c07ea2012-04-13 13:50:27 +00001671void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001672 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001673 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001674
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001675 while ((element = iter.next()) != NULL) {
fmalitac3b589a2014-06-05 12:40:07 -07001676 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001677 }
1678}
1679
reed@google.com5c3d1472011-02-22 19:12:23 +00001680///////////////////////////////////////////////////////////////////////////////
1681
reed@google.com754de5f2014-02-24 19:38:20 +00001682bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001683 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001684}
1685
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001686bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001687 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001688}
1689
reed@google.com3b3e8952012-08-16 20:53:31 +00001690bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001691 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001692 return true;
1693
reed1f836ee2014-07-07 07:49:34 -07001694 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001695 return true;
1696 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001697
reed1f836ee2014-07-07 07:49:34 -07001698 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001699 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001700 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001701 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001702 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001703 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001704
reed@android.coma380ae42009-07-21 01:17:02 +00001705 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001706 // TODO: should we use | instead, or compare all 4 at once?
1707 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001708 return true;
1709 }
reed@google.comc0784db2013-12-13 21:16:12 +00001710 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001711 return true;
1712 }
1713 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001714 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715}
1716
reed@google.com3b3e8952012-08-16 20:53:31 +00001717bool SkCanvas::quickReject(const SkPath& path) const {
1718 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001719}
1720
reed@google.com3b3e8952012-08-16 20:53:31 +00001721bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001722 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001723 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001724 return false;
1725 }
1726
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001727 SkMatrix inverse;
1728 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001729 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001730 if (bounds) {
1731 bounds->setEmpty();
1732 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001733 return false;
1734 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001735
bsalomon49f085d2014-09-05 13:34:00 -07001736 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001737 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001738 // adjust it outwards in case we are antialiasing
1739 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001740
reed@google.com8f4d2302013-12-17 16:44:46 +00001741 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1742 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001743 inverse.mapRect(bounds, r);
1744 }
1745 return true;
1746}
1747
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001748bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001749 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001750 if (clip.isEmpty()) {
1751 if (bounds) {
1752 bounds->setEmpty();
1753 }
1754 return false;
1755 }
1756
bsalomon49f085d2014-09-05 13:34:00 -07001757 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001758 *bounds = clip.getBounds();
1759 }
1760 return true;
1761}
1762
reed@android.com8a1c16f2008-12-17 15:59:43 +00001763const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001764 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765}
1766
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001767const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001768 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001769}
1770
reed@google.com9c135db2014-03-12 18:28:35 +00001771GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1772 SkBaseDevice* dev = this->getTopDevice();
1773 return dev ? dev->accessRenderTarget() : NULL;
1774}
1775
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001776GrContext* SkCanvas::getGrContext() {
1777#if SK_SUPPORT_GPU
1778 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001779 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001780 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001781 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001782 return renderTarget->getContext();
1783 }
1784 }
1785#endif
1786
1787 return NULL;
1788
1789}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001790
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001791void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1792 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001793 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001794 if (outer.isEmpty()) {
1795 return;
1796 }
1797 if (inner.isEmpty()) {
1798 this->drawRRect(outer, paint);
1799 return;
1800 }
1801
1802 // We don't have this method (yet), but technically this is what we should
1803 // be able to assert...
1804 // SkASSERT(outer.contains(inner));
1805 //
1806 // For now at least check for containment of bounds
1807 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1808
1809 this->onDrawDRRect(outer, inner, paint);
1810}
1811
reed41af9662015-01-05 07:49:08 -08001812// These need to stop being virtual -- clients need to override the onDraw... versions
1813
1814void SkCanvas::drawPaint(const SkPaint& paint) {
1815 this->onDrawPaint(paint);
1816}
1817
1818void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1819 this->onDrawRect(r, paint);
1820}
1821
1822void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1823 this->onDrawOval(r, paint);
1824}
1825
1826void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1827 this->onDrawRRect(rrect, paint);
1828}
1829
1830void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1831 this->onDrawPoints(mode, count, pts, paint);
1832}
1833
1834void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1835 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1836 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1837 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1838 indices, indexCount, paint);
1839}
1840
1841void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1842 this->onDrawPath(path, paint);
1843}
1844
reeda85d4d02015-05-06 12:56:48 -07001845void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
1846 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001847}
1848
reede47829b2015-08-06 10:02:53 -07001849#ifdef SK_SUPPORT_LEGACY_SRCPTR_DRAWIMAGERECT
reed41af9662015-01-05 07:49:08 -08001850void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001851 const SkPaint* paint, SrcRectConstraint constraint) {
reeda85d4d02015-05-06 12:56:48 -07001852 if (dst.isEmpty()) {
1853 return;
1854 }
reed562fe472015-07-28 07:35:14 -07001855 this->onDrawImageRect(image, src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001856}
reede47829b2015-08-06 10:02:53 -07001857void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1858 const SkPaint* paint, SrcRectConstraint constraint) {
1859 if (bitmap.drawsNothing() || dst.isEmpty()) {
1860 return;
1861 }
1862 this->onDrawBitmapRect(bitmap, src, dst, paint, constraint);
1863}
1864#endif
1865
1866void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1867 const SkPaint* paint, SrcRectConstraint constraint) {
1868 if (dst.isEmpty() || src.isEmpty()) {
1869 return;
1870 }
1871 this->onDrawImageRect(image, &src, dst, paint, constraint);
1872}
reed41af9662015-01-05 07:49:08 -08001873
reed84984ef2015-07-17 07:09:43 -07001874void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1875 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001876 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001877}
1878
reede47829b2015-08-06 10:02:53 -07001879#ifdef SK_SUPPORT_LEGACY_SIMPLE_DRAWIMAGERECT
1880void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) {
1881 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint);
1882}
1883#else
1884void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1885 SrcRectConstraint constraint) {
1886 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1887 constraint);
1888}
1889#endif
1890
reed4c21dc52015-06-25 12:32:03 -07001891void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1892 const SkPaint* paint) {
1893 if (dst.isEmpty()) {
1894 return;
1895 }
1896 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001897 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001898 }
1899 this->onDrawImageNine(image, center, dst, paint);
1900}
1901
reed41af9662015-01-05 07:49:08 -08001902void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001903 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001904 return;
1905 }
reed41af9662015-01-05 07:49:08 -08001906 this->onDrawBitmap(bitmap, dx, dy, paint);
1907}
1908
reede47829b2015-08-06 10:02:53 -07001909void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001910 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001911 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001912 return;
1913 }
reede47829b2015-08-06 10:02:53 -07001914 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001915}
1916
reed84984ef2015-07-17 07:09:43 -07001917void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1918 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001919 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001920}
1921
reede47829b2015-08-06 10:02:53 -07001922#ifdef SK_SUPPORT_LEGACY_SIMPLE_DRAWIMAGERECT
1923void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint) {
1924 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint);
1925}
1926#else
1927void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1928 SrcRectConstraint constraint) {
1929 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1930 constraint);
1931}
1932#endif
1933
reed41af9662015-01-05 07:49:08 -08001934void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1935 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001936 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001937 return;
1938 }
reed4c21dc52015-06-25 12:32:03 -07001939 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07001940 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001941 }
reed41af9662015-01-05 07:49:08 -08001942 this->onDrawBitmapNine(bitmap, center, dst, paint);
1943}
1944
1945void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001946 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001947 return;
1948 }
reed41af9662015-01-05 07:49:08 -08001949 this->onDrawSprite(bitmap, left, top, paint);
1950}
1951
reed71c3c762015-06-24 10:29:17 -07001952void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
1953 const SkColor colors[], int count, SkXfermode::Mode mode,
1954 const SkRect* cull, const SkPaint* paint) {
1955 if (count <= 0) {
1956 return;
1957 }
1958 SkASSERT(atlas);
1959 SkASSERT(xform);
1960 SkASSERT(tex);
1961 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
1962}
1963
reede47829b2015-08-06 10:02:53 -07001964void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1965 const SkPaint* paint, SrcRectConstraint constraint) {
1966 if (src) {
1967 this->drawImageRect(image, *src, dst, paint, constraint);
1968 } else {
1969 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
1970 dst, paint, constraint);
1971 }
1972}
1973void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
1974 const SkPaint* paint, SrcRectConstraint constraint) {
1975 if (src) {
1976 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
1977 } else {
1978 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
1979 dst, paint, constraint);
1980 }
1981}
1982
reed@android.com8a1c16f2008-12-17 15:59:43 +00001983//////////////////////////////////////////////////////////////////////////////
1984// These are the virtual drawing methods
1985//////////////////////////////////////////////////////////////////////////////
1986
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001987void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07001988 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00001989 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
1990 }
1991}
1992
reed41af9662015-01-05 07:49:08 -08001993void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001994 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00001995 this->internalDrawPaint(paint);
1996}
1997
1998void SkCanvas::internalDrawPaint(const SkPaint& paint) {
reedc83a2972015-07-16 07:40:45 -07001999 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, NULL, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002000
2001 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002002 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002003 }
2004
reed@google.com4e2b3d32011-04-07 14:18:59 +00002005 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002006}
2007
reed41af9662015-01-05 07:49:08 -08002008void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2009 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002010 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002011 if ((long)count <= 0) {
2012 return;
2013 }
2014
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002015 SkRect r, storage;
2016 const SkRect* bounds = NULL;
reed@google.coma584aed2012-05-16 14:06:02 +00002017 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002018 // special-case 2 points (common for drawing a single line)
2019 if (2 == count) {
2020 r.set(pts[0], pts[1]);
2021 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002022 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002023 }
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002024 bounds = &paint.computeFastStrokeBounds(r, &storage);
2025 if (this->quickReject(*bounds)) {
reed@google.coma584aed2012-05-16 14:06:02 +00002026 return;
2027 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002028 }
reed@google.coma584aed2012-05-16 14:06:02 +00002029
reed@android.com8a1c16f2008-12-17 15:59:43 +00002030 SkASSERT(pts != NULL);
2031
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002032 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002033
reed@android.com8a1c16f2008-12-17 15:59:43 +00002034 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002035 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002036 }
reed@google.com4b226022011-01-11 18:32:13 +00002037
reed@google.com4e2b3d32011-04-07 14:18:59 +00002038 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002039}
2040
reed41af9662015-01-05 07:49:08 -08002041void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002042 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002043 SkRect storage;
2044 const SkRect* bounds = NULL;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002045 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002046 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2047 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2048 SkRect tmp(r);
2049 tmp.sort();
2050
2051 bounds = &paint.computeFastBounds(tmp, &storage);
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002052 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002053 return;
2054 }
2055 }
reed@google.com4b226022011-01-11 18:32:13 +00002056
reedc83a2972015-07-16 07:40:45 -07002057 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002058
2059 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002060 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061 }
2062
reed@google.com4e2b3d32011-04-07 14:18:59 +00002063 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064}
2065
reed41af9662015-01-05 07:49:08 -08002066void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002067 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002068 SkRect storage;
2069 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002070 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002071 bounds = &paint.computeFastBounds(oval, &storage);
2072 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002073 return;
2074 }
2075 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002076
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002077 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002078
2079 while (iter.next()) {
2080 iter.fDevice->drawOval(iter, oval, looper.paint());
2081 }
2082
2083 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002084}
2085
reed41af9662015-01-05 07:49:08 -08002086void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002087 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002088 SkRect storage;
2089 const SkRect* bounds = NULL;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002090 if (paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002091 bounds = &paint.computeFastBounds(rrect.getBounds(), &storage);
2092 if (this->quickReject(*bounds)) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002093 return;
2094 }
2095 }
2096
2097 if (rrect.isRect()) {
2098 // call the non-virtual version
2099 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002100 return;
2101 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002102 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002103 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2104 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002105 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002106
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002107 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002108
2109 while (iter.next()) {
2110 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2111 }
2112
2113 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002114}
2115
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002116void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2117 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002118 SkRect storage;
2119 const SkRect* bounds = NULL;
2120 if (paint.canComputeFastBounds()) {
2121 bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
2122 if (this->quickReject(*bounds)) {
2123 return;
2124 }
2125 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002126
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002127 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002128
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002129 while (iter.next()) {
2130 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2131 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002132
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002133 LOOPER_END
2134}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002135
reed41af9662015-01-05 07:49:08 -08002136void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002137 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002138 if (!path.isFinite()) {
2139 return;
2140 }
2141
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002142 SkRect storage;
2143 const SkRect* bounds = NULL;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002144 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002145 const SkRect& pathBounds = path.getBounds();
2146 bounds = &paint.computeFastBounds(pathBounds, &storage);
2147 if (this->quickReject(*bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002148 return;
2149 }
2150 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002151
2152 const SkRect& r = path.getBounds();
2153 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002154 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002155 this->internalDrawPaint(paint);
2156 }
2157 return;
2158 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002159
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002160 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002161
2162 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002163 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002164 }
2165
reed@google.com4e2b3d32011-04-07 14:18:59 +00002166 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002167}
2168
reeda85d4d02015-05-06 12:56:48 -07002169void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002170 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002171 SkRect bounds = SkRect::MakeXYWH(x, y,
2172 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
2173 if (NULL == paint || paint->canComputeFastBounds()) {
2174 if (paint) {
2175 paint->computeFastBounds(bounds, &bounds);
2176 }
2177 if (this->quickReject(bounds)) {
2178 return;
2179 }
2180 }
2181
2182 SkLazyPaint lazy;
2183 if (NULL == paint) {
2184 paint = lazy.init();
2185 }
2186
2187 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds)
2188
2189 while (iter.next()) {
2190 iter.fDevice->drawImage(iter, image, x, y, looper.paint());
2191 }
2192
2193 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002194}
2195
reed41af9662015-01-05 07:49:08 -08002196void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002197 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002198 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
reeda85d4d02015-05-06 12:56:48 -07002199 SkRect storage;
2200 const SkRect* bounds = &dst;
2201 if (NULL == paint || paint->canComputeFastBounds()) {
2202 if (paint) {
2203 bounds = &paint->computeFastBounds(dst, &storage);
2204 }
2205 if (this->quickReject(*bounds)) {
2206 return;
2207 }
2208 }
2209 SkLazyPaint lazy;
2210 if (NULL == paint) {
2211 paint = lazy.init();
2212 }
2213
reedc83a2972015-07-16 07:40:45 -07002214 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2215 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002216
2217 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002218 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002219 }
2220
2221 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002222}
2223
reed41af9662015-01-05 07:49:08 -08002224void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002225 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002226 SkDEBUGCODE(bitmap.validate();)
2227
reed@google.com3d608122011-11-21 15:16:16 +00002228 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002229 SkRect bounds = {
2230 x, y,
2231 x + SkIntToScalar(bitmap.width()),
2232 y + SkIntToScalar(bitmap.height())
2233 };
2234 if (paint) {
2235 (void)paint->computeFastBounds(bounds, &bounds);
2236 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002237 if (this->quickReject(bounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002238 return;
2239 }
2240 }
reed@google.com4b226022011-01-11 18:32:13 +00002241
reed@android.com8a1c16f2008-12-17 15:59:43 +00002242 SkMatrix matrix;
2243 matrix.setTranslate(x, y);
robertphillips@google.com9bf380c2013-07-25 12:10:42 +00002244 this->internalDrawBitmap(bitmap, matrix, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245}
2246
reed@google.com9987ec32011-09-07 11:57:52 +00002247// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002248void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002249 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002250 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002251 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002252 return;
2253 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002254
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002255 SkRect storage;
2256 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002257 if (NULL == paint || paint->canComputeFastBounds()) {
reed@google.com9efd9a02012-01-30 15:41:43 +00002258 if (paint) {
2259 bounds = &paint->computeFastBounds(dst, &storage);
2260 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002261 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002262 return;
2263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002264 }
reed@google.com3d608122011-11-21 15:16:16 +00002265
reed@google.com33535f32012-09-25 15:37:50 +00002266 SkLazyPaint lazy;
2267 if (NULL == paint) {
2268 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002269 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002270
reedc83a2972015-07-16 07:40:45 -07002271 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, bounds,
2272 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002273
reed@google.com33535f32012-09-25 15:37:50 +00002274 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002275 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002276 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002277
reed@google.com33535f32012-09-25 15:37:50 +00002278 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279}
2280
reed41af9662015-01-05 07:49:08 -08002281void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002282 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002283 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002284 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002285 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002286}
2287
reed4c21dc52015-06-25 12:32:03 -07002288void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2289 const SkPaint* paint) {
2290 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2291
2292 SkRect storage;
2293 const SkRect* bounds = &dst;
reed@google.com3d608122011-11-21 15:16:16 +00002294 if (NULL == paint || paint->canComputeFastBounds()) {
djsollen@google.com60abb072012-02-15 18:49:15 +00002295 if (paint) {
2296 bounds = &paint->computeFastBounds(dst, &storage);
2297 }
reed@google.com3b3e8952012-08-16 20:53:31 +00002298 if (this->quickReject(*bounds)) {
reed@google.com3d608122011-11-21 15:16:16 +00002299 return;
2300 }
2301 }
reed4c21dc52015-06-25 12:32:03 -07002302
2303 SkLazyPaint lazy;
2304 if (NULL == paint) {
2305 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002306 }
reed4c21dc52015-06-25 12:32:03 -07002307
2308 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2309
2310 while (iter.next()) {
2311 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002312 }
reed4c21dc52015-06-25 12:32:03 -07002313
2314 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002315}
2316
reed41af9662015-01-05 07:49:08 -08002317void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2318 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002319 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002320 SkDEBUGCODE(bitmap.validate();)
2321
reed4c21dc52015-06-25 12:32:03 -07002322 SkRect storage;
2323 const SkRect* bounds = &dst;
2324 if (NULL == paint || paint->canComputeFastBounds()) {
2325 if (paint) {
2326 bounds = &paint->computeFastBounds(dst, &storage);
2327 }
2328 if (this->quickReject(*bounds)) {
2329 return;
2330 }
2331 }
2332
2333 SkLazyPaint lazy;
2334 if (NULL == paint) {
2335 paint = lazy.init();
2336 }
2337
2338 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
2339
2340 while (iter.next()) {
2341 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2342 }
2343
2344 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002345}
2346
reed@google.comf67e4cf2011-03-15 20:56:58 +00002347class SkDeviceFilteredPaint {
2348public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002349 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002350 uint32_t filteredFlags = device->filterTextFlags(paint);
2351 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002352 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002353 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002354 fPaint = newPaint;
2355 } else {
2356 fPaint = &paint;
2357 }
2358 }
2359
reed@google.comf67e4cf2011-03-15 20:56:58 +00002360 const SkPaint& paint() const { return *fPaint; }
2361
2362private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002363 const SkPaint* fPaint;
2364 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002365};
2366
bungeman@google.com52c748b2011-08-22 21:30:43 +00002367void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2368 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002369 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002370 draw.fDevice->drawRect(draw, r, paint);
2371 } else {
2372 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002373 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002374 draw.fDevice->drawRect(draw, r, p);
2375 }
2376}
2377
2378void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2379 const char text[], size_t byteLength,
2380 SkScalar x, SkScalar y) {
2381 SkASSERT(byteLength == 0 || text != NULL);
2382
2383 // nothing to draw
2384 if (text == NULL || byteLength == 0 ||
2385 draw.fClip->isEmpty() ||
2386 (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
2387 return;
2388 }
2389
2390 SkScalar width = 0;
2391 SkPoint start;
2392
2393 start.set(0, 0); // to avoid warning
2394 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2395 SkPaint::kStrikeThruText_Flag)) {
2396 width = paint.measureText(text, byteLength);
2397
2398 SkScalar offsetX = 0;
2399 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2400 offsetX = SkScalarHalf(width);
2401 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2402 offsetX = width;
2403 }
2404 start.set(x - offsetX, y);
2405 }
2406
2407 if (0 == width) {
2408 return;
2409 }
2410
2411 uint32_t flags = paint.getFlags();
2412
2413 if (flags & (SkPaint::kUnderlineText_Flag |
2414 SkPaint::kStrikeThruText_Flag)) {
2415 SkScalar textSize = paint.getTextSize();
2416 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2417 SkRect r;
2418
2419 r.fLeft = start.fX;
2420 r.fRight = start.fX + width;
2421
2422 if (flags & SkPaint::kUnderlineText_Flag) {
2423 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2424 start.fY);
2425 r.fTop = offset;
2426 r.fBottom = offset + height;
2427 DrawRect(draw, paint, r, textSize);
2428 }
2429 if (flags & SkPaint::kStrikeThruText_Flag) {
2430 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2431 start.fY);
2432 r.fTop = offset;
2433 r.fBottom = offset + height;
2434 DrawRect(draw, paint, r, textSize);
2435 }
2436 }
2437}
2438
reed@google.come0d9ce82014-04-23 04:00:17 +00002439void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2440 const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002441 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442
2443 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002444 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002445 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002446 DrawTextDecorations(iter, dfp.paint(),
2447 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002448 }
2449
reed@google.com4e2b3d32011-04-07 14:18:59 +00002450 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002451}
2452
reed@google.come0d9ce82014-04-23 04:00:17 +00002453void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2454 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002455 SkPoint textOffset = SkPoint::Make(0, 0);
2456
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002457 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002458
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002460 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002461 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002462 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002463 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002464
reed@google.com4e2b3d32011-04-07 14:18:59 +00002465 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002466}
2467
reed@google.come0d9ce82014-04-23 04:00:17 +00002468void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2469 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002470
2471 SkPoint textOffset = SkPoint::Make(0, constY);
2472
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002473 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002474
reed@android.com8a1c16f2008-12-17 15:59:43 +00002475 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002476 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002477 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002478 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002479 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002480
reed@google.com4e2b3d32011-04-07 14:18:59 +00002481 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482}
2483
reed@google.come0d9ce82014-04-23 04:00:17 +00002484void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2485 const SkMatrix* matrix, const SkPaint& paint) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002486 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, NULL)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002487
reed@android.com8a1c16f2008-12-17 15:59:43 +00002488 while (iter.next()) {
2489 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002490 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002491 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002492
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002493 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002494}
2495
fmalita00d5c2c2014-08-21 08:53:26 -07002496void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2497 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002498
fmalita85d5eb92015-03-04 11:20:12 -08002499 SkRect storage;
2500 const SkRect* bounds = NULL;
fmalita19653d12014-10-16 11:53:30 -07002501 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002502 storage = blob->bounds().makeOffset(x, y);
2503 bounds = &paint.computeFastBounds(storage, &storage);
fmalita7ba7aa72014-08-29 09:46:36 -07002504
fmalita85d5eb92015-03-04 11:20:12 -08002505 if (this->quickReject(*bounds)) {
fmalita7ba7aa72014-08-29 09:46:36 -07002506 return;
2507 }
2508 }
2509
fmalita024f9962015-03-03 19:08:17 -08002510 // We cannot filter in the looper as we normally do, because the paint is
2511 // incomplete at this point (text-related attributes are embedded within blob run paints).
2512 SkDrawFilter* drawFilter = fMCRec->fFilter;
2513 fMCRec->fFilter = NULL;
2514
fmalita85d5eb92015-03-04 11:20:12 -08002515 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002516
fmalitaaa1b9122014-08-28 14:32:24 -07002517 while (iter.next()) {
2518 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002519 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002520 }
2521
fmalitaaa1b9122014-08-28 14:32:24 -07002522 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002523
2524 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002525}
2526
reed@google.come0d9ce82014-04-23 04:00:17 +00002527// These will become non-virtual, so they always call the (virtual) onDraw... method
2528void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2529 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002530 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002531 this->onDrawText(text, byteLength, x, y, paint);
2532}
2533void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2534 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002535 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002536 this->onDrawPosText(text, byteLength, pos, paint);
2537}
2538void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2539 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002540 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002541 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2542}
2543void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2544 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002545 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002546 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2547}
fmalita00d5c2c2014-08-21 08:53:26 -07002548void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2549 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002550 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002551 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002552 this->onDrawTextBlob(blob, x, y, paint);
2553 }
2554}
reed@google.come0d9ce82014-04-23 04:00:17 +00002555
reed41af9662015-01-05 07:49:08 -08002556void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2557 const SkPoint verts[], const SkPoint texs[],
2558 const SkColor colors[], SkXfermode* xmode,
2559 const uint16_t indices[], int indexCount,
2560 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002561 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002562 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
reed@google.com4b226022011-01-11 18:32:13 +00002563
reed@android.com8a1c16f2008-12-17 15:59:43 +00002564 while (iter.next()) {
2565 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002566 colors, xmode, indices, indexCount,
2567 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002568 }
reed@google.com4b226022011-01-11 18:32:13 +00002569
reed@google.com4e2b3d32011-04-07 14:18:59 +00002570 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002571}
2572
dandovb3c9d1c2014-08-12 08:34:29 -07002573void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2574 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002575 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
dandovb3c9d1c2014-08-12 08:34:29 -07002576 if (NULL == cubics) {
2577 return;
2578 }
mtklein6cfa73a2014-08-13 13:33:49 -07002579
dandovecfff212014-08-04 10:02:00 -07002580 // Since a patch is always within the convex hull of the control points, we discard it when its
2581 // bounding rectangle is completely outside the current clip.
2582 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002583 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002584 if (this->quickReject(bounds)) {
2585 return;
2586 }
mtklein6cfa73a2014-08-13 13:33:49 -07002587
dandovb3c9d1c2014-08-12 08:34:29 -07002588 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2589}
2590
2591void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2592 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2593
dandovecfff212014-08-04 10:02:00 -07002594 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
mtklein6cfa73a2014-08-13 13:33:49 -07002595
dandovecfff212014-08-04 10:02:00 -07002596 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002597 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002598 }
mtklein6cfa73a2014-08-13 13:33:49 -07002599
dandovecfff212014-08-04 10:02:00 -07002600 LOOPER_END
2601}
2602
reeda8db7282015-07-07 10:22:31 -07002603void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2604 if (dr) {
2605 if (x || y) {
2606 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2607 this->onDrawDrawable(dr, &matrix);
2608 } else {
2609 this->onDrawDrawable(dr, NULL);
2610 }
reed6a070dc2014-11-11 19:36:09 -08002611 }
2612}
2613
reeda8db7282015-07-07 10:22:31 -07002614void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2615 if (dr) {
2616 if (matrix && matrix->isIdentity()) {
2617 matrix = NULL;
2618 }
2619 this->onDrawDrawable(dr, matrix);
2620 }
2621}
2622
2623void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2624 SkRect bounds = dr->getBounds();
2625 if (matrix) {
2626 matrix->mapRect(&bounds);
2627 }
2628 if (this->quickReject(bounds)) {
2629 return;
2630 }
2631 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002632}
2633
reed71c3c762015-06-24 10:29:17 -07002634void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2635 const SkColor colors[], int count, SkXfermode::Mode mode,
2636 const SkRect* cull, const SkPaint* paint) {
2637 if (cull && this->quickReject(*cull)) {
2638 return;
2639 }
2640
2641 SkPaint pnt;
2642 if (paint) {
2643 pnt = *paint;
2644 }
2645
2646 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, NULL)
2647 while (iter.next()) {
2648 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2649 }
2650 LOOPER_END
2651}
2652
reed@android.com8a1c16f2008-12-17 15:59:43 +00002653//////////////////////////////////////////////////////////////////////////////
2654// These methods are NOT virtual, and therefore must call back into virtual
2655// methods, rather than actually drawing themselves.
2656//////////////////////////////////////////////////////////////////////////////
2657
2658void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002659 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002660 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002661 SkPaint paint;
2662
2663 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002664 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002665 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002666 }
2667 this->drawPaint(paint);
2668}
2669
reed@android.com845fdac2009-06-23 03:01:32 +00002670void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002671 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002672 SkPaint paint;
2673
2674 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002675 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002676 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002677 }
2678 this->drawPaint(paint);
2679}
2680
2681void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002682 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002683 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002684
reed@android.com8a1c16f2008-12-17 15:59:43 +00002685 pt.set(x, y);
2686 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2687}
2688
2689void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002690 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002691 SkPoint pt;
2692 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002693
reed@android.com8a1c16f2008-12-17 15:59:43 +00002694 pt.set(x, y);
2695 paint.setColor(color);
2696 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2697}
2698
2699void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2700 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002701 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002702 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002703
reed@android.com8a1c16f2008-12-17 15:59:43 +00002704 pts[0].set(x0, y0);
2705 pts[1].set(x1, y1);
2706 this->drawPoints(kLines_PointMode, 2, pts, paint);
2707}
2708
2709void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2710 SkScalar right, SkScalar bottom,
2711 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002712 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002713 SkRect r;
2714
2715 r.set(left, top, right, bottom);
2716 this->drawRect(r, paint);
2717}
2718
2719void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2720 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002721 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002722 if (radius < 0) {
2723 radius = 0;
2724 }
2725
2726 SkRect r;
2727 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002728 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002729}
2730
2731void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2732 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002733 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002734 if (rx > 0 && ry > 0) {
2735 if (paint.canComputeFastBounds()) {
2736 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002737 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002738 return;
2739 }
2740 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002741 SkRRect rrect;
2742 rrect.setRectXY(r, rx, ry);
2743 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002744 } else {
2745 this->drawRect(r, paint);
2746 }
2747}
2748
reed@android.com8a1c16f2008-12-17 15:59:43 +00002749void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2750 SkScalar sweepAngle, bool useCenter,
2751 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002752 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002753 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2754 this->drawOval(oval, paint);
2755 } else {
2756 SkPath path;
2757 if (useCenter) {
2758 path.moveTo(oval.centerX(), oval.centerY());
2759 }
2760 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2761 if (useCenter) {
2762 path.close();
2763 }
2764 this->drawPath(path, paint);
2765 }
2766}
2767
2768void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2769 const SkPath& path, SkScalar hOffset,
2770 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002771 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002772 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002773
reed@android.com8a1c16f2008-12-17 15:59:43 +00002774 matrix.setTranslate(hOffset, vOffset);
2775 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2776}
2777
reed@android.comf76bacf2009-05-13 14:00:33 +00002778///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002779
2780/**
2781 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2782 * against the playback cost of recursing into the subpicture to get at its actual ops.
2783 *
2784 * For now we pick a conservatively small value, though measurement (and other heuristics like
2785 * the type of ops contained) may justify changing this value.
2786 */
2787#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002788
reedd5fa1a42014-08-09 11:08:05 -07002789void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002790 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002791 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002792 if (matrix && matrix->isIdentity()) {
2793 matrix = NULL;
2794 }
reed1c2c4412015-04-30 13:09:24 -07002795 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2796 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2797 picture->playback(this);
2798 } else {
2799 this->onDrawPicture(picture, matrix, paint);
2800 }
reedd5fa1a42014-08-09 11:08:05 -07002801 }
2802}
robertphillips9b14f262014-06-04 05:40:44 -07002803
reedd5fa1a42014-08-09 11:08:05 -07002804void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2805 const SkPaint* paint) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002806 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002807 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002808 // Canvas has to first give the device the opportunity to render
2809 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002810 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002811 return; // the device has rendered the entire picture
2812 }
2813 }
2814
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002815 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002816 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002817}
2818
reed@android.com8a1c16f2008-12-17 15:59:43 +00002819///////////////////////////////////////////////////////////////////////////////
2820///////////////////////////////////////////////////////////////////////////////
2821
2822SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
reed@google.comd6423292010-12-20 20:53:13 +00002823 SK_COMPILE_ASSERT(sizeof(fStorage) >= sizeof(SkDrawIter), fStorage_too_small);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002824
2825 SkASSERT(canvas);
2826
2827 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2828 fDone = !fImpl->next();
2829}
2830
2831SkCanvas::LayerIter::~LayerIter() {
2832 fImpl->~SkDrawIter();
2833}
2834
2835void SkCanvas::LayerIter::next() {
2836 fDone = !fImpl->next();
2837}
2838
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002839SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002840 return fImpl->getDevice();
2841}
2842
2843const SkMatrix& SkCanvas::LayerIter::matrix() const {
2844 return fImpl->getMatrix();
2845}
2846
2847const SkPaint& SkCanvas::LayerIter::paint() const {
2848 const SkPaint* paint = fImpl->getPaint();
2849 if (NULL == paint) {
2850 paint = &fDefaultPaint;
2851 }
2852 return *paint;
2853}
2854
2855const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2856int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2857int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002858
2859///////////////////////////////////////////////////////////////////////////////
2860
fmalitac3b589a2014-06-05 12:40:07 -07002861SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002862
2863///////////////////////////////////////////////////////////////////////////////
2864
2865static bool supported_for_raster_canvas(const SkImageInfo& info) {
2866 switch (info.alphaType()) {
2867 case kPremul_SkAlphaType:
2868 case kOpaque_SkAlphaType:
2869 break;
2870 default:
2871 return false;
2872 }
2873
2874 switch (info.colorType()) {
2875 case kAlpha_8_SkColorType:
2876 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00002877 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002878 break;
2879 default:
2880 return false;
2881 }
2882
2883 return true;
2884}
2885
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002886SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
2887 if (!supported_for_raster_canvas(info)) {
2888 return NULL;
2889 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00002890
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002891 SkBitmap bitmap;
2892 if (!bitmap.installPixels(info, pixels, rowBytes)) {
2893 return NULL;
2894 }
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00002895 return SkNEW_ARGS(SkCanvas, (bitmap));
2896}
reedd5fa1a42014-08-09 11:08:05 -07002897
2898///////////////////////////////////////////////////////////////////////////////
2899
2900SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002901 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07002902 : fCanvas(canvas)
2903 , fSaveCount(canvas->getSaveCount())
2904{
bsalomon49f085d2014-09-05 13:34:00 -07002905 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002906 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07002907 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002908 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07002909 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002910 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07002911 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002912 canvas->save();
2913 }
mtklein6cfa73a2014-08-13 13:33:49 -07002914
bsalomon49f085d2014-09-05 13:34:00 -07002915 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07002916 canvas->concat(*matrix);
2917 }
2918}
2919
2920SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
2921 fCanvas->restoreToCount(fSaveCount);
2922}