blob: 814375687a743b7a897a88a2a6ce2f1682075359 [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"
reed262a71b2015-12-05 13:07:27 -080020#include "SkImage_Base.h"
21#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000022#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070023#include "SkNinePatchIter.h"
reedc83a2972015-07-16 07:40:45 -070024#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070025#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000026#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000027#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080028#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000029#include "SkRRect.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000030#include "SkSmallAllocator.h"
reed@google.com97af1a62012-08-28 12:19:02 +000031#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070032#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000033#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000034#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080035#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070036
37#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000038
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000039#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080040#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000041#include "GrRenderTarget.h"
robertphillips7354a4b2015-12-16 05:08:27 -080042#include "SkGr.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000043#endif
44
reedc83a2972015-07-16 07:40:45 -070045/*
46 * Return true if the drawing this rect would hit every pixels in the canvas.
47 *
48 * Returns false if
49 * - rect does not contain the canvas' bounds
50 * - paint is not fill
51 * - paint would blur or otherwise change the coverage of the rect
52 */
53bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
54 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070055 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
56 (int)kNone_ShaderOverrideOpacity,
57 "need_matching_enums0");
58 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
59 (int)kOpaque_ShaderOverrideOpacity,
60 "need_matching_enums1");
61 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
62 (int)kNotOpaque_ShaderOverrideOpacity,
63 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070064
65 const SkISize size = this->getBaseLayerSize();
66 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
67 if (!this->getClipStack()->quickContains(bounds)) {
68 return false;
69 }
70
71 if (rect) {
72 if (!this->getTotalMatrix().rectStaysRect()) {
73 return false; // conservative
74 }
75
76 SkRect devRect;
77 this->getTotalMatrix().mapRect(&devRect, *rect);
fmalita8c0144c2015-07-22 05:56:16 -070078 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070079 return false;
80 }
81 }
82
83 if (paint) {
84 SkPaint::Style paintStyle = paint->getStyle();
85 if (!(paintStyle == SkPaint::kFill_Style ||
86 paintStyle == SkPaint::kStrokeAndFill_Style)) {
87 return false;
88 }
89 if (paint->getMaskFilter() || paint->getLooper()
90 || paint->getPathEffect() || paint->getImageFilter()) {
91 return false; // conservative
92 }
93 }
94 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
95}
96
97///////////////////////////////////////////////////////////////////////////////////////////////////
98
reedd990e2f2014-12-22 11:58:30 -080099static bool gIgnoreSaveLayerBounds;
100void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
101 gIgnoreSaveLayerBounds = ignore;
102}
103bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
104 return gIgnoreSaveLayerBounds;
105}
106
reed0acf1b42014-12-22 16:12:38 -0800107static bool gTreatSpriteAsBitmap;
108void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
109 gTreatSpriteAsBitmap = spriteAsBitmap;
110}
111bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
112 return gTreatSpriteAsBitmap;
113}
114
reed@google.comda17f752012-08-16 18:27:05 +0000115// experimental for faster tiled drawing...
116//#define SK_ENABLE_CLIP_QUICKREJECT
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +0000117
reed@android.com8a1c16f2008-12-17 15:59:43 +0000118//#define SK_TRACE_SAVERESTORE
119
120#ifdef SK_TRACE_SAVERESTORE
121 static int gLayerCounter;
122 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
123 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
124
125 static int gRecCounter;
126 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
127 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
128
129 static int gCanvasCounter;
130 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
131 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
132#else
133 #define inc_layer()
134 #define dec_layer()
135 #define inc_rec()
136 #define dec_rec()
137 #define inc_canvas()
138 #define dec_canvas()
139#endif
140
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000141typedef SkTLazy<SkPaint> SkLazyPaint;
142
reedc83a2972015-07-16 07:40:45 -0700143void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000144 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700145 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
146 ? SkSurface::kDiscard_ContentChangeMode
147 : SkSurface::kRetain_ContentChangeMode);
148 }
149}
150
151void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
152 ShaderOverrideOpacity overrideOpacity) {
153 if (fSurfaceBase) {
154 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
155 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
156 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
157 // and therefore we don't care which mode we're in.
158 //
159 if (fSurfaceBase->outstandingImageSnapshot()) {
160 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
161 mode = SkSurface::kDiscard_ContentChangeMode;
162 }
163 }
164 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000165 }
166}
167
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169
reed4a8126e2014-09-22 07:29:03 -0700170static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
171 const uint32_t propFlags = props.flags();
172 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
173 flags &= ~SkPaint::kDither_Flag;
174 }
175 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
176 flags &= ~SkPaint::kAntiAlias_Flag;
177 }
178 return flags;
179}
180
181///////////////////////////////////////////////////////////////////////////////
182
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000183/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 The clip/matrix/proc are fields that reflect the top of the save/restore
185 stack. Whenever the canvas changes, it marks a dirty flag, and then before
186 these are used (assuming we're not on a layer) we rebuild these cache
187 values: they reflect the top of the save stack, but translated and clipped
188 by the device's XY offset and bitmap-bounds.
189*/
190struct DeviceCM {
191 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000192 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000193 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000194 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700195 const SkMatrix* fMatrix;
196 SkMatrix fMatrixStorage;
197 const bool fDeviceIsBitmapDevice;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198
reed96e657d2015-03-10 17:30:07 -0700199 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed86a17e72015-05-14 12:25:22 -0700200 bool conservativeRasterClip, bool deviceIsBitmapDevice)
halcanary96fcdcc2015-08-27 07:41:13 -0700201 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700202 , fClip(conservativeRasterClip)
reed61f501f2015-04-29 08:34:00 -0700203 , fDeviceIsBitmapDevice(deviceIsBitmapDevice)
reedd9544982014-09-09 18:46:22 -0700204 {
halcanary96fcdcc2015-08-27 07:41:13 -0700205 if (nullptr != device) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206 device->ref();
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000207 device->onAttachToCanvas(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 }
reed@google.com4b226022011-01-11 18:32:13 +0000209 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700210 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000211 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000212
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000213 ~DeviceCM() {
bsalomon49f085d2014-09-05 13:34:00 -0700214 if (fDevice) {
robertphillips@google.com40a1ae42012-07-13 15:36:15 +0000215 fDevice->onDetachFromCanvas();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000216 fDevice->unref();
217 }
halcanary385fe4d2015-08-26 13:07:48 -0700218 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000219 }
reed@google.com4b226022011-01-11 18:32:13 +0000220
mtkleinfeaadee2015-04-08 11:25:48 -0700221 void reset(const SkIRect& bounds) {
222 SkASSERT(!fPaint);
223 SkASSERT(!fNext);
224 SkASSERT(fDevice);
225 fClip.setRect(bounds);
226 }
227
reed@google.com045e62d2011-10-24 12:19:46 +0000228 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
229 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000230 int x = fDevice->getOrigin().x();
231 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000232 int width = fDevice->width();
233 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000234
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235 if ((x | y) == 0) {
236 fMatrix = &totalMatrix;
237 fClip = totalClip;
238 } else {
239 fMatrixStorage = totalMatrix;
240 fMatrixStorage.postTranslate(SkIntToScalar(-x),
241 SkIntToScalar(-y));
242 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000243
reed@android.com8a1c16f2008-12-17 15:59:43 +0000244 totalClip.translate(-x, -y, &fClip);
245 }
246
reed@google.com045e62d2011-10-24 12:19:46 +0000247 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248
249 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000250
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000252 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 SkRegion::kDifference_Op);
254 }
reed@google.com4b226022011-01-11 18:32:13 +0000255
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000256 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
257
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258#ifdef SK_DEBUG
259 if (!fClip.isEmpty()) {
260 SkIRect deviceR;
261 deviceR.set(0, 0, width, height);
262 SkASSERT(deviceR.contains(fClip.getBounds()));
263 }
264#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000265 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266};
267
268/* This is the record we keep for each save/restore level in the stack.
269 Since a level optionally copies the matrix and/or stack, we have pointers
270 for these fields. If the value is copied for this level, the copy is
271 stored in the ...Storage field, and the pointer points to that. If the
272 value is not copied for this level, we ignore ...Storage, and just point
273 at the corresponding value in the previous level in the stack.
274*/
275class SkCanvas::MCRec {
276public:
reed1f836ee2014-07-07 07:49:34 -0700277 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700278 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 /* If there are any layers in the stack, this points to the top-most
280 one that is at or below this level in the stack (so we know what
281 bitmap/device to draw into from this level. This value is NOT
282 reference counted, since the real owner is either our fLayer field,
283 or a previous one in a lower level.)
284 */
reed2ff1fce2014-12-11 07:07:37 -0800285 DeviceCM* fTopLayer;
286 SkRasterClip fRasterClip;
287 SkMatrix fMatrix;
288 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289
reedd9544982014-09-09 18:46:22 -0700290 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700291 fFilter = nullptr;
292 fLayer = nullptr;
293 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800294 fMatrix.reset();
295 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700296
reedd9544982014-09-09 18:46:22 -0700297 // don't bother initializing fNext
298 inc_rec();
299 }
reed2ff1fce2014-12-11 07:07:37 -0800300 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) {
reedd9544982014-09-09 18:46:22 -0700301 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700302 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700303 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800304 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700305
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 // don't bother initializing fNext
307 inc_rec();
308 }
309 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000310 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700311 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312 dec_rec();
313 }
mtkleinfeaadee2015-04-08 11:25:48 -0700314
315 void reset(const SkIRect& bounds) {
316 SkASSERT(fLayer);
317 SkASSERT(fDeferredSaveCount == 0);
318
319 fMatrix.reset();
320 fRasterClip.setRect(bounds);
321 fLayer->reset(bounds);
322 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323};
324
325class SkDrawIter : public SkDraw {
326public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000327 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000328 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000329 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330 canvas->updateDeviceCMCache();
331
reed687fa1c2015-04-07 08:00:56 -0700332 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000333 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000334 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000335 }
reed@google.com4b226022011-01-11 18:32:13 +0000336
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 bool next() {
338 // skip over recs with empty clips
339 if (fSkipEmptyClips) {
340 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
341 fCurrLayer = fCurrLayer->fNext;
342 }
343 }
344
reed@google.comf68c5e22012-02-24 16:38:58 +0000345 const DeviceCM* rec = fCurrLayer;
346 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347
348 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000349 fClip = &((SkRasterClip*)&rec->fClip)->forceGetBW();
350 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700352 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700353 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700354 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000356 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357
358 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700359 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000360
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 return true;
362 }
363 return false;
364 }
reed@google.com4b226022011-01-11 18:32:13 +0000365
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000366 SkBaseDevice* getDevice() const { return fDevice; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000367 int getX() const { return fDevice->getOrigin().x(); }
368 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 const SkMatrix& getMatrix() const { return *fMatrix; }
370 const SkRegion& getClip() const { return *fClip; }
371 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000372
reed@android.com8a1c16f2008-12-17 15:59:43 +0000373private:
374 SkCanvas* fCanvas;
375 const DeviceCM* fCurrLayer;
376 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 SkBool8 fSkipEmptyClips;
378
379 typedef SkDraw INHERITED;
380};
381
382/////////////////////////////////////////////////////////////////////////////
383
reeddbc3cef2015-04-29 12:18:57 -0700384static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
385 return lazy->isValid() ? lazy->get() : lazy->set(orig);
386}
387
388/**
389 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700390 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700391 */
392static SkColorFilter* image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700393 SkImageFilter* imgf = paint.getImageFilter();
394 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700395 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700396 }
397
398 SkColorFilter* imgCF;
399 if (!imgf->asAColorFilter(&imgCF)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700400 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700401 }
402
403 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700404 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700405 // there is no existing paint colorfilter, so we can just return the imagefilter's
406 return imgCF;
407 }
408
409 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
410 // and we need to combine them into a single colorfilter.
411 SkAutoTUnref<SkColorFilter> autoImgCF(imgCF);
412 return SkColorFilter::CreateComposeFilter(imgCF, paintCF);
reeddbc3cef2015-04-29 12:18:57 -0700413}
414
senorblanco87e066e2015-10-28 11:23:36 -0700415/**
416 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
417 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
418 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
419 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
420 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
421 * conservative "effective" bounds based on the settings in the paint... with one exception. This
422 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
423 * deliberately ignored.
424 */
425static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
426 const SkRect& rawBounds,
427 SkRect* storage) {
428 SkPaint tmpUnfiltered(paint);
429 tmpUnfiltered.setImageFilter(nullptr);
430 if (tmpUnfiltered.canComputeFastBounds()) {
431 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
432 } else {
433 return rawBounds;
434 }
435}
436
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437class AutoDrawLooper {
438public:
senorblanco87e066e2015-10-28 11:23:36 -0700439 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
440 // paint. It's used to determine the size of the offscreen layer for filters.
441 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700442 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000443 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700444 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000445 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000446 fFilter = canvas->getDrawFilter();
reed4a8126e2014-09-22 07:29:03 -0700447 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000448 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700449 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000450 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451
reeddbc3cef2015-04-29 12:18:57 -0700452 SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint);
453 if (simplifiedCF) {
454 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
455 paint->setColorFilter(simplifiedCF)->unref();
halcanary96fcdcc2015-08-27 07:41:13 -0700456 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700457 fPaint = paint;
458 }
459
460 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700461 /**
462 * We implement ImageFilters for a given draw by creating a layer, then applying the
463 * imagefilter to the pixels of that layer (its backing surface/image), and then
464 * we call restore() to xfer that layer to the main canvas.
465 *
466 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
467 * 2. Generate the src pixels:
468 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
469 * return (fPaint). We then draw the primitive (using srcover) into a cleared
470 * buffer/surface.
471 * 3. Restore the layer created in #1
472 * The imagefilter is passed the buffer/surface from the layer (now filled with the
473 * src pixels of the primitive). It returns a new "filtered" buffer, which we
474 * draw onto the previous layer using the xfermode from the original paint.
475 */
reed@google.com8926b162012-03-23 15:36:36 +0000476 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700477 tmp.setImageFilter(fPaint->getImageFilter());
478 tmp.setXfermode(fPaint->getXfermode());
senorblanco87e066e2015-10-28 11:23:36 -0700479 SkRect storage;
480 if (rawBounds) {
481 // Make rawBounds include all paint outsets except for those due to image filters.
482 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
483 }
senorblanco87e066e2015-10-28 11:23:36 -0700484 (void)canvas->internalSaveLayer(rawBounds, &tmp, SkCanvas::kARGB_ClipLayer_SaveFlag,
reed76033be2015-03-14 10:54:31 -0700485 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700486 fTempLayerForImageFilter = true;
487 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000488 }
489
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000490 if (SkDrawLooper* looper = paint.getLooper()) {
491 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
492 looper->contextSize());
493 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000494 fIsSimple = false;
495 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700496 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000497 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700498 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000499 }
piotaixrb5fae932014-09-24 13:03:30 -0700500
reed4a8126e2014-09-22 07:29:03 -0700501 uint32_t oldFlags = paint.getFlags();
502 fNewPaintFlags = filter_paint_flags(props, oldFlags);
503 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700504 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700505 paint->setFlags(fNewPaintFlags);
506 fPaint = paint;
507 // if we're not simple, doNext() will take care of calling setFlags()
508 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000509 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000510
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700512 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000513 fCanvas->internalRestore();
514 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000515 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000517
reed@google.com4e2b3d32011-04-07 14:18:59 +0000518 const SkPaint& paint() const {
519 SkASSERT(fPaint);
520 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000522
reed@google.com129ec222012-05-15 13:24:09 +0000523 bool next(SkDrawFilter::Type drawType) {
524 if (fDone) {
525 return false;
526 } else if (fIsSimple) {
527 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000528 return !fPaint->nothingToDraw();
529 } else {
530 return this->doNext(drawType);
531 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000532 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534private:
reeddbc3cef2015-04-29 12:18:57 -0700535 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
536 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000537 SkCanvas* fCanvas;
538 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000539 SkDrawFilter* fFilter;
540 const SkPaint* fPaint;
541 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700542 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700543 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000544 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000545 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000546 SkDrawLooper::Context* fLooperContext;
547 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000548
549 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550};
551
reed@google.com129ec222012-05-15 13:24:09 +0000552bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700553 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000554 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700555 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000556
reeddbc3cef2015-04-29 12:18:57 -0700557 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
558 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700559 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000560
reed5c476fb2015-04-20 08:04:21 -0700561 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700562 paint->setImageFilter(nullptr);
563 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000564 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000565
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000566 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000567 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000568 return false;
569 }
570 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000571 if (!fFilter->filter(paint, drawType)) {
572 fDone = true;
573 return false;
574 }
halcanary96fcdcc2015-08-27 07:41:13 -0700575 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000576 // no looper means we only draw once
577 fDone = true;
578 }
579 }
580 fPaint = paint;
581
582 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000583 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000584 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000585 }
586
587 // call this after any possible paint modifiers
588 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700589 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000590 return false;
591 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000592 return true;
593}
594
reed@android.com8a1c16f2008-12-17 15:59:43 +0000595////////// macros to place around the internal draw calls //////////////////
596
reed262a71b2015-12-05 13:07:27 -0800597#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
598 this->predrawNotify(); \
599 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
600 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
601 SkDrawIter iter(this);
602
603
reed@google.com8926b162012-03-23 15:36:36 +0000604#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000605 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700606 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000607 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000608 SkDrawIter iter(this);
609
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000610#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000611 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700612 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000613 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000614 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000615
reedc83a2972015-07-16 07:40:45 -0700616#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
617 this->predrawNotify(bounds, &paint, auxOpaque); \
618 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
619 while (looper.next(type)) { \
620 SkDrawIter iter(this);
621
reed@google.com4e2b3d32011-04-07 14:18:59 +0000622#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000623
624////////////////////////////////////////////////////////////////////////////
625
mtkleinfeaadee2015-04-08 11:25:48 -0700626void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
627 this->restoreToCount(1);
628 fCachedLocalClipBounds.setEmpty();
629 fCachedLocalClipBoundsDirty = true;
630 fClipStack->reset();
631 fMCRec->reset(bounds);
632
633 // We're peering through a lot of structs here. Only at this scope do we
634 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
635 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
636}
637
reedd9544982014-09-09 18:46:22 -0700638SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800639 if (device && device->forceConservativeRasterClip()) {
640 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
641 }
642 // Since init() is only called once by our constructors, it is safe to perform this
643 // const-cast.
644 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
645
reed@google.comc0784db2013-12-13 21:16:12 +0000646 fCachedLocalClipBounds.setEmpty();
647 fCachedLocalClipBoundsDirty = true;
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000648 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000649 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700650 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800651 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700652 fMetaData = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653
halcanary385fe4d2015-08-26 13:07:48 -0700654 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700655
reed@android.com8a1c16f2008-12-17 15:59:43 +0000656 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700657 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000658
reeda499f902015-05-01 09:34:31 -0700659 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
660 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
halcanary96fcdcc2015-08-27 07:41:13 -0700661 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip, false);
reedb679ca82015-04-07 04:40:48 -0700662
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664
halcanary96fcdcc2015-08-27 07:41:13 -0700665 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000666
reedf92c8662014-08-18 08:02:43 -0700667 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700668 // The root device and the canvas should always have the same pixel geometry
669 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700670 device->onAttachToCanvas(this);
671 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800672 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700673 }
674 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675}
676
reed@google.comcde92112011-07-06 20:00:52 +0000677SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000678 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700679 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800680 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000681{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000682 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000683
halcanary96fcdcc2015-08-27 07:41:13 -0700684 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000685}
686
reedd9544982014-09-09 18:46:22 -0700687static SkBitmap make_nopixels(int width, int height) {
688 SkBitmap bitmap;
689 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
690 return bitmap;
691}
692
693class SkNoPixelsBitmapDevice : public SkBitmapDevice {
694public:
robertphillipsfcf78292015-06-19 11:49:52 -0700695 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
696 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800697 {
698 this->setOrigin(bounds.x(), bounds.y());
699 }
reedd9544982014-09-09 18:46:22 -0700700
701private:
piotaixrb5fae932014-09-24 13:03:30 -0700702
reedd9544982014-09-09 18:46:22 -0700703 typedef SkBitmapDevice INHERITED;
704};
705
reed96a857e2015-01-25 10:33:58 -0800706SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000707 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800708 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800709 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000710{
711 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700712
halcanary385fe4d2015-08-26 13:07:48 -0700713 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
714 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700715}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000716
reed78e27682014-11-19 08:04:34 -0800717SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700718 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700719 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800720 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700721{
722 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700723
halcanary385fe4d2015-08-26 13:07:48 -0700724 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700725}
726
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000727SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000728 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700729 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800730 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000731{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000732 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700733
reedd9544982014-09-09 18:46:22 -0700734 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000735}
736
robertphillipsfcf78292015-06-19 11:49:52 -0700737SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
738 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700739 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800740 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700741{
742 inc_canvas();
743
744 this->init(device, flags);
745}
746
reed4a8126e2014-09-22 07:29:03 -0700747SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700748 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700749 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800750 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700751{
752 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700753
halcanary385fe4d2015-08-26 13:07:48 -0700754 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700755 this->init(device, kDefault_InitFlags);
756}
reed29c857d2014-09-21 10:25:07 -0700757
reed4a8126e2014-09-22 07:29:03 -0700758SkCanvas::SkCanvas(const SkBitmap& bitmap)
759 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
760 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800761 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700762{
763 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700764
halcanary385fe4d2015-08-26 13:07:48 -0700765 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700766 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000767}
768
769SkCanvas::~SkCanvas() {
770 // free up the contents of our deque
771 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000772
reed@android.com8a1c16f2008-12-17 15:59:43 +0000773 this->internalRestore(); // restore the last, since we're going away
774
halcanary385fe4d2015-08-26 13:07:48 -0700775 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000776
reed@android.com8a1c16f2008-12-17 15:59:43 +0000777 dec_canvas();
778}
779
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780SkDrawFilter* SkCanvas::getDrawFilter() const {
781 return fMCRec->fFilter;
782}
783
784SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700785 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
787 return filter;
788}
789
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000790SkMetaData& SkCanvas::getMetaData() {
791 // metadata users are rare, so we lazily allocate it. If that changes we
792 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700793 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000794 fMetaData = new SkMetaData;
795 }
796 return *fMetaData;
797}
798
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799///////////////////////////////////////////////////////////////////////////////
800
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000801void SkCanvas::flush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000802 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000803 if (device) {
804 device->flush();
805 }
806}
807
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000808SkISize SkCanvas::getTopLayerSize() const {
809 SkBaseDevice* d = this->getTopDevice();
810 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
811}
812
813SkIPoint SkCanvas::getTopLayerOrigin() const {
814 SkBaseDevice* d = this->getTopDevice();
815 return d ? d->getOrigin() : SkIPoint::Make(0, 0);
816}
817
818SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000819 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000820 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
821}
822
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000823SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000824 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000825 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000826 SkASSERT(rec && rec->fLayer);
827 return rec->fLayer->fDevice;
828}
829
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000830SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000831 if (updateMatrixClip) {
832 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
833 }
reed@google.com9266fed2011-03-30 00:18:03 +0000834 return fMCRec->fTopLayer->fDevice;
835}
836
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000837bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
838 if (kUnknown_SkColorType == bitmap->colorType() || bitmap->getTexture()) {
839 return false;
840 }
841
842 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700843 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700844 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000845 return false;
846 }
847 weAllocated = true;
848 }
849
reedcf01e312015-05-23 19:14:51 -0700850 SkAutoPixmapUnlock unlocker;
851 if (bitmap->requestLock(&unlocker)) {
852 const SkPixmap& pm = unlocker.pixmap();
853 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
854 return true;
855 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000856 }
857
858 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700859 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000860 }
861 return false;
862}
reed@google.com51df9e32010-12-23 19:29:18 +0000863
bsalomon@google.comc6980972011-11-02 19:57:21 +0000864bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000865 SkIRect r = srcRect;
866 const SkISize size = this->getBaseLayerSize();
867 if (!r.intersect(0, 0, size.width(), size.height())) {
868 bitmap->reset();
869 return false;
870 }
871
reed84825042014-09-02 12:50:45 -0700872 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000873 // bitmap will already be reset.
874 return false;
875 }
876 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
877 bitmap->reset();
878 return false;
879 }
880 return true;
881}
882
reed96472de2014-12-10 09:53:42 -0800883bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000884 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000885 if (!device) {
886 return false;
887 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000888 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800889
reed96472de2014-12-10 09:53:42 -0800890 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
891 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000892 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000893 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000894
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000895 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800896 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000897}
898
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000899bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
900 if (bitmap.getTexture()) {
901 return false;
902 }
reedcf01e312015-05-23 19:14:51 -0700903
904 SkAutoPixmapUnlock unlocker;
905 if (bitmap.requestLock(&unlocker)) {
906 const SkPixmap& pm = unlocker.pixmap();
907 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000908 }
909 return false;
910}
911
912bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
913 int x, int y) {
914 switch (origInfo.colorType()) {
915 case kUnknown_SkColorType:
916 case kIndex_8_SkColorType:
917 return false;
918 default:
919 break;
920 }
halcanary96fcdcc2015-08-27 07:41:13 -0700921 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000922 return false;
923 }
924
925 const SkISize size = this->getBaseLayerSize();
926 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
927 if (!target.intersect(0, 0, size.width(), size.height())) {
928 return false;
929 }
930
931 SkBaseDevice* device = this->getDevice();
932 if (!device) {
933 return false;
934 }
935
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000936 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700937 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000938
939 // if x or y are negative, then we have to adjust pixels
940 if (x > 0) {
941 x = 0;
942 }
943 if (y > 0) {
944 y = 0;
945 }
946 // here x,y are either 0 or negative
947 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
948
reed4af35f32014-06-27 17:47:49 -0700949 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700950 const bool completeOverwrite = info.dimensions() == size;
951 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700952
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000953 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000954 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000955}
reed@google.com51df9e32010-12-23 19:29:18 +0000956
junov@google.com4370aed2012-01-18 16:21:08 +0000957SkCanvas* SkCanvas::canvasForDrawIter() {
958 return this;
959}
960
reed@android.com8a1c16f2008-12-17 15:59:43 +0000961//////////////////////////////////////////////////////////////////////////////
962
reed@android.com8a1c16f2008-12-17 15:59:43 +0000963void SkCanvas::updateDeviceCMCache() {
964 if (fDeviceCMDirty) {
965 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700966 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000967 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000968
halcanary96fcdcc2015-08-27 07:41:13 -0700969 if (nullptr == layer->fNext) { // only one layer
970 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000972 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973 do {
reed687fa1c2015-04-07 08:00:56 -0700974 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700975 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976 }
977 fDeviceCMDirty = false;
978 }
979}
980
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981///////////////////////////////////////////////////////////////////////////////
982
reed2ff1fce2014-12-11 07:07:37 -0800983void SkCanvas::checkForDeferredSave() {
984 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800985 this->doSave();
986 }
987}
988
reedf0090cb2014-11-26 08:55:51 -0800989int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800990#ifdef SK_DEBUG
991 int count = 0;
992 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
993 for (;;) {
994 const MCRec* rec = (const MCRec*)iter.next();
995 if (!rec) {
996 break;
997 }
998 count += 1 + rec->fDeferredSaveCount;
999 }
1000 SkASSERT(count == fSaveCount);
1001#endif
1002 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001003}
1004
1005int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001006 fSaveCount += 1;
1007 fMCRec->fDeferredSaveCount += 1;
1008 return this->getSaveCount() - 1; // return our prev value
1009}
1010
1011void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001012 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001013
1014 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1015 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001016 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001017}
1018
1019void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001020 if (fMCRec->fDeferredSaveCount > 0) {
1021 SkASSERT(fSaveCount > 1);
1022 fSaveCount -= 1;
1023 fMCRec->fDeferredSaveCount -= 1;
1024 } else {
1025 // check for underflow
1026 if (fMCStack.count() > 1) {
1027 this->willRestore();
1028 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001029 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001030 this->internalRestore();
1031 this->didRestore();
1032 }
reedf0090cb2014-11-26 08:55:51 -08001033 }
1034}
1035
1036void SkCanvas::restoreToCount(int count) {
1037 // sanity check
1038 if (count < 1) {
1039 count = 1;
1040 }
mtkleinf0f14112014-12-12 08:46:25 -08001041
reedf0090cb2014-11-26 08:55:51 -08001042 int n = this->getSaveCount() - count;
1043 for (int i = 0; i < n; ++i) {
1044 this->restore();
1045 }
1046}
1047
reed2ff1fce2014-12-11 07:07:37 -08001048void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001049 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001050 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001051 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001052
reed687fa1c2015-04-07 08:00:56 -07001053 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001054}
1055
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001057#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
reed@google.comb93ba452014-03-10 19:47:58 +00001059#else
1060 return true;
1061#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062}
1063
junov@chromium.orga907ac32012-02-24 21:54:07 +00001064bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveFlags flags,
reed9b3aa542015-03-11 08:47:12 -07001065 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001066 SkIRect clipBounds;
1067 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001068 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001069 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001070
reed96e657d2015-03-10 17:30:07 -07001071 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1072
senorblanco87e066e2015-10-28 11:23:36 -07001073// This is a temporary hack, until individual filters can do their own
1074// bloating, when this will be removed.
1075#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1076 SkRect storage;
1077#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001078 if (imageFilter) {
reed96e657d2015-03-10 17:30:07 -07001079 imageFilter->filterBounds(clipBounds, ctm, &clipBounds);
senorblanco87e066e2015-10-28 11:23:36 -07001080#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
1081 if (bounds && imageFilter->canComputeFastBounds()) {
1082 imageFilter->computeFastBounds(*bounds, &storage);
1083 bounds = &storage;
1084 } else {
1085 bounds = nullptr;
1086 }
senorblancodb64af32015-12-09 10:11:43 -08001087#else
1088 if (bounds && !imageFilter->canComputeFastBounds()) {
1089 bounds = nullptr;
1090 }
senorblanco87e066e2015-10-28 11:23:36 -07001091#endif
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001092 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001093 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001094 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001096
reed96e657d2015-03-10 17:30:07 -07001097 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 r.roundOut(&ir);
1099 // early exit if the layer's bounds are clipped out
1100 if (!ir.intersect(clipBounds)) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001101 if (bounds_affects_clip(flags)) {
reed9b3aa542015-03-11 08:47:12 -07001102 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001103 fMCRec->fRasterClip.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001104 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001105 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106 }
1107 } else { // no user bounds, so just use the clip
1108 ir = clipBounds;
1109 }
reed180aec42015-03-11 10:39:04 -07001110 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001111
senorblanco@chromium.org89f077c2014-02-24 15:16:42 +00001112 if (bounds_affects_clip(flags)) {
reed180aec42015-03-11 10:39:04 -07001113 // Simplify the current clips since they will be applied properly during restore()
reed9b3aa542015-03-11 08:47:12 -07001114 fCachedLocalClipBoundsDirty = true;
reed687fa1c2015-04-07 08:00:56 -07001115 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001116 fMCRec->fRasterClip.setRect(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001117 }
1118
1119 if (intersection) {
1120 *intersection = ir;
1121 }
1122 return true;
1123}
1124
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001125int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
reedd990e2f2014-12-22 11:58:30 -08001126 if (gIgnoreSaveLayerBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -07001127 bounds = nullptr;
reedd990e2f2014-12-22 11:58:30 -08001128 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001129 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag);
reeda6441162015-03-26 13:40:09 -07001130 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001131 this->internalSaveLayer(bounds, paint, kARGB_ClipLayer_SaveFlag, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001132 return this->getSaveCount() - 1;
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001133}
1134
reed2ff1fce2014-12-11 07:07:37 -08001135int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) {
reedd990e2f2014-12-22 11:58:30 -08001136 if (gIgnoreSaveLayerBounds) {
halcanary96fcdcc2015-08-27 07:41:13 -07001137 bounds = nullptr;
reedd990e2f2014-12-22 11:58:30 -08001138 }
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001139 SaveLayerStrategy strategy = this->willSaveLayer(bounds, paint, flags);
reeda6441162015-03-26 13:40:09 -07001140 fSaveCount += 1;
reed76033be2015-03-14 10:54:31 -07001141 this->internalSaveLayer(bounds, paint, flags, strategy);
reed2ff1fce2014-12-11 07:07:37 -08001142 return this->getSaveCount() - 1;
reed@google.com8926b162012-03-23 15:36:36 +00001143}
1144
reed70ee31b2015-12-10 13:44:45 -08001145int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
1146 unsigned flags = kARGB_ClipLayer_SaveFlag | kPreserveLCDText_PrivateSaveFlag;
1147 return this->saveLayer(bounds, paint, (SaveFlags)flags);
1148}
1149
robertphillips7354a4b2015-12-16 05:08:27 -08001150static void draw_filter_into_device(SkBaseDevice* src, SkImageFilter* filter, SkBaseDevice* dst) {
1151
1152 SkBitmap srcBM;
1153
1154#if SK_SUPPORT_GPU
1155 GrRenderTarget* srcRT = src->accessRenderTarget();
1156 if (srcRT && !srcRT->asTexture() && dst->accessRenderTarget()) {
1157 // When both the src & the dst are on the gpu but the src doesn't have a texture,
1158 // we create a temporary texture for the draw.
1159 // TODO: we should actually only copy the portion of the source needed to apply the image
1160 // filter
1161 GrContext* context = srcRT->getContext();
1162 SkAutoTUnref<GrTexture> tex(context->textureProvider()->createTexture(srcRT->desc(), true));
1163
1164 context->copySurface(tex, srcRT);
1165
1166 GrWrapTextureInBitmap(tex, src->width(), src->height(), src->isOpaque(), &srcBM);
1167 } else
1168#endif
1169 {
1170 srcBM = src->accessBitmap(false);
1171 }
1172
1173 SkCanvas c(dst);
1174
1175 SkPaint p;
1176 p.setImageFilter(filter);
1177 c.drawBitmap(srcBM, 0, 0, &p);
1178}
reed70ee31b2015-12-10 13:44:45 -08001179
reed2ff1fce2014-12-11 07:07:37 -08001180void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags,
reed76033be2015-03-14 10:54:31 -07001181 SaveLayerStrategy strategy) {
reed@google.comb93ba452014-03-10 19:47:58 +00001182#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
commit-bot@chromium.org2a5cd602014-05-30 20:41:20 +00001183 flags |= kClipToLayer_SaveFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001184#endif
1185
junov@chromium.orga907ac32012-02-24 21:54:07 +00001186 // do this before we create the layer. We don't call the public save() since
1187 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001188 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001189
1190 fDeviceCMDirty = true;
1191
1192 SkIRect ir;
halcanary96fcdcc2015-08-27 07:41:13 -07001193 if (!this->clipRectBounds(bounds, flags, &ir, paint ? paint->getImageFilter() : nullptr)) {
reed2ff1fce2014-12-11 07:07:37 -08001194 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001195 }
1196
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001197 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1198 // the clipRectBounds() call above?
1199 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001200 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001201 }
1202
reed76033be2015-03-14 10:54:31 -07001203 bool isOpaque = !SkToBool(flags & kHasAlphaLayer_SaveFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001204 SkPixelGeometry geo = fProps.pixelGeometry();
1205 if (paint) {
reed76033be2015-03-14 10:54:31 -07001206 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001207 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001208 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001209 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001210 }
1211 }
commit-bot@chromium.org15a14052014-02-16 00:59:25 +00001212 SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(),
1213 isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001214
reedb2db8982014-11-13 12:41:02 -08001215 SkBaseDevice* device = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001216 if (nullptr == device) {
reedb2db8982014-11-13 12:41:02 -08001217 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001218 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001219 }
reedb2db8982014-11-13 12:41:02 -08001220
reed61f501f2015-04-29 08:34:00 -07001221 bool forceSpriteOnRestore = false;
1222 {
reed70ee31b2015-12-10 13:44:45 -08001223 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
1224 SkToBool(flags & kPreserveLCDText_PrivateSaveFlag);
reeddaa57bf2015-05-15 10:39:17 -07001225 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001226 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
1227 preserveLCDText, false);
reed61f501f2015-04-29 08:34:00 -07001228 SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001229 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001230 // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint)
robertphillips9a53fd72015-06-22 09:46:59 -07001231 const SkSurfaceProps surfaceProps(fProps.flags(), createInfo.fPixelGeometry);
1232 newDev = SkBitmapDevice::Create(createInfo.fInfo, surfaceProps);
halcanary96fcdcc2015-08-27 07:41:13 -07001233 if (nullptr == newDev) {
reed61f501f2015-04-29 08:34:00 -07001234 SkErrorInternals::SetError(kInternalError_SkError,
1235 "Unable to create device for layer.");
1236 return;
1237 }
1238 forceSpriteOnRestore = true;
1239 }
1240 device = newDev;
bungeman@google.come25c6842011-08-17 14:53:54 +00001241 }
bsalomon@google.come97f0852011-06-17 13:10:25 +00001242
reed@google.com6f8f2922011-03-04 22:27:10 +00001243 device->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001244
1245 if (0) {
1246 draw_filter_into_device(fMCRec->fTopLayer->fDevice, nullptr, device);
1247 }
1248
halcanary385fe4d2015-08-26 13:07:48 -07001249 DeviceCM* layer =
1250 new DeviceCM(device, paint, this, fConservativeRasterClip, forceSpriteOnRestore);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 device->unref();
1252
1253 layer->fNext = fMCRec->fTopLayer;
1254 fMCRec->fLayer = layer;
1255 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reed@android.com8a1c16f2008-12-17 15:59:43 +00001256}
1257
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001258int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
1259 return this->saveLayerAlpha(bounds, alpha, kARGB_ClipLayer_SaveFlag);
1260}
1261
reed@android.com8a1c16f2008-12-17 15:59:43 +00001262int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
1263 SaveFlags flags) {
1264 if (0xFF == alpha) {
halcanary96fcdcc2015-08-27 07:41:13 -07001265 return this->saveLayer(bounds, nullptr, flags);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266 } else {
1267 SkPaint tmpPaint;
1268 tmpPaint.setAlpha(alpha);
1269 return this->saveLayer(bounds, &tmpPaint, flags);
1270 }
1271}
1272
reed@android.com8a1c16f2008-12-17 15:59:43 +00001273void SkCanvas::internalRestore() {
1274 SkASSERT(fMCStack.count() != 0);
1275
1276 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001277 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001278
reed687fa1c2015-04-07 08:00:56 -07001279 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001280
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001281 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001282 DeviceCM* layer = fMCRec->fLayer; // may be null
1283 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001284 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001285
1286 // now do the normal restore()
1287 fMCRec->~MCRec(); // balanced in save()
1288 fMCStack.pop_back();
1289 fMCRec = (MCRec*)fMCStack.back();
1290
1291 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1292 since if we're being recorded, we don't want to record this (the
1293 recorder will have already recorded the restore).
1294 */
bsalomon49f085d2014-09-05 13:34:00 -07001295 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001297 const SkIPoint& origin = layer->fDevice->getOrigin();
reed@google.com8926b162012-03-23 15:36:36 +00001298 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
reed61f501f2015-04-29 08:34:00 -07001299 layer->fPaint, layer->fDeviceIsBitmapDevice);
reed@google.com8926b162012-03-23 15:36:36 +00001300 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001302 delete layer;
reedb679ca82015-04-07 04:40:48 -07001303 } else {
1304 // we're at the root
reeda499f902015-05-01 09:34:31 -07001305 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001306 layer->~DeviceCM();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001307 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001308 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309}
1310
reed4a8126e2014-09-22 07:29:03 -07001311SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001312 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001313 props = &fProps;
1314 }
1315 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001316}
1317
reed4a8126e2014-09-22 07:29:03 -07001318SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001319 SkBaseDevice* dev = this->getDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001320 return dev ? dev->newSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001321}
1322
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001323SkImageInfo SkCanvas::imageInfo() const {
1324 SkBaseDevice* dev = this->getDevice();
1325 if (dev) {
1326 return dev->imageInfo();
1327 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001328 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001329 }
1330}
1331
1332const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001333 SkPixmap pmap;
1334 if (!this->onPeekPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001335 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001336 }
1337 if (info) {
1338 *info = pmap.info();
1339 }
1340 if (rowBytes) {
1341 *rowBytes = pmap.rowBytes();
1342 }
1343 return pmap.addr();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001344}
1345
reed884e97c2015-05-26 11:31:54 -07001346bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001347 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001348 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001349}
1350
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001351void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001352 SkPixmap pmap;
1353 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001354 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001355 }
1356 if (info) {
1357 *info = pmap.info();
1358 }
1359 if (rowBytes) {
1360 *rowBytes = pmap.rowBytes();
1361 }
1362 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001363 *origin = this->getTopDevice(false)->getOrigin();
1364 }
reed884e97c2015-05-26 11:31:54 -07001365 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001366}
1367
reed884e97c2015-05-26 11:31:54 -07001368bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001369 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001370 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001371}
1372
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001373SkAutoROCanvasPixels::SkAutoROCanvasPixels(SkCanvas* canvas) {
1374 fAddr = canvas->peekPixels(&fInfo, &fRowBytes);
halcanary96fcdcc2015-08-27 07:41:13 -07001375 if (nullptr == fAddr) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001376 fInfo = canvas->imageInfo();
reed84825042014-09-02 12:50:45 -07001377 if (kUnknown_SkColorType == fInfo.colorType() || !fBitmap.tryAllocPixels(fInfo)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001378 return; // failure, fAddr is nullptr
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001379 }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001380 if (!canvas->readPixels(&fBitmap, 0, 0)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001381 return; // failure, fAddr is nullptr
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001382 }
1383 fAddr = fBitmap.getPixels();
1384 fRowBytes = fBitmap.rowBytes();
1385 }
1386 SkASSERT(fAddr); // success
1387}
1388
1389bool SkAutoROCanvasPixels::asROBitmap(SkBitmap* bitmap) const {
1390 if (fAddr) {
commit-bot@chromium.org00f8d6c2014-05-29 15:57:20 +00001391 return bitmap->installPixels(fInfo, const_cast<void*>(fAddr), fRowBytes);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001392 } else {
1393 bitmap->reset();
1394 return false;
1395 }
1396}
1397
reed@android.com8a1c16f2008-12-17 15:59:43 +00001398/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001399
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001400void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y,
reed61f501f2015-04-29 08:34:00 -07001401 const SkPaint* paint, bool deviceIsBitmapDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001402 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001403 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404 paint = &tmp;
1405 }
reed@google.com4b226022011-01-11 18:32:13 +00001406
reed@google.com8926b162012-03-23 15:36:36 +00001407 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001409 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001410 paint = &looper.paint();
1411 SkImageFilter* filter = paint->getImageFilter();
1412 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
reed@google.com8926b162012-03-23 15:36:36 +00001413 if (filter && !dstDev->canHandleImageFilter(filter)) {
reed88d064d2015-10-12 11:30:02 -07001414 SkImageFilter::DeviceProxy proxy(dstDev);
reed@google.com76dd2772012-01-05 21:15:07 +00001415 SkBitmap dst;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001416 SkIPoint offset = SkIPoint::Make(0, 0);
reed@google.comb55deeb2012-01-06 14:43:09 +00001417 const SkBitmap& src = srcDev->accessBitmap(false);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +00001418 SkMatrix matrix = *iter.fMatrix;
senorblanco@chromium.orgd5424a42014-04-02 19:20:05 +00001419 matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
senorblancodb64af32015-12-09 10:11:43 -08001420#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001421 SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height());
senorblancodb64af32015-12-09 10:11:43 -08001422#else
1423 SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y());
1424#endif
senorblancobe129b22014-08-08 07:14:35 -07001425 SkAutoTUnref<SkImageFilter::Cache> cache(dstDev->getImageFilterCache());
reedc9b5f8b2015-10-22 13:20:20 -07001426 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
1427 SkImageFilter::kApprox_SizeConstraint);
senorblanco@chromium.org4cb543d2014-03-14 15:44:01 +00001428 if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) {
tomhudson@google.com5efe0cb2012-04-10 19:14:48 +00001429 SkPaint tmpUnfiltered(*paint);
halcanary96fcdcc2015-08-27 07:41:13 -07001430 tmpUnfiltered.setImageFilter(nullptr);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +00001431 dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
1432 tmpUnfiltered);
reed@google.com76dd2772012-01-05 21:15:07 +00001433 }
reed61f501f2015-04-29 08:34:00 -07001434 } else if (deviceIsBitmapDevice) {
reed9572a102015-05-26 19:22:17 -07001435 const SkBitmap& src = static_cast<SkBitmapDevice*>(srcDev)->fBitmap;
reed61f501f2015-04-29 08:34:00 -07001436 dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001437 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001438 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001439 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001440 }
reed@google.com4e2b3d32011-04-07 14:18:59 +00001441 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442}
1443
reed32704672015-12-16 08:27:10 -08001444void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint) {
1445 if (gTreatSpriteAsBitmap) {
1446 this->save();
1447 this->resetMatrix();
1448 this->drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), paint);
1449 this->restore();
1450 return;
1451 }
reed4657ce22015-12-16 07:52:46 -08001452
reed32704672015-12-16 08:27:10 -08001453 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawSprite()");
1454 if (bitmap.drawsNothing()) {
1455 return;
1456 }
1457 SkDEBUGCODE(bitmap.validate();)
1458
1459 SkPaint tmp;
1460 if (nullptr == paint) {
1461 paint = &tmp;
1462 }
1463
1464 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
1465
1466 while (iter.next()) {
1467 const SkIPoint pos = { x - iter.getX(), y - iter.getY() };
1468 iter.fDevice->drawBitmapAsSprite(iter, bitmap, pos.x(), pos.y(), looper.paint());
1469 }
1470 LOOPER_END
1471}
1472
1473/////////////////////////////////////////////////////////////////////////////
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001474void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001475 SkMatrix m;
1476 m.setTranslate(dx, dy);
1477 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478}
1479
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001480void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001481 SkMatrix m;
1482 m.setScale(sx, sy);
1483 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001484}
1485
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001486void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001487 SkMatrix m;
1488 m.setRotate(degrees);
1489 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001490}
1491
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001492void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001493 SkMatrix m;
1494 m.setSkew(sx, sy);
1495 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001496}
1497
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001498void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001499 if (matrix.isIdentity()) {
1500 return;
1501 }
1502
reed2ff1fce2014-12-11 07:07:37 -08001503 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001505 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001506 fMCRec->fMatrix.preConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001507
1508 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001509}
1510
reed86a17e72015-05-14 12:25:22 -07001511void SkCanvas::setMatrix(const SkMatrix& matrix) {
1512 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001514 fCachedLocalClipBoundsDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001515 fMCRec->fMatrix = matrix;
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001516 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517}
1518
reed@android.com8a1c16f2008-12-17 15:59:43 +00001519void SkCanvas::resetMatrix() {
1520 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00001521
reed@android.com8a1c16f2008-12-17 15:59:43 +00001522 matrix.reset();
1523 this->setMatrix(matrix);
1524}
1525
1526//////////////////////////////////////////////////////////////////////////////
1527
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001528void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001529 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001530 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1531 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001532}
1533
1534void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001535#ifdef SK_ENABLE_CLIP_QUICKREJECT
1536 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001537 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001538 return false;
1539 }
1540
reed@google.com3b3e8952012-08-16 20:53:31 +00001541 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001542 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001543 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001544
reed687fa1c2015-04-07 08:00:56 -07001545 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001546 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001547 }
1548 }
1549#endif
1550
bsalomonac8cabd2015-11-20 18:53:07 -08001551 if (!fAllowSoftClip) {
1552 edgeStyle = kHard_ClipEdgeStyle;
1553 }
reed90ba0952015-11-20 13:42:47 -08001554
reedc64eff52015-11-21 12:39:45 -08001555 const bool rectStaysRect = fMCRec->fMatrix.rectStaysRect();
1556 SkRect devR;
1557 if (rectStaysRect) {
1558 fMCRec->fMatrix.mapRect(&devR, rect);
1559 }
bsalomonac8cabd2015-11-20 18:53:07 -08001560
reedc64eff52015-11-21 12:39:45 -08001561 // Check if we can quick-accept the clip call (and do nothing)
1562 //
1563 // TODO: investigate if a (conservative) version of this could be done in ::clipRect,
1564 // so that subclasses (like PictureRecording) didn't see unnecessary clips, which in turn
1565 // might allow lazy save/restores to eliminate entire save/restore blocks.
1566 //
1567 if (SkRegion::kIntersect_Op == op &&
1568 kHard_ClipEdgeStyle == edgeStyle
1569 && rectStaysRect)
1570 {
1571 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1572#if 0
1573 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1574 rect.left(), rect.top(), rect.right(), rect.bottom());
1575#endif
1576 return;
1577 }
1578 }
1579
1580 AutoValidateClip avc(this);
1581
1582 fDeviceCMDirty = true;
1583 fCachedLocalClipBoundsDirty = true;
1584
1585 if (rectStaysRect) {
1586 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1587 fClipStack->clipDevRect(devR, op, isAA);
1588 fMCRec->fRasterClip.op(devR, this->getBaseLayerSize(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001590 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001591 // and clip against that, since it can handle any matrix. However, to
1592 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1593 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001594 SkPath path;
1595
1596 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001597 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001598 }
1599}
1600
reed73e714e2014-09-04 09:02:23 -07001601static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath,
1602 SkRegion::Op op, bool doAA) {
reedd64c9482014-09-05 17:37:38 -07001603 rc->op(devPath, canvas->getBaseLayerSize(), op, doAA);
reed@google.com819c9212011-02-23 18:56:55 +00001604}
1605
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001606void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001607 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001608 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001609 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001610 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1611 } else {
1612 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001613 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001615
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001616void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001617 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001618 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001619 AutoValidateClip avc(this);
1620
1621 fDeviceCMDirty = true;
1622 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001623 if (!fAllowSoftClip) {
1624 edgeStyle = kHard_ClipEdgeStyle;
1625 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001626
reed687fa1c2015-04-07 08:00:56 -07001627 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001628
robertphillips125f19a2015-11-23 09:00:05 -08001629 fMCRec->fRasterClip.op(transformedRRect, this->getBaseLayerSize(), op,
1630 kSoft_ClipEdgeStyle == edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001631 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001632 }
1633
1634 SkPath path;
1635 path.addRRect(rrect);
1636 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001637 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001638}
1639
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001640void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001641 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001642 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001643
1644 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1645 SkRect r;
1646 if (path.isRect(&r)) {
1647 this->onClipRect(r, op, edgeStyle);
1648 return;
1649 }
1650 SkRRect rrect;
1651 if (path.isOval(&r)) {
1652 rrect.setOval(r);
1653 this->onClipRRect(rrect, op, edgeStyle);
1654 return;
1655 }
1656 if (path.isRRect(&rrect)) {
1657 this->onClipRRect(rrect, op, edgeStyle);
1658 return;
1659 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001660 }
robertphillips39f05382015-11-24 09:30:12 -08001661
1662 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001663}
1664
1665void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001666#ifdef SK_ENABLE_CLIP_QUICKREJECT
1667 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001668 if (fMCRec->fRasterClip.isEmpty()) {
reed@google.comda17f752012-08-16 18:27:05 +00001669 return false;
1670 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001671
reed@google.com3b3e8952012-08-16 20:53:31 +00001672 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001673 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001674 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001675
reed687fa1c2015-04-07 08:00:56 -07001676 fClipStack->clipEmpty();
reed1f836ee2014-07-07 07:49:34 -07001677 return fMCRec->fRasterClip.setEmpty();
reed@google.comda17f752012-08-16 18:27:05 +00001678 }
1679 }
1680#endif
1681
reed@google.com5c3d1472011-02-22 19:12:23 +00001682 AutoValidateClip avc(this);
1683
reed@android.com8a1c16f2008-12-17 15:59:43 +00001684 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001685 fCachedLocalClipBoundsDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001686 if (!fAllowSoftClip) {
1687 edgeStyle = kHard_ClipEdgeStyle;
1688 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001689
1690 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001691 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001692
reed@google.comfe701122011-11-08 19:41:23 +00001693 // Check if the transfomation, or the original path itself
1694 // made us empty. Note this can also happen if we contained NaN
1695 // values. computing the bounds detects this, and will set our
1696 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1697 if (devPath.getBounds().isEmpty()) {
1698 // resetting the path will remove any NaN or other wanky values
1699 // that might upset our scan converter.
1700 devPath.reset();
1701 }
1702
reed@google.com5c3d1472011-02-22 19:12:23 +00001703 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001704 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001705
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001706 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001707 bool clipIsAA = getClipStack()->asPath(&devPath);
1708 if (clipIsAA) {
1709 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001710 }
fmalita1a481fe2015-02-04 07:39:34 -08001711
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001712 op = SkRegion::kReplace_Op;
1713 }
1714
reed73e714e2014-09-04 09:02:23 -07001715 rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716}
1717
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001718void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001719 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001720 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001721}
1722
1723void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001724 AutoValidateClip avc(this);
1725
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001727 fCachedLocalClipBoundsDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001728
reed@google.com5c3d1472011-02-22 19:12:23 +00001729 // todo: signal fClipStack that we have a region, and therefore (I guess)
1730 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001731 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001732
reed1f836ee2014-07-07 07:49:34 -07001733 fMCRec->fRasterClip.op(rgn, op);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734}
1735
reed@google.com819c9212011-02-23 18:56:55 +00001736#ifdef SK_DEBUG
1737void SkCanvas::validateClip() const {
1738 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001739 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001740 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001741 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001742 return;
1743 }
1744
reed@google.com819c9212011-02-23 18:56:55 +00001745 SkIRect ir;
1746 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001747 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001748
reed687fa1c2015-04-07 08:00:56 -07001749 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001750 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001751 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001752 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001753 case SkClipStack::Element::kRect_Type:
1754 element->getRect().round(&ir);
1755 tmpClip.op(ir, element->getOp());
1756 break;
1757 case SkClipStack::Element::kEmpty_Type:
1758 tmpClip.setEmpty();
1759 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001760 default: {
1761 SkPath path;
1762 element->asPath(&path);
reed73e714e2014-09-04 09:02:23 -07001763 rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001764 break;
1765 }
reed@google.com819c9212011-02-23 18:56:55 +00001766 }
1767 }
reed@google.com819c9212011-02-23 18:56:55 +00001768}
1769#endif
1770
reed@google.com90c07ea2012-04-13 13:50:27 +00001771void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001772 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001773 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001774
halcanary96fcdcc2015-08-27 07:41:13 -07001775 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001776 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001777 }
1778}
1779
reed@google.com5c3d1472011-02-22 19:12:23 +00001780///////////////////////////////////////////////////////////////////////////////
1781
reed@google.com754de5f2014-02-24 19:38:20 +00001782bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001783 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001784}
1785
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001786bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001787 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001788}
1789
reed@google.com3b3e8952012-08-16 20:53:31 +00001790bool SkCanvas::quickReject(const SkRect& rect) const {
reed@google.com16078632011-12-06 18:56:37 +00001791 if (!rect.isFinite())
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001792 return true;
1793
reed1f836ee2014-07-07 07:49:34 -07001794 if (fMCRec->fRasterClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795 return true;
1796 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797
reed1f836ee2014-07-07 07:49:34 -07001798 if (fMCRec->fMatrix.hasPerspective()) {
reed@android.coma380ae42009-07-21 01:17:02 +00001799 SkRect dst;
reed1f836ee2014-07-07 07:49:34 -07001800 fMCRec->fMatrix.mapRect(&dst, rect);
reedb07a94f2014-11-19 05:03:18 -08001801 return !SkIRect::Intersects(dst.roundOut(), fMCRec->fRasterClip.getBounds());
reed@android.coma380ae42009-07-21 01:17:02 +00001802 } else {
reed@google.comc0784db2013-12-13 21:16:12 +00001803 const SkRect& clipR = this->getLocalClipBounds();
reed@android.comd252db02009-04-01 18:31:44 +00001804
reed@android.coma380ae42009-07-21 01:17:02 +00001805 // for speed, do the most likely reject compares first
reed@google.comc0784db2013-12-13 21:16:12 +00001806 // TODO: should we use | instead, or compare all 4 at once?
1807 if (rect.fTop >= clipR.fBottom || rect.fBottom <= clipR.fTop) {
reed@android.coma380ae42009-07-21 01:17:02 +00001808 return true;
1809 }
reed@google.comc0784db2013-12-13 21:16:12 +00001810 if (rect.fLeft >= clipR.fRight || rect.fRight <= clipR.fLeft) {
reed@android.coma380ae42009-07-21 01:17:02 +00001811 return true;
1812 }
1813 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001814 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815}
1816
reed@google.com3b3e8952012-08-16 20:53:31 +00001817bool SkCanvas::quickReject(const SkPath& path) const {
1818 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001819}
1820
reed@google.com3b3e8952012-08-16 20:53:31 +00001821bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001822 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001823 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001824 return false;
1825 }
1826
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001827 SkMatrix inverse;
1828 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001829 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001830 if (bounds) {
1831 bounds->setEmpty();
1832 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001833 return false;
1834 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001835
bsalomon49f085d2014-09-05 13:34:00 -07001836 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001837 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001838 // adjust it outwards in case we are antialiasing
1839 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001840
reed@google.com8f4d2302013-12-17 16:44:46 +00001841 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1842 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001843 inverse.mapRect(bounds, r);
1844 }
1845 return true;
1846}
1847
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001848bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001849 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001850 if (clip.isEmpty()) {
1851 if (bounds) {
1852 bounds->setEmpty();
1853 }
1854 return false;
1855 }
1856
bsalomon49f085d2014-09-05 13:34:00 -07001857 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001858 *bounds = clip.getBounds();
1859 }
1860 return true;
1861}
1862
reed@android.com8a1c16f2008-12-17 15:59:43 +00001863const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001864 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001865}
1866
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001867const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001868 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001869}
1870
reed@google.com9c135db2014-03-12 18:28:35 +00001871GrRenderTarget* SkCanvas::internal_private_accessTopLayerRenderTarget() {
1872 SkBaseDevice* dev = this->getTopDevice();
halcanary96fcdcc2015-08-27 07:41:13 -07001873 return dev ? dev->accessRenderTarget() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001874}
1875
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001876GrContext* SkCanvas::getGrContext() {
1877#if SK_SUPPORT_GPU
1878 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07001879 if (device) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001880 GrRenderTarget* renderTarget = device->accessRenderTarget();
bsalomon49f085d2014-09-05 13:34:00 -07001881 if (renderTarget) {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001882 return renderTarget->getContext();
1883 }
1884 }
1885#endif
1886
halcanary96fcdcc2015-08-27 07:41:13 -07001887 return nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001888
1889}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001890
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001891void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1892 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001893 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001894 if (outer.isEmpty()) {
1895 return;
1896 }
1897 if (inner.isEmpty()) {
1898 this->drawRRect(outer, paint);
1899 return;
1900 }
1901
1902 // We don't have this method (yet), but technically this is what we should
1903 // be able to assert...
1904 // SkASSERT(outer.contains(inner));
1905 //
1906 // For now at least check for containment of bounds
1907 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1908
1909 this->onDrawDRRect(outer, inner, paint);
1910}
1911
reed41af9662015-01-05 07:49:08 -08001912// These need to stop being virtual -- clients need to override the onDraw... versions
1913
1914void SkCanvas::drawPaint(const SkPaint& paint) {
1915 this->onDrawPaint(paint);
1916}
1917
1918void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1919 this->onDrawRect(r, paint);
1920}
1921
1922void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1923 this->onDrawOval(r, paint);
1924}
1925
1926void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1927 this->onDrawRRect(rrect, paint);
1928}
1929
1930void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1931 this->onDrawPoints(mode, count, pts, paint);
1932}
1933
1934void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1935 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1936 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1937 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1938 indices, indexCount, paint);
1939}
1940
1941void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1942 this->onDrawPath(path, paint);
1943}
1944
reeda85d4d02015-05-06 12:56:48 -07001945void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
1946 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001947}
1948
reede47829b2015-08-06 10:02:53 -07001949void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1950 const SkPaint* paint, SrcRectConstraint constraint) {
1951 if (dst.isEmpty() || src.isEmpty()) {
1952 return;
1953 }
1954 this->onDrawImageRect(image, &src, dst, paint, constraint);
1955}
reed41af9662015-01-05 07:49:08 -08001956
reed84984ef2015-07-17 07:09:43 -07001957void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1958 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001959 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001960}
1961
reede47829b2015-08-06 10:02:53 -07001962void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1963 SrcRectConstraint constraint) {
1964 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1965 constraint);
1966}
reede47829b2015-08-06 10:02:53 -07001967
reed4c21dc52015-06-25 12:32:03 -07001968void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1969 const SkPaint* paint) {
1970 if (dst.isEmpty()) {
1971 return;
1972 }
1973 if (!SkNinePatchIter::Valid(image->width(), image->height(), center)) {
reede47829b2015-08-06 10:02:53 -07001974 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001975 }
1976 this->onDrawImageNine(image, center, dst, paint);
1977}
1978
reed41af9662015-01-05 07:49:08 -08001979void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001980 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001981 return;
1982 }
reed41af9662015-01-05 07:49:08 -08001983 this->onDrawBitmap(bitmap, dx, dy, paint);
1984}
1985
reede47829b2015-08-06 10:02:53 -07001986void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001987 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001988 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001989 return;
1990 }
reede47829b2015-08-06 10:02:53 -07001991 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001992}
1993
reed84984ef2015-07-17 07:09:43 -07001994void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1995 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001996 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001997}
1998
reede47829b2015-08-06 10:02:53 -07001999void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2000 SrcRectConstraint constraint) {
2001 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2002 constraint);
2003}
reede47829b2015-08-06 10:02:53 -07002004
reed41af9662015-01-05 07:49:08 -08002005void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2006 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002007 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002008 return;
2009 }
reed4c21dc52015-06-25 12:32:03 -07002010 if (!SkNinePatchIter::Valid(bitmap.width(), bitmap.height(), center)) {
reeda5517e22015-07-14 10:54:12 -07002011 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002012 }
reed41af9662015-01-05 07:49:08 -08002013 this->onDrawBitmapNine(bitmap, center, dst, paint);
2014}
2015
reed32704672015-12-16 08:27:10 -08002016void SkCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
2017 if (bitmap.drawsNothing()) {
2018 return;
2019 }
2020 this->onDrawSprite(bitmap, left, top, paint);
2021}
2022
reed71c3c762015-06-24 10:29:17 -07002023void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2024 const SkColor colors[], int count, SkXfermode::Mode mode,
2025 const SkRect* cull, const SkPaint* paint) {
2026 if (count <= 0) {
2027 return;
2028 }
2029 SkASSERT(atlas);
2030 SkASSERT(xform);
2031 SkASSERT(tex);
2032 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2033}
2034
reede47829b2015-08-06 10:02:53 -07002035void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2036 const SkPaint* paint, SrcRectConstraint constraint) {
2037 if (src) {
2038 this->drawImageRect(image, *src, dst, paint, constraint);
2039 } else {
2040 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2041 dst, paint, constraint);
2042 }
2043}
2044void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2045 const SkPaint* paint, SrcRectConstraint constraint) {
2046 if (src) {
2047 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2048 } else {
2049 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2050 dst, paint, constraint);
2051 }
2052}
2053
reed@android.com8a1c16f2008-12-17 15:59:43 +00002054//////////////////////////////////////////////////////////////////////////////
2055// These are the virtual drawing methods
2056//////////////////////////////////////////////////////////////////////////////
2057
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002058void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002059 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002060 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2061 }
2062}
2063
reed41af9662015-01-05 07:49:08 -08002064void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002065 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002066 this->internalDrawPaint(paint);
2067}
2068
2069void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002070 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002071
2072 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002073 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002074 }
2075
reed@google.com4e2b3d32011-04-07 14:18:59 +00002076 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002077}
2078
reed41af9662015-01-05 07:49:08 -08002079void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2080 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002081 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002082 if ((long)count <= 0) {
2083 return;
2084 }
2085
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002086 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002087 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002088 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002089 // special-case 2 points (common for drawing a single line)
2090 if (2 == count) {
2091 r.set(pts[0], pts[1]);
2092 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002093 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002094 }
senorblanco87e066e2015-10-28 11:23:36 -07002095 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2096 return;
2097 }
2098 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002099 }
reed@google.coma584aed2012-05-16 14:06:02 +00002100
halcanary96fcdcc2015-08-27 07:41:13 -07002101 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002103 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002104
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002106 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002107 }
reed@google.com4b226022011-01-11 18:32:13 +00002108
reed@google.com4e2b3d32011-04-07 14:18:59 +00002109 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002110}
2111
reed41af9662015-01-05 07:49:08 -08002112void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002113 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002114 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002115 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002117 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2118 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2119 SkRect tmp(r);
2120 tmp.sort();
2121
senorblanco87e066e2015-10-28 11:23:36 -07002122 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2123 return;
2124 }
2125 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002126 }
reed@google.com4b226022011-01-11 18:32:13 +00002127
reedc83a2972015-07-16 07:40:45 -07002128 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002129
2130 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002131 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002132 }
2133
reed@google.com4e2b3d32011-04-07 14:18:59 +00002134 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002135}
2136
reed41af9662015-01-05 07:49:08 -08002137void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002138 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002139 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002140 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002141 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002142 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2143 return;
2144 }
2145 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002146 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002147
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002148 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002149
2150 while (iter.next()) {
2151 iter.fDevice->drawOval(iter, oval, looper.paint());
2152 }
2153
2154 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002155}
2156
reed41af9662015-01-05 07:49:08 -08002157void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002158 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002159 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002160 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002161 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002162 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2163 return;
2164 }
2165 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002166 }
2167
2168 if (rrect.isRect()) {
2169 // call the non-virtual version
2170 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002171 return;
2172 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002173 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002174 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2175 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002176 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002177
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002178 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002179
2180 while (iter.next()) {
2181 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2182 }
2183
2184 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002185}
2186
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002187void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2188 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002189 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002190 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002191 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002192 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2193 return;
2194 }
2195 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002196 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002197
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002198 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002199
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002200 while (iter.next()) {
2201 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2202 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002203
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002204 LOOPER_END
2205}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002206
reed41af9662015-01-05 07:49:08 -08002207void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002208 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002209 if (!path.isFinite()) {
2210 return;
2211 }
2212
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002213 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002214 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002215 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002216 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002217 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2218 return;
2219 }
2220 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002222
2223 const SkRect& r = path.getBounds();
2224 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002225 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002226 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002227 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002228 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002229 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002231 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002232
2233 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002234 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235 }
2236
reed@google.com4e2b3d32011-04-07 14:18:59 +00002237 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002238}
2239
reed262a71b2015-12-05 13:07:27 -08002240bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
2241#ifdef SK_SUPPORT_LEGACY_LAYER_BITMAP_IMAGEFILTERS
2242 return false;
2243#endif
2244
2245 if (!paint.getImageFilter()) {
2246 return false;
2247 }
2248
2249 const SkMatrix& ctm = this->getTotalMatrix();
2250 const unsigned kSubpixelBits = 0; // matching SkDraw::drawBitmap()
2251 if (!SkTreatAsSprite(ctm, w, h, kSubpixelBits)) {
2252 return false;
2253 }
2254
2255 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2256 // Once we can filter and the filter will return a result larger than itself, we should be
2257 // able to remove this constraint.
2258 // skbug.com/4526
2259 //
2260 SkPoint pt;
2261 ctm.mapXY(x, y, &pt);
2262 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2263 return ir.contains(fMCRec->fRasterClip.getBounds());
2264}
2265
reeda85d4d02015-05-06 12:56:48 -07002266void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002267 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002268 SkRect bounds = SkRect::MakeXYWH(x, y,
2269 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002270 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002271 SkRect tmp = bounds;
2272 if (paint) {
2273 paint->computeFastBounds(tmp, &tmp);
2274 }
2275 if (this->quickReject(tmp)) {
2276 return;
2277 }
reeda85d4d02015-05-06 12:56:48 -07002278 }
2279
2280 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002281 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002282 paint = lazy.init();
2283 }
reed262a71b2015-12-05 13:07:27 -08002284
2285 const bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2286 *paint);
2287 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2288
reeda85d4d02015-05-06 12:56:48 -07002289 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002290 const SkPaint& pnt = looper.paint();
2291 if (drawAsSprite && pnt.getImageFilter()) {
2292 SkBitmap bitmap;
2293 if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
2294 SkPoint pt;
2295 iter.fMatrix->mapXY(x, y, &pt);
2296 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2297 SkScalarRoundToInt(pt.fX),
2298 SkScalarRoundToInt(pt.fY), pnt);
2299 }
2300 } else {
2301 iter.fDevice->drawImage(iter, image, x, y, pnt);
2302 }
reeda85d4d02015-05-06 12:56:48 -07002303 }
2304
2305 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002306}
2307
reed41af9662015-01-05 07:49:08 -08002308void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002309 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002310 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002311 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002312 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002313 if (paint) {
2314 paint->computeFastBounds(dst, &storage);
2315 }
2316 if (this->quickReject(storage)) {
2317 return;
2318 }
reeda85d4d02015-05-06 12:56:48 -07002319 }
2320 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002321 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002322 paint = lazy.init();
2323 }
2324
senorblancoc41e7e12015-12-07 12:51:30 -08002325 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002326 image->isOpaque())
reeda85d4d02015-05-06 12:56:48 -07002327
2328 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002329 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002330 }
2331
2332 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002333}
2334
reed41af9662015-01-05 07:49:08 -08002335void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002336 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002337 SkDEBUGCODE(bitmap.validate();)
2338
reed33366972015-10-08 09:22:02 -07002339 if (bitmap.drawsNothing()) {
2340 return;
2341 }
2342
2343 SkLazyPaint lazy;
2344 if (nullptr == paint) {
2345 paint = lazy.init();
2346 }
2347
2348 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2349
2350 SkRect storage;
2351 const SkRect* bounds = nullptr;
2352 if (paint->canComputeFastBounds()) {
2353 bitmap.getBounds(&storage);
2354 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002355 SkRect tmp = storage;
2356 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2357 return;
2358 }
2359 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002360 }
reed@google.com4b226022011-01-11 18:32:13 +00002361
reed262a71b2015-12-05 13:07:27 -08002362 const bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
2363 bitmap.height(), *paint);
2364 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002365
2366 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002367 const SkPaint& pnt = looper.paint();
2368 if (drawAsSprite && pnt.getImageFilter()) {
2369 SkPoint pt;
2370 iter.fMatrix->mapXY(x, y, &pt);
2371 iter.fDevice->drawBitmapAsSprite(iter, bitmap,
2372 SkScalarRoundToInt(pt.fX),
2373 SkScalarRoundToInt(pt.fY), pnt);
2374 } else {
2375 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2376 }
reed33366972015-10-08 09:22:02 -07002377 }
reed262a71b2015-12-05 13:07:27 -08002378
reed33366972015-10-08 09:22:02 -07002379 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002380}
2381
reed@google.com9987ec32011-09-07 11:57:52 +00002382// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002383void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002384 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002385 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002386 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002387 return;
2388 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002389
halcanary96fcdcc2015-08-27 07:41:13 -07002390 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002391 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002392 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2393 return;
2394 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395 }
reed@google.com3d608122011-11-21 15:16:16 +00002396
reed@google.com33535f32012-09-25 15:37:50 +00002397 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002398 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002399 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002400 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002401
senorblancoc41e7e12015-12-07 12:51:30 -08002402 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002403 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002404
reed@google.com33535f32012-09-25 15:37:50 +00002405 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002406 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002407 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002408
reed@google.com33535f32012-09-25 15:37:50 +00002409 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002410}
2411
reed41af9662015-01-05 07:49:08 -08002412void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002413 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002414 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002415 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002416 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002417}
2418
reed4c21dc52015-06-25 12:32:03 -07002419void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2420 const SkPaint* paint) {
2421 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
2422
halcanary96fcdcc2015-08-27 07:41:13 -07002423 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002424 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002425 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2426 return;
2427 }
reed@google.com3d608122011-11-21 15:16:16 +00002428 }
reed4c21dc52015-06-25 12:32:03 -07002429
2430 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002431 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002432 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002433 }
reed4c21dc52015-06-25 12:32:03 -07002434
senorblancoc41e7e12015-12-07 12:51:30 -08002435 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002436
2437 while (iter.next()) {
2438 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002439 }
reed4c21dc52015-06-25 12:32:03 -07002440
2441 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002442}
2443
reed41af9662015-01-05 07:49:08 -08002444void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2445 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002446 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002447 SkDEBUGCODE(bitmap.validate();)
2448
halcanary96fcdcc2015-08-27 07:41:13 -07002449 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002450 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002451 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2452 return;
2453 }
reed4c21dc52015-06-25 12:32:03 -07002454 }
2455
2456 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002457 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002458 paint = lazy.init();
2459 }
2460
senorblancoc41e7e12015-12-07 12:51:30 -08002461 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
reed4c21dc52015-06-25 12:32:03 -07002462
2463 while (iter.next()) {
2464 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2465 }
2466
2467 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002468}
2469
reed@google.comf67e4cf2011-03-15 20:56:58 +00002470class SkDeviceFilteredPaint {
2471public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002472 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002473 uint32_t filteredFlags = device->filterTextFlags(paint);
2474 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002475 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002476 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002477 fPaint = newPaint;
2478 } else {
2479 fPaint = &paint;
2480 }
2481 }
2482
reed@google.comf67e4cf2011-03-15 20:56:58 +00002483 const SkPaint& paint() const { return *fPaint; }
2484
2485private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002486 const SkPaint* fPaint;
2487 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002488};
2489
bungeman@google.com52c748b2011-08-22 21:30:43 +00002490void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2491 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002492 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002493 draw.fDevice->drawRect(draw, r, paint);
2494 } else {
2495 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002496 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002497 draw.fDevice->drawRect(draw, r, p);
2498 }
2499}
2500
2501void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2502 const char text[], size_t byteLength,
2503 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002504 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002505
2506 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002507 if (text == nullptr || byteLength == 0 ||
bungeman@google.com52c748b2011-08-22 21:30:43 +00002508 draw.fClip->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002509 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002510 return;
2511 }
2512
2513 SkScalar width = 0;
2514 SkPoint start;
2515
2516 start.set(0, 0); // to avoid warning
2517 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2518 SkPaint::kStrikeThruText_Flag)) {
2519 width = paint.measureText(text, byteLength);
2520
2521 SkScalar offsetX = 0;
2522 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2523 offsetX = SkScalarHalf(width);
2524 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2525 offsetX = width;
2526 }
2527 start.set(x - offsetX, y);
2528 }
2529
2530 if (0 == width) {
2531 return;
2532 }
2533
2534 uint32_t flags = paint.getFlags();
2535
2536 if (flags & (SkPaint::kUnderlineText_Flag |
2537 SkPaint::kStrikeThruText_Flag)) {
2538 SkScalar textSize = paint.getTextSize();
2539 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2540 SkRect r;
2541
2542 r.fLeft = start.fX;
2543 r.fRight = start.fX + width;
2544
2545 if (flags & SkPaint::kUnderlineText_Flag) {
2546 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2547 start.fY);
2548 r.fTop = offset;
2549 r.fBottom = offset + height;
2550 DrawRect(draw, paint, r, textSize);
2551 }
2552 if (flags & SkPaint::kStrikeThruText_Flag) {
2553 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2554 start.fY);
2555 r.fTop = offset;
2556 r.fBottom = offset + height;
2557 DrawRect(draw, paint, r, textSize);
2558 }
2559 }
2560}
2561
reed@google.come0d9ce82014-04-23 04:00:17 +00002562void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2563 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002564 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002565
2566 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002567 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002568 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002569 DrawTextDecorations(iter, dfp.paint(),
2570 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002571 }
2572
reed@google.com4e2b3d32011-04-07 14:18:59 +00002573 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002574}
2575
reed@google.come0d9ce82014-04-23 04:00:17 +00002576void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2577 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002578 SkPoint textOffset = SkPoint::Make(0, 0);
2579
halcanary96fcdcc2015-08-27 07:41:13 -07002580 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002581
reed@android.com8a1c16f2008-12-17 15:59:43 +00002582 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002583 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002584 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002585 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002586 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002587
reed@google.com4e2b3d32011-04-07 14:18:59 +00002588 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002589}
2590
reed@google.come0d9ce82014-04-23 04:00:17 +00002591void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2592 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002593
2594 SkPoint textOffset = SkPoint::Make(0, constY);
2595
halcanary96fcdcc2015-08-27 07:41:13 -07002596 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002597
reed@android.com8a1c16f2008-12-17 15:59:43 +00002598 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002599 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002600 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002601 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002602 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002603
reed@google.com4e2b3d32011-04-07 14:18:59 +00002604 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002605}
2606
reed@google.come0d9ce82014-04-23 04:00:17 +00002607void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2608 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002609 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002610
reed@android.com8a1c16f2008-12-17 15:59:43 +00002611 while (iter.next()) {
2612 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002613 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002614 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002615
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002616 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002617}
2618
fmalita00d5c2c2014-08-21 08:53:26 -07002619void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2620 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002621
fmalita85d5eb92015-03-04 11:20:12 -08002622 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002623 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002624 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002625 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002626 SkRect tmp;
2627 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2628 return;
2629 }
2630 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002631 }
2632
fmalita024f9962015-03-03 19:08:17 -08002633 // We cannot filter in the looper as we normally do, because the paint is
2634 // incomplete at this point (text-related attributes are embedded within blob run paints).
2635 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002636 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002637
fmalita85d5eb92015-03-04 11:20:12 -08002638 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002639
fmalitaaa1b9122014-08-28 14:32:24 -07002640 while (iter.next()) {
2641 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002642 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002643 }
2644
fmalitaaa1b9122014-08-28 14:32:24 -07002645 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002646
2647 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002648}
2649
reed@google.come0d9ce82014-04-23 04:00:17 +00002650// These will become non-virtual, so they always call the (virtual) onDraw... method
2651void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2652 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002653 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002654 this->onDrawText(text, byteLength, x, y, paint);
2655}
2656void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2657 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002658 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002659 this->onDrawPosText(text, byteLength, pos, paint);
2660}
2661void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2662 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002663 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002664 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2665}
2666void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2667 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002668 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reed@google.come0d9ce82014-04-23 04:00:17 +00002669 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2670}
fmalita00d5c2c2014-08-21 08:53:26 -07002671void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2672 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002673 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
bsalomon49f085d2014-09-05 13:34:00 -07002674 if (blob) {
fmalita00d5c2c2014-08-21 08:53:26 -07002675 this->onDrawTextBlob(blob, x, y, paint);
2676 }
2677}
reed@google.come0d9ce82014-04-23 04:00:17 +00002678
reed41af9662015-01-05 07:49:08 -08002679void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2680 const SkPoint verts[], const SkPoint texs[],
2681 const SkColor colors[], SkXfermode* xmode,
2682 const uint16_t indices[], int indexCount,
2683 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002684 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002685 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002686
reed@android.com8a1c16f2008-12-17 15:59:43 +00002687 while (iter.next()) {
2688 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002689 colors, xmode, indices, indexCount,
2690 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002691 }
reed@google.com4b226022011-01-11 18:32:13 +00002692
reed@google.com4e2b3d32011-04-07 14:18:59 +00002693 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002694}
2695
dandovb3c9d1c2014-08-12 08:34:29 -07002696void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2697 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002698 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002699 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002700 return;
2701 }
mtklein6cfa73a2014-08-13 13:33:49 -07002702
dandovecfff212014-08-04 10:02:00 -07002703 // Since a patch is always within the convex hull of the control points, we discard it when its
2704 // bounding rectangle is completely outside the current clip.
2705 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002706 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002707 if (this->quickReject(bounds)) {
2708 return;
2709 }
mtklein6cfa73a2014-08-13 13:33:49 -07002710
dandovb3c9d1c2014-08-12 08:34:29 -07002711 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2712}
2713
2714void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2715 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2716
halcanary96fcdcc2015-08-27 07:41:13 -07002717 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002718
dandovecfff212014-08-04 10:02:00 -07002719 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002720 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002721 }
mtklein6cfa73a2014-08-13 13:33:49 -07002722
dandovecfff212014-08-04 10:02:00 -07002723 LOOPER_END
2724}
2725
reeda8db7282015-07-07 10:22:31 -07002726void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
2727 if (dr) {
2728 if (x || y) {
2729 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2730 this->onDrawDrawable(dr, &matrix);
2731 } else {
halcanary96fcdcc2015-08-27 07:41:13 -07002732 this->onDrawDrawable(dr, nullptr);
reeda8db7282015-07-07 10:22:31 -07002733 }
reed6a070dc2014-11-11 19:36:09 -08002734 }
2735}
2736
reeda8db7282015-07-07 10:22:31 -07002737void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2738 if (dr) {
2739 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002740 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002741 }
2742 this->onDrawDrawable(dr, matrix);
2743 }
2744}
2745
2746void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2747 SkRect bounds = dr->getBounds();
2748 if (matrix) {
2749 matrix->mapRect(&bounds);
2750 }
2751 if (this->quickReject(bounds)) {
2752 return;
2753 }
2754 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002755}
2756
reed71c3c762015-06-24 10:29:17 -07002757void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2758 const SkColor colors[], int count, SkXfermode::Mode mode,
2759 const SkRect* cull, const SkPaint* paint) {
2760 if (cull && this->quickReject(*cull)) {
2761 return;
2762 }
2763
2764 SkPaint pnt;
2765 if (paint) {
2766 pnt = *paint;
2767 }
2768
halcanary96fcdcc2015-08-27 07:41:13 -07002769 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002770 while (iter.next()) {
2771 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2772 }
2773 LOOPER_END
2774}
2775
reed@android.com8a1c16f2008-12-17 15:59:43 +00002776//////////////////////////////////////////////////////////////////////////////
2777// These methods are NOT virtual, and therefore must call back into virtual
2778// methods, rather than actually drawing themselves.
2779//////////////////////////////////////////////////////////////////////////////
2780
2781void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002782 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002783 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002784 SkPaint paint;
2785
2786 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002787 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002788 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002789 }
2790 this->drawPaint(paint);
2791}
2792
reed@android.com845fdac2009-06-23 03:01:32 +00002793void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002794 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002795 SkPaint paint;
2796
2797 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002798 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002799 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002800 }
2801 this->drawPaint(paint);
2802}
2803
2804void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002805 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002806 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002807
reed@android.com8a1c16f2008-12-17 15:59:43 +00002808 pt.set(x, y);
2809 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2810}
2811
2812void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002813 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002814 SkPoint pt;
2815 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002816
reed@android.com8a1c16f2008-12-17 15:59:43 +00002817 pt.set(x, y);
2818 paint.setColor(color);
2819 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2820}
2821
2822void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2823 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002824 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002825 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002826
reed@android.com8a1c16f2008-12-17 15:59:43 +00002827 pts[0].set(x0, y0);
2828 pts[1].set(x1, y1);
2829 this->drawPoints(kLines_PointMode, 2, pts, paint);
2830}
2831
2832void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2833 SkScalar right, SkScalar bottom,
2834 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002835 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002836 SkRect r;
2837
2838 r.set(left, top, right, bottom);
2839 this->drawRect(r, paint);
2840}
2841
2842void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2843 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002844 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002845 if (radius < 0) {
2846 radius = 0;
2847 }
2848
2849 SkRect r;
2850 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002851 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002852}
2853
2854void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2855 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002856 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002857 if (rx > 0 && ry > 0) {
2858 if (paint.canComputeFastBounds()) {
2859 SkRect storage;
reed@google.com3b3e8952012-08-16 20:53:31 +00002860 if (this->quickReject(paint.computeFastBounds(r, &storage))) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002861 return;
2862 }
2863 }
reed@google.com4ed0fb72012-12-12 20:48:18 +00002864 SkRRect rrect;
2865 rrect.setRectXY(r, rx, ry);
2866 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002867 } else {
2868 this->drawRect(r, paint);
2869 }
2870}
2871
reed@android.com8a1c16f2008-12-17 15:59:43 +00002872void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2873 SkScalar sweepAngle, bool useCenter,
2874 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002875 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002876 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
2877 this->drawOval(oval, paint);
2878 } else {
2879 SkPath path;
2880 if (useCenter) {
2881 path.moveTo(oval.centerX(), oval.centerY());
2882 }
2883 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
2884 if (useCenter) {
2885 path.close();
2886 }
2887 this->drawPath(path, paint);
2888 }
2889}
2890
2891void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
2892 const SkPath& path, SkScalar hOffset,
2893 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002894 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002895 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00002896
reed@android.com8a1c16f2008-12-17 15:59:43 +00002897 matrix.setTranslate(hOffset, vOffset);
2898 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
2899}
2900
reed@android.comf76bacf2009-05-13 14:00:33 +00002901///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07002902
2903/**
2904 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
2905 * against the playback cost of recursing into the subpicture to get at its actual ops.
2906 *
2907 * For now we pick a conservatively small value, though measurement (and other heuristics like
2908 * the type of ops contained) may justify changing this value.
2909 */
2910#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07002911
reedd5fa1a42014-08-09 11:08:05 -07002912void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reed1c2c4412015-04-30 13:09:24 -07002913 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
bsalomon49f085d2014-09-05 13:34:00 -07002914 if (picture) {
reedd5fa1a42014-08-09 11:08:05 -07002915 if (matrix && matrix->isIdentity()) {
halcanary96fcdcc2015-08-27 07:41:13 -07002916 matrix = nullptr;
reedd5fa1a42014-08-09 11:08:05 -07002917 }
reed1c2c4412015-04-30 13:09:24 -07002918 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
2919 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
2920 picture->playback(this);
2921 } else {
2922 this->onDrawPicture(picture, matrix, paint);
2923 }
reedd5fa1a42014-08-09 11:08:05 -07002924 }
2925}
robertphillips9b14f262014-06-04 05:40:44 -07002926
reedd5fa1a42014-08-09 11:08:05 -07002927void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
2928 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07002929 if (!paint || paint->canComputeFastBounds()) {
2930 SkRect bounds = picture->cullRect();
2931 if (paint) {
2932 paint->computeFastBounds(bounds, &bounds);
2933 }
2934 if (matrix) {
2935 matrix->mapRect(&bounds);
2936 }
2937 if (this->quickReject(bounds)) {
2938 return;
2939 }
2940 }
2941
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002942 SkBaseDevice* device = this->getTopDevice();
bsalomon49f085d2014-09-05 13:34:00 -07002943 if (device) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002944 // Canvas has to first give the device the opportunity to render
2945 // the picture itself.
reedd5fa1a42014-08-09 11:08:05 -07002946 if (device->EXPERIMENTAL_drawPicture(this, picture, matrix, paint)) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +00002947 return; // the device has rendered the entire picture
2948 }
2949 }
2950
robertphillipsa8d7f0b2014-08-29 08:03:56 -07002951 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07002952 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002953}
2954
reed@android.com8a1c16f2008-12-17 15:59:43 +00002955///////////////////////////////////////////////////////////////////////////////
2956///////////////////////////////////////////////////////////////////////////////
2957
2958SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07002959 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002960
2961 SkASSERT(canvas);
2962
2963 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
2964 fDone = !fImpl->next();
2965}
2966
2967SkCanvas::LayerIter::~LayerIter() {
2968 fImpl->~SkDrawIter();
2969}
2970
2971void SkCanvas::LayerIter::next() {
2972 fDone = !fImpl->next();
2973}
2974
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002975SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002976 return fImpl->getDevice();
2977}
2978
2979const SkMatrix& SkCanvas::LayerIter::matrix() const {
2980 return fImpl->getMatrix();
2981}
2982
2983const SkPaint& SkCanvas::LayerIter::paint() const {
2984 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07002985 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002986 paint = &fDefaultPaint;
2987 }
2988 return *paint;
2989}
2990
2991const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
2992int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
2993int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00002994
2995///////////////////////////////////////////////////////////////////////////////
2996
fmalitac3b589a2014-06-05 12:40:07 -07002997SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00002998
2999///////////////////////////////////////////////////////////////////////////////
3000
3001static bool supported_for_raster_canvas(const SkImageInfo& info) {
3002 switch (info.alphaType()) {
3003 case kPremul_SkAlphaType:
3004 case kOpaque_SkAlphaType:
3005 break;
3006 default:
3007 return false;
3008 }
3009
3010 switch (info.colorType()) {
3011 case kAlpha_8_SkColorType:
3012 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003013 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003014 break;
3015 default:
3016 return false;
3017 }
3018
3019 return true;
3020}
3021
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003022SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3023 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003024 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003025 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003026
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003027 SkBitmap bitmap;
3028 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003029 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003030 }
halcanary385fe4d2015-08-26 13:07:48 -07003031 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003032}
reedd5fa1a42014-08-09 11:08:05 -07003033
3034///////////////////////////////////////////////////////////////////////////////
3035
3036SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003037 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003038 : fCanvas(canvas)
3039 , fSaveCount(canvas->getSaveCount())
3040{
bsalomon49f085d2014-09-05 13:34:00 -07003041 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003042 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003043 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003044 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003045 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003046 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003047 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003048 canvas->save();
3049 }
mtklein6cfa73a2014-08-13 13:33:49 -07003050
bsalomon49f085d2014-09-05 13:34:00 -07003051 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003052 canvas->concat(*matrix);
3053 }
3054}
3055
3056SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3057 fCanvas->restoreToCount(fSaveCount);
3058}