blob: f8069ee2d3d14bac546195f5bd415ab2a7157902 [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"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
piotaixrb5fae932014-09-24 13:03:30 -070017#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080018#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070019#include "SkImageFilter.h"
20#include "SkImageFilterCache.h"
msarettc573a402016-08-02 08:05:56 -070021#include "SkLatticeIter.h"
Mike Reed5df49342016-11-12 08:06:55 -060022#include "SkMakeUnique.h"
reed262a71b2015-12-05 13:07:27 -080023#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000024#include "SkMetaData.h"
Florin Malitaee424ac2016-12-01 12:47:59 -050025#include "SkNoDrawCanvas.h"
msarettfbfa2582016-08-12 08:29:08 -070026#include "SkNx.h"
reedc83a2972015-07-16 07:40:45 -070027#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070028#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000029#include "SkPicture.h"
vjiaoblackb2796fd2016-09-09 09:22:39 -070030#include "SkRadialShadowMapShader.h"
reed@google.com00177082011-10-12 14:34:30 +000031#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080032#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000033#include "SkRRect.h"
vjiaoblack904527d2016-08-09 09:32:09 -070034#include "SkShadowPaintFilterCanvas.h"
35#include "SkShadowShader.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000036#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080037#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000038#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070039#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000040#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000041#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080042#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070043#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000044
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000045#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080046#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000047#include "GrRenderTarget.h"
bsalomon614d8f92016-07-13 15:42:40 -070048#include "SkGrPriv.h"
vjiaoblacke6f5d562016-08-25 06:30:23 -070049
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000050#endif
Mike Reedebfce6d2016-12-12 10:02:12 -050051#include "SkClipOpPriv.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000052
reede3b38ce2016-01-08 09:18:44 -080053#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
54
reedc83a2972015-07-16 07:40:45 -070055/*
56 * Return true if the drawing this rect would hit every pixels in the canvas.
57 *
58 * Returns false if
59 * - rect does not contain the canvas' bounds
60 * - paint is not fill
61 * - paint would blur or otherwise change the coverage of the rect
62 */
63bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
64 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070065 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
66 (int)kNone_ShaderOverrideOpacity,
67 "need_matching_enums0");
68 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
69 (int)kOpaque_ShaderOverrideOpacity,
70 "need_matching_enums1");
71 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
72 (int)kNotOpaque_ShaderOverrideOpacity,
73 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070074
75 const SkISize size = this->getBaseLayerSize();
76 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
77 if (!this->getClipStack()->quickContains(bounds)) {
78 return false;
79 }
80
81 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070082 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070083 return false; // conservative
84 }
halcanaryc5769b22016-08-10 07:13:21 -070085
86 SkRect devRect;
87 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
88 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070089 return false;
90 }
91 }
92
93 if (paint) {
94 SkPaint::Style paintStyle = paint->getStyle();
95 if (!(paintStyle == SkPaint::kFill_Style ||
96 paintStyle == SkPaint::kStrokeAndFill_Style)) {
97 return false;
98 }
99 if (paint->getMaskFilter() || paint->getLooper()
100 || paint->getPathEffect() || paint->getImageFilter()) {
101 return false; // conservative
102 }
103 }
104 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
105}
106
107///////////////////////////////////////////////////////////////////////////////////////////////////
108
reedd990e2f2014-12-22 11:58:30 -0800109static bool gIgnoreSaveLayerBounds;
110void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
111 gIgnoreSaveLayerBounds = ignore;
112}
113bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
114 return gIgnoreSaveLayerBounds;
115}
116
reed0acf1b42014-12-22 16:12:38 -0800117static bool gTreatSpriteAsBitmap;
118void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
119 gTreatSpriteAsBitmap = spriteAsBitmap;
120}
121bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
122 return gTreatSpriteAsBitmap;
123}
124
reed@google.comda17f752012-08-16 18:27:05 +0000125// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126//#define SK_TRACE_SAVERESTORE
127
128#ifdef SK_TRACE_SAVERESTORE
129 static int gLayerCounter;
130 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
131 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
132
133 static int gRecCounter;
134 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
135 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
136
137 static int gCanvasCounter;
138 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
139 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
140#else
141 #define inc_layer()
142 #define dec_layer()
143 #define inc_rec()
144 #define dec_rec()
145 #define inc_canvas()
146 #define dec_canvas()
147#endif
148
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000149typedef SkTLazy<SkPaint> SkLazyPaint;
150
reedc83a2972015-07-16 07:40:45 -0700151void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000152 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700153 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
154 ? SkSurface::kDiscard_ContentChangeMode
155 : SkSurface::kRetain_ContentChangeMode);
156 }
157}
158
159void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
160 ShaderOverrideOpacity overrideOpacity) {
161 if (fSurfaceBase) {
162 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
163 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
164 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
165 // and therefore we don't care which mode we're in.
166 //
167 if (fSurfaceBase->outstandingImageSnapshot()) {
168 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
169 mode = SkSurface::kDiscard_ContentChangeMode;
170 }
171 }
172 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000173 }
174}
175
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000178/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179 The clip/matrix/proc are fields that reflect the top of the save/restore
180 stack. Whenever the canvas changes, it marks a dirty flag, and then before
181 these are used (assuming we're not on a layer) we rebuild these cache
182 values: they reflect the top of the save stack, but translated and clipped
183 by the device's XY offset and bitmap-bounds.
184*/
185struct DeviceCM {
186 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000187 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000188 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000189 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700190 const SkMatrix* fMatrix;
191 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700192 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000193
reed96e657d2015-03-10 17:30:07 -0700194 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed7503d602016-07-15 14:23:29 -0700195 bool conservativeRasterClip, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700196 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700197 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700198 , fStashedMatrix(stashed)
reedd9544982014-09-09 18:46:22 -0700199 {
reed2c9e2002016-07-25 08:05:22 -0700200 SkSafeRef(device);
reed@google.com4b226022011-01-11 18:32:13 +0000201 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700202 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000203 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000205 ~DeviceCM() {
reed2c9e2002016-07-25 08:05:22 -0700206 SkSafeUnref(fDevice);
halcanary385fe4d2015-08-26 13:07:48 -0700207 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000208 }
reed@google.com4b226022011-01-11 18:32:13 +0000209
mtkleinfeaadee2015-04-08 11:25:48 -0700210 void reset(const SkIRect& bounds) {
211 SkASSERT(!fPaint);
212 SkASSERT(!fNext);
213 SkASSERT(fDevice);
214 fClip.setRect(bounds);
215 }
216
reed@google.com045e62d2011-10-24 12:19:46 +0000217 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
reedde6c5312016-09-02 12:10:07 -0700218 SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000219 int x = fDevice->getOrigin().x();
220 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 int width = fDevice->width();
222 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000223
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 if ((x | y) == 0) {
225 fMatrix = &totalMatrix;
226 fClip = totalClip;
227 } else {
228 fMatrixStorage = totalMatrix;
229 fMatrixStorage.postTranslate(SkIntToScalar(-x),
230 SkIntToScalar(-y));
231 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000232
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 totalClip.translate(-x, -y, &fClip);
234 }
235
reed@google.com045e62d2011-10-24 12:19:46 +0000236 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237
238 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000239
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000241 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 SkRegion::kDifference_Op);
243 }
reed@google.com4b226022011-01-11 18:32:13 +0000244
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245#ifdef SK_DEBUG
246 if (!fClip.isEmpty()) {
247 SkIRect deviceR;
248 deviceR.set(0, 0, width, height);
249 SkASSERT(deviceR.contains(fClip.getBounds()));
250 }
251#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000252 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253};
254
255/* This is the record we keep for each save/restore level in the stack.
256 Since a level optionally copies the matrix and/or stack, we have pointers
257 for these fields. If the value is copied for this level, the copy is
258 stored in the ...Storage field, and the pointer points to that. If the
259 value is not copied for this level, we ignore ...Storage, and just point
260 at the corresponding value in the previous level in the stack.
261*/
262class SkCanvas::MCRec {
263public:
reed1f836ee2014-07-07 07:49:34 -0700264 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700265 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 /* If there are any layers in the stack, this points to the top-most
267 one that is at or below this level in the stack (so we know what
268 bitmap/device to draw into from this level. This value is NOT
269 reference counted, since the real owner is either our fLayer field,
270 or a previous one in a lower level.)
271 */
reed2ff1fce2014-12-11 07:07:37 -0800272 DeviceCM* fTopLayer;
273 SkRasterClip fRasterClip;
274 SkMatrix fMatrix;
275 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276
vjiaoblacke5de1302016-07-13 14:05:28 -0700277 // This is the current cumulative depth (aggregate of all done translateZ calls)
278 SkScalar fCurDrawDepth;
279
reedd9544982014-09-09 18:46:22 -0700280 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700281 fFilter = nullptr;
282 fLayer = nullptr;
283 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800284 fMatrix.reset();
285 fDeferredSaveCount = 0;
vjiaoblacke5de1302016-07-13 14:05:28 -0700286 fCurDrawDepth = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700287
reedd9544982014-09-09 18:46:22 -0700288 // don't bother initializing fNext
289 inc_rec();
290 }
vjiaoblacke5de1302016-07-13 14:05:28 -0700291 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
292 fCurDrawDepth(prev.fCurDrawDepth) {
reedd9544982014-09-09 18:46:22 -0700293 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700294 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700295 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800296 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700297
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298 // don't bother initializing fNext
299 inc_rec();
300 }
301 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000302 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700303 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304 dec_rec();
305 }
mtkleinfeaadee2015-04-08 11:25:48 -0700306
307 void reset(const SkIRect& bounds) {
308 SkASSERT(fLayer);
309 SkASSERT(fDeferredSaveCount == 0);
310
311 fMatrix.reset();
312 fRasterClip.setRect(bounds);
313 fLayer->reset(bounds);
314 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000315};
316
reed02f9ed72016-09-06 09:06:18 -0700317static SkIRect compute_device_bounds(SkBaseDevice* device) {
318 return SkIRect::MakeXYWH(device->getOrigin().x(), device->getOrigin().y(),
319 device->width(), device->height());
320}
321
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322class SkDrawIter : public SkDraw {
323public:
reed3aafe112016-08-18 12:45:34 -0700324 SkDrawIter(SkCanvas* canvas) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 canvas->updateDeviceCMCache();
326
bungeman6bd52842016-10-27 09:30:08 -0700327 fClipStack = canvas->getClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000328 fCurrLayer = canvas->fMCRec->fTopLayer;
reed02f9ed72016-09-06 09:06:18 -0700329
330 fMultiDeviceCS = nullptr;
331 if (fCurrLayer->fNext) {
bungeman6bd52842016-10-27 09:30:08 -0700332 fMultiDeviceCS = canvas->fClipStack.get();
reed02f9ed72016-09-06 09:06:18 -0700333 fMultiDeviceCS->save();
334 }
335 }
336
337 ~SkDrawIter() {
338 if (fMultiDeviceCS) {
339 fMultiDeviceCS->restore();
340 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 }
reed@google.com4b226022011-01-11 18:32:13 +0000342
reed@android.com8a1c16f2008-12-17 15:59:43 +0000343 bool next() {
reed02f9ed72016-09-06 09:06:18 -0700344 if (fMultiDeviceCS && fDevice) {
345 // remove the previous device's bounds
Mike Reedc1f77742016-12-09 09:00:50 -0500346 fMultiDeviceCS->clipDevRect(compute_device_bounds(fDevice), kDifference_SkClipOp);
reed02f9ed72016-09-06 09:06:18 -0700347 }
348
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 // skip over recs with empty clips
reed3aafe112016-08-18 12:45:34 -0700350 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
351 fCurrLayer = fCurrLayer->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000352 }
353
reed@google.comf68c5e22012-02-24 16:38:58 +0000354 const DeviceCM* rec = fCurrLayer;
355 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356
357 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000358 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000359 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700360 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700361 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700362 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000364 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000365
366 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700367 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369 return true;
370 }
371 return false;
372 }
reed@google.com4b226022011-01-11 18:32:13 +0000373
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000374 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700375 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000376 int getX() const { return fDevice->getOrigin().x(); }
377 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000380
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382 const DeviceCM* fCurrLayer;
383 const SkPaint* fPaint; // May be null.
reed02f9ed72016-09-06 09:06:18 -0700384 SkClipStack* fMultiDeviceCS;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000385
386 typedef SkDraw INHERITED;
387};
388
389/////////////////////////////////////////////////////////////////////////////
390
reeddbc3cef2015-04-29 12:18:57 -0700391static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
392 return lazy->isValid() ? lazy->get() : lazy->set(orig);
393}
394
395/**
396 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700397 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700398 */
reedd053ce92016-03-22 10:17:23 -0700399static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700400 SkImageFilter* imgf = paint.getImageFilter();
401 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700402 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700403 }
404
reedd053ce92016-03-22 10:17:23 -0700405 SkColorFilter* imgCFPtr;
406 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700407 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700408 }
reedd053ce92016-03-22 10:17:23 -0700409 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700410
411 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700412 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700413 // there is no existing paint colorfilter, so we can just return the imagefilter's
414 return imgCF;
415 }
416
417 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
418 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700419 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700420}
421
senorblanco87e066e2015-10-28 11:23:36 -0700422/**
423 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
424 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
425 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
426 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
427 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
428 * conservative "effective" bounds based on the settings in the paint... with one exception. This
429 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
430 * deliberately ignored.
431 */
432static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
433 const SkRect& rawBounds,
434 SkRect* storage) {
435 SkPaint tmpUnfiltered(paint);
436 tmpUnfiltered.setImageFilter(nullptr);
437 if (tmpUnfiltered.canComputeFastBounds()) {
438 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
439 } else {
440 return rawBounds;
441 }
442}
443
reed@android.com8a1c16f2008-12-17 15:59:43 +0000444class AutoDrawLooper {
445public:
senorblanco87e066e2015-10-28 11:23:36 -0700446 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
447 // paint. It's used to determine the size of the offscreen layer for filters.
448 // If null, the clip will be used instead.
reed3aafe112016-08-18 12:45:34 -0700449 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700450 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000451 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800452#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800454#else
455 fFilter = nullptr;
456#endif
reed4a8126e2014-09-22 07:29:03 -0700457 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000458 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700459 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000460 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461
reedd053ce92016-03-22 10:17:23 -0700462 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700463 if (simplifiedCF) {
464 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700465 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700466 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700467 fPaint = paint;
468 }
469
470 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700471 /**
472 * We implement ImageFilters for a given draw by creating a layer, then applying the
473 * imagefilter to the pixels of that layer (its backing surface/image), and then
474 * we call restore() to xfer that layer to the main canvas.
475 *
476 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
477 * 2. Generate the src pixels:
478 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
479 * return (fPaint). We then draw the primitive (using srcover) into a cleared
480 * buffer/surface.
481 * 3. Restore the layer created in #1
482 * The imagefilter is passed the buffer/surface from the layer (now filled with the
483 * src pixels of the primitive). It returns a new "filtered" buffer, which we
484 * draw onto the previous layer using the xfermode from the original paint.
485 */
reed@google.com8926b162012-03-23 15:36:36 +0000486 SkPaint tmp;
Mike Reed5e257172016-11-01 11:22:05 -0400487 tmp.setImageFilter(sk_ref_sp(fPaint->getImageFilter()));
reed374772b2016-10-05 17:33:02 -0700488 tmp.setBlendMode(fPaint->getBlendMode());
senorblanco87e066e2015-10-28 11:23:36 -0700489 SkRect storage;
490 if (rawBounds) {
491 // Make rawBounds include all paint outsets except for those due to image filters.
492 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
493 }
reedbfd5f172016-01-07 11:28:08 -0800494 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700495 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700496 fTempLayerForImageFilter = true;
497 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000498 }
499
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000500 if (SkDrawLooper* looper = paint.getLooper()) {
herbbf6d80a2016-11-15 06:26:56 -0800501 fLooperContext = fLooperContextAllocator.createWithIniter(
502 looper->contextSize(),
503 [&](void* buffer) {
504 return looper->createContext(canvas, buffer);
505 });
reed@google.com129ec222012-05-15 13:24:09 +0000506 fIsSimple = false;
507 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700508 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000509 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700510 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000511 }
512 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000513
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700515 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000516 fCanvas->internalRestore();
517 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000518 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000520
reed@google.com4e2b3d32011-04-07 14:18:59 +0000521 const SkPaint& paint() const {
522 SkASSERT(fPaint);
523 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000525
reed@google.com129ec222012-05-15 13:24:09 +0000526 bool next(SkDrawFilter::Type drawType) {
527 if (fDone) {
528 return false;
529 } else if (fIsSimple) {
530 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000531 return !fPaint->nothingToDraw();
532 } else {
533 return this->doNext(drawType);
534 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000535 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000536
reed@android.com8a1c16f2008-12-17 15:59:43 +0000537private:
reeddbc3cef2015-04-29 12:18:57 -0700538 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
539 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000540 SkCanvas* fCanvas;
541 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000542 SkDrawFilter* fFilter;
543 const SkPaint* fPaint;
544 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700545 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000546 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000547 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000548 SkDrawLooper::Context* fLooperContext;
549 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000550
551 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000552};
553
reed@google.com129ec222012-05-15 13:24:09 +0000554bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700555 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000556 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700557 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000558
reeddbc3cef2015-04-29 12:18:57 -0700559 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
560 *fLazyPaintInit.get() : fOrigPaint);
reed@google.com129ec222012-05-15 13:24:09 +0000561
reed5c476fb2015-04-20 08:04:21 -0700562 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700563 paint->setImageFilter(nullptr);
reed374772b2016-10-05 17:33:02 -0700564 paint->setBlendMode(SkBlendMode::kSrcOver);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000565 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000566
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000567 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000568 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000569 return false;
570 }
571 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000572 if (!fFilter->filter(paint, drawType)) {
573 fDone = true;
574 return false;
575 }
halcanary96fcdcc2015-08-27 07:41:13 -0700576 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000577 // no looper means we only draw once
578 fDone = true;
579 }
580 }
581 fPaint = paint;
582
583 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000584 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000585 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000586 }
587
588 // call this after any possible paint modifiers
589 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700590 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000591 return false;
592 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000593 return true;
594}
595
reed@android.com8a1c16f2008-12-17 15:59:43 +0000596////////// macros to place around the internal draw calls //////////////////
597
reed3aafe112016-08-18 12:45:34 -0700598#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
599 this->predrawNotify(); \
600 AutoDrawLooper looper(this, paint, skipLayerForFilter, bounds); \
601 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
reed262a71b2015-12-05 13:07:27 -0800602 SkDrawIter iter(this);
603
604
reed@google.com8926b162012-03-23 15:36:36 +0000605#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000606 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700607 AutoDrawLooper looper(this, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000608 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000609 SkDrawIter iter(this);
610
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000611#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000612 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700613 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000614 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000615 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000616
reedc83a2972015-07-16 07:40:45 -0700617#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
618 this->predrawNotify(bounds, &paint, auxOpaque); \
reed3aafe112016-08-18 12:45:34 -0700619 AutoDrawLooper looper(this, paint, false, bounds); \
reedc83a2972015-07-16 07:40:45 -0700620 while (looper.next(type)) { \
621 SkDrawIter iter(this);
622
reed@google.com4e2b3d32011-04-07 14:18:59 +0000623#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624
625////////////////////////////////////////////////////////////////////////////
626
msarettfbfa2582016-08-12 08:29:08 -0700627static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
628 if (bounds.isEmpty()) {
629 return SkRect::MakeEmpty();
630 }
631
632 // Expand bounds out by 1 in case we are anti-aliasing. We store the
633 // bounds as floats to enable a faster quick reject implementation.
634 SkRect dst;
635 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
636 return dst;
637}
638
mtkleinfeaadee2015-04-08 11:25:48 -0700639void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
640 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700641 fClipStack->reset();
642 fMCRec->reset(bounds);
643
644 // We're peering through a lot of structs here. Only at this scope do we
645 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
646 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
msarettfbfa2582016-08-12 08:29:08 -0700647 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700648 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700649}
650
reedd9544982014-09-09 18:46:22 -0700651SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800652 if (device && device->forceConservativeRasterClip()) {
653 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
654 }
655 // Since init() is only called once by our constructors, it is safe to perform this
656 // const-cast.
657 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
658
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000659 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700660 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800661 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700662 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700663#ifdef SK_EXPERIMENTAL_SHADOWING
664 fLights = nullptr;
665#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666
halcanary385fe4d2015-08-26 13:07:48 -0700667 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700668
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700670 new (fMCRec) MCRec(fConservativeRasterClip);
msarett9637ea92016-08-18 14:03:30 -0700671 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000672
reeda499f902015-05-01 09:34:31 -0700673 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
674 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700675 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700676 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700677
reed@android.com8a1c16f2008-12-17 15:59:43 +0000678 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679
halcanary96fcdcc2015-08-27 07:41:13 -0700680 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000681
reedf92c8662014-08-18 08:02:43 -0700682 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700683 // The root device and the canvas should always have the same pixel geometry
684 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700685 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800686 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700687 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700688 }
msarettfbfa2582016-08-12 08:29:08 -0700689
reedf92c8662014-08-18 08:02:43 -0700690 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000691}
692
reed@google.comcde92112011-07-06 20:00:52 +0000693SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000694 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700695 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800696 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000697{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000698 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000699
halcanary96fcdcc2015-08-27 07:41:13 -0700700 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000701}
702
reedd9544982014-09-09 18:46:22 -0700703static SkBitmap make_nopixels(int width, int height) {
704 SkBitmap bitmap;
705 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
706 return bitmap;
707}
708
709class SkNoPixelsBitmapDevice : public SkBitmapDevice {
710public:
robertphillipsfcf78292015-06-19 11:49:52 -0700711 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
712 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800713 {
714 this->setOrigin(bounds.x(), bounds.y());
715 }
reedd9544982014-09-09 18:46:22 -0700716
717private:
piotaixrb5fae932014-09-24 13:03:30 -0700718
reedd9544982014-09-09 18:46:22 -0700719 typedef SkBitmapDevice INHERITED;
720};
721
reed96a857e2015-01-25 10:33:58 -0800722SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000723 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800724 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800725 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000726{
727 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700728
halcanary385fe4d2015-08-26 13:07:48 -0700729 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
730 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700731}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000732
reed78e27682014-11-19 08:04:34 -0800733SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700734 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700735 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800736 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700737{
738 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700739
halcanary385fe4d2015-08-26 13:07:48 -0700740 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700741}
742
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000743SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000744 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700745 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800746 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000747{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700749
reedd9544982014-09-09 18:46:22 -0700750 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000751}
752
robertphillipsfcf78292015-06-19 11:49:52 -0700753SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
754 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700755 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800756 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700757{
758 inc_canvas();
759
760 this->init(device, flags);
761}
762
reed4a8126e2014-09-22 07:29:03 -0700763SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700764 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700765 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800766 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700767{
768 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700769
Hal Canary704cd322016-11-07 14:13:52 -0500770 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
771 this->init(device.get(), kDefault_InitFlags);
reed4a8126e2014-09-22 07:29:03 -0700772}
reed29c857d2014-09-21 10:25:07 -0700773
reed4a8126e2014-09-22 07:29:03 -0700774SkCanvas::SkCanvas(const SkBitmap& bitmap)
775 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
776 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800777 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700778{
779 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700780
Hal Canary704cd322016-11-07 14:13:52 -0500781 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
782 this->init(device.get(), kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000783}
784
785SkCanvas::~SkCanvas() {
786 // free up the contents of our deque
787 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 this->internalRestore(); // restore the last, since we're going away
790
halcanary385fe4d2015-08-26 13:07:48 -0700791 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000792
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793 dec_canvas();
794}
795
fmalita53d9f1c2016-01-25 06:23:54 -0800796#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000797SkDrawFilter* SkCanvas::getDrawFilter() const {
798 return fMCRec->fFilter;
799}
800
801SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700802 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
804 return filter;
805}
fmalita77650002016-01-21 18:47:11 -0800806#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000808SkMetaData& SkCanvas::getMetaData() {
809 // metadata users are rare, so we lazily allocate it. If that changes we
810 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700811 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000812 fMetaData = new SkMetaData;
813 }
814 return *fMetaData;
815}
816
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817///////////////////////////////////////////////////////////////////////////////
818
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000819void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700820 this->onFlush();
821}
822
823void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000824 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000825 if (device) {
826 device->flush();
827 }
828}
829
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000830SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000831 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000832 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
833}
834
senorblancoafc7cce2016-02-02 18:44:15 -0800835SkIRect SkCanvas::getTopLayerBounds() const {
836 SkBaseDevice* d = this->getTopDevice();
837 if (!d) {
838 return SkIRect::MakeEmpty();
839 }
840 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
841}
842
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000843SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000845 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000846 SkASSERT(rec && rec->fLayer);
847 return rec->fLayer->fDevice;
848}
849
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000850SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000851 if (updateMatrixClip) {
852 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
853 }
reed@google.com9266fed2011-03-30 00:18:03 +0000854 return fMCRec->fTopLayer->fDevice;
855}
856
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000857bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700858 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000859 return false;
860 }
861
862 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700863 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700864 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000865 return false;
866 }
867 weAllocated = true;
868 }
869
reedcf01e312015-05-23 19:14:51 -0700870 SkAutoPixmapUnlock unlocker;
871 if (bitmap->requestLock(&unlocker)) {
872 const SkPixmap& pm = unlocker.pixmap();
873 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
874 return true;
875 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000876 }
877
878 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700879 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000880 }
881 return false;
882}
reed@google.com51df9e32010-12-23 19:29:18 +0000883
bsalomon@google.comc6980972011-11-02 19:57:21 +0000884bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000885 SkIRect r = srcRect;
886 const SkISize size = this->getBaseLayerSize();
887 if (!r.intersect(0, 0, size.width(), size.height())) {
888 bitmap->reset();
889 return false;
890 }
891
reed84825042014-09-02 12:50:45 -0700892 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000893 // bitmap will already be reset.
894 return false;
895 }
896 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
897 bitmap->reset();
898 return false;
899 }
900 return true;
901}
902
reed96472de2014-12-10 09:53:42 -0800903bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000904 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000905 if (!device) {
906 return false;
907 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000908 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800909
reed96472de2014-12-10 09:53:42 -0800910 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
911 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000912 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000913 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000914
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000915 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800916 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000917}
918
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000919bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700920 SkAutoPixmapUnlock unlocker;
921 if (bitmap.requestLock(&unlocker)) {
922 const SkPixmap& pm = unlocker.pixmap();
923 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000924 }
925 return false;
926}
927
928bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
929 int x, int y) {
930 switch (origInfo.colorType()) {
931 case kUnknown_SkColorType:
932 case kIndex_8_SkColorType:
933 return false;
934 default:
935 break;
936 }
halcanary96fcdcc2015-08-27 07:41:13 -0700937 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000938 return false;
939 }
940
941 const SkISize size = this->getBaseLayerSize();
942 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
943 if (!target.intersect(0, 0, size.width(), size.height())) {
944 return false;
945 }
946
947 SkBaseDevice* device = this->getDevice();
948 if (!device) {
949 return false;
950 }
951
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000952 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700953 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000954
955 // if x or y are negative, then we have to adjust pixels
956 if (x > 0) {
957 x = 0;
958 }
959 if (y > 0) {
960 y = 0;
961 }
962 // here x,y are either 0 or negative
963 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
964
reed4af35f32014-06-27 17:47:49 -0700965 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700966 const bool completeOverwrite = info.dimensions() == size;
967 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700968
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000969 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000970 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000971}
reed@google.com51df9e32010-12-23 19:29:18 +0000972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973//////////////////////////////////////////////////////////////////////////////
974
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975void SkCanvas::updateDeviceCMCache() {
976 if (fDeviceCMDirty) {
977 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700978 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000980
halcanary96fcdcc2015-08-27 07:41:13 -0700981 if (nullptr == layer->fNext) { // only one layer
reedde6c5312016-09-02 12:10:07 -0700982 layer->updateMC(totalMatrix, totalClip, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000984 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 do {
reedde6c5312016-09-02 12:10:07 -0700986 layer->updateMC(totalMatrix, clip, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700987 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 }
989 fDeviceCMDirty = false;
990 }
991}
992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993///////////////////////////////////////////////////////////////////////////////
994
reed2ff1fce2014-12-11 07:07:37 -0800995void SkCanvas::checkForDeferredSave() {
996 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800997 this->doSave();
998 }
999}
1000
reedf0090cb2014-11-26 08:55:51 -08001001int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001002#ifdef SK_DEBUG
1003 int count = 0;
1004 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1005 for (;;) {
1006 const MCRec* rec = (const MCRec*)iter.next();
1007 if (!rec) {
1008 break;
1009 }
1010 count += 1 + rec->fDeferredSaveCount;
1011 }
1012 SkASSERT(count == fSaveCount);
1013#endif
1014 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001015}
1016
1017int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001018 fSaveCount += 1;
1019 fMCRec->fDeferredSaveCount += 1;
1020 return this->getSaveCount() - 1; // return our prev value
1021}
1022
1023void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001024 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001025
1026 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1027 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001028 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001029}
1030
1031void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001032 if (fMCRec->fDeferredSaveCount > 0) {
1033 SkASSERT(fSaveCount > 1);
1034 fSaveCount -= 1;
1035 fMCRec->fDeferredSaveCount -= 1;
1036 } else {
1037 // check for underflow
1038 if (fMCStack.count() > 1) {
1039 this->willRestore();
1040 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001041 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001042 this->internalRestore();
1043 this->didRestore();
1044 }
reedf0090cb2014-11-26 08:55:51 -08001045 }
1046}
1047
1048void SkCanvas::restoreToCount(int count) {
1049 // sanity check
1050 if (count < 1) {
1051 count = 1;
1052 }
mtkleinf0f14112014-12-12 08:46:25 -08001053
reedf0090cb2014-11-26 08:55:51 -08001054 int n = this->getSaveCount() - count;
1055 for (int i = 0; i < n; ++i) {
1056 this->restore();
1057 }
1058}
1059
reed2ff1fce2014-12-11 07:07:37 -08001060void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001062 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001064
reed687fa1c2015-04-07 08:00:56 -07001065 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066}
1067
reed4960eee2015-12-18 07:09:18 -08001068bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed4960eee2015-12-18 07:09:18 -08001069 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070}
1071
reed4960eee2015-12-18 07:09:18 -08001072bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001073 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001074 SkIRect clipBounds;
1075 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001076 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001077 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001078
reed96e657d2015-03-10 17:30:07 -07001079 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1080
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001081 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001082 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001083 if (bounds && !imageFilter->canComputeFastBounds()) {
1084 bounds = nullptr;
1085 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001086 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001087 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001088 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001090
reed96e657d2015-03-10 17:30:07 -07001091 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 r.roundOut(&ir);
1093 // early exit if the layer's bounds are clipped out
1094 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001095 if (BoundsAffectsClip(saveLayerFlags)) {
reed1f836ee2014-07-07 07:49:34 -07001096 fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001097 fDeviceClipBounds.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001098 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001099 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 }
1101 } else { // no user bounds, so just use the clip
1102 ir = clipBounds;
1103 }
reed180aec42015-03-11 10:39:04 -07001104 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105
reed4960eee2015-12-18 07:09:18 -08001106 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001107 // Simplify the current clips since they will be applied properly during restore()
Mike Reedc1f77742016-12-09 09:00:50 -05001108 fClipStack->clipDevRect(ir, kReplace_SkClipOp);
reed180aec42015-03-11 10:39:04 -07001109 fMCRec->fRasterClip.setRect(ir);
msarettfbfa2582016-08-12 08:29:08 -07001110 fDeviceClipBounds = qr_clip_bounds(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001111 }
1112
1113 if (intersection) {
1114 *intersection = ir;
1115 }
1116 return true;
1117}
1118
reed4960eee2015-12-18 07:09:18 -08001119
reed4960eee2015-12-18 07:09:18 -08001120int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1121 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001122}
1123
reed70ee31b2015-12-10 13:44:45 -08001124int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001125 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1126}
1127
1128int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1129 SaveLayerRec rec(origRec);
1130 if (gIgnoreSaveLayerBounds) {
1131 rec.fBounds = nullptr;
1132 }
1133 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1134 fSaveCount += 1;
1135 this->internalSaveLayer(rec, strategy);
1136 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001137}
1138
reeda2217ef2016-07-20 06:04:34 -07001139void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1140 SkBaseDevice* dst, const SkMatrix& ctm,
1141 const SkClipStack* clipStack) {
1142 SkDraw draw;
1143 SkRasterClip rc;
1144 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1145 if (!dst->accessPixels(&draw.fDst)) {
1146 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001147 }
reeda2217ef2016-07-20 06:04:34 -07001148 draw.fMatrix = &SkMatrix::I();
1149 draw.fRC = &rc;
1150 draw.fClipStack = clipStack;
1151 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001152
1153 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001154 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001155
1156 int x = src->getOrigin().x() - dst->getOrigin().x();
1157 int y = src->getOrigin().y() - dst->getOrigin().y();
1158 auto special = src->snapSpecial();
1159 if (special) {
1160 dst->drawSpecial(draw, special.get(), x, y, p);
1161 }
robertphillips7354a4b2015-12-16 05:08:27 -08001162}
reed70ee31b2015-12-10 13:44:45 -08001163
reed129ed1c2016-02-22 06:42:31 -08001164static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1165 const SkPaint* paint) {
1166 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1167 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001168 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001169 const bool hasImageFilter = paint && paint->getImageFilter();
1170
1171 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1172 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1173 // force to L32
1174 return SkImageInfo::MakeN32(w, h, alphaType);
1175 } else {
1176 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001177 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001178 }
1179}
1180
reed4960eee2015-12-18 07:09:18 -08001181void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1182 const SkRect* bounds = rec.fBounds;
1183 const SkPaint* paint = rec.fPaint;
1184 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1185
reed8c30a812016-04-20 16:36:51 -07001186 SkLazyPaint lazyP;
1187 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1188 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001189 SkMatrix remainder;
1190 SkSize scale;
1191 /*
1192 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1193 * but they do handle scaling. To accommodate this, we do the following:
1194 *
1195 * 1. Stash off the current CTM
1196 * 2. Decompose the CTM into SCALE and REMAINDER
1197 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1198 * contains the REMAINDER
1199 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1200 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1201 * of the original imagefilter, and draw that (via drawSprite)
1202 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1203 *
1204 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1205 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1206 */
reed96a04f32016-04-25 09:25:15 -07001207 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001208 stashedMatrix.decomposeScale(&scale, &remainder))
1209 {
1210 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1211 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1212 SkPaint* p = lazyP.set(*paint);
1213 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1214 SkFilterQuality::kLow_SkFilterQuality,
1215 sk_ref_sp(imageFilter)));
1216 imageFilter = p->getImageFilter();
1217 paint = p;
1218 }
reed8c30a812016-04-20 16:36:51 -07001219
junov@chromium.orga907ac32012-02-24 21:54:07 +00001220 // do this before we create the layer. We don't call the public save() since
1221 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001222 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001223
1224 fDeviceCMDirty = true;
1225
1226 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001227 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001228 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 }
1230
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001231 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1232 // the clipRectBounds() call above?
1233 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001234 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001235 }
1236
reed4960eee2015-12-18 07:09:18 -08001237 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001238 SkPixelGeometry geo = fProps.pixelGeometry();
1239 if (paint) {
reed76033be2015-03-14 10:54:31 -07001240 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001241 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001242 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001243 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001244 }
1245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246
robertphillips5139e502016-07-19 05:10:40 -07001247 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001248 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001249 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001250 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001251 }
reedb2db8982014-11-13 12:41:02 -08001252
robertphillips5139e502016-07-19 05:10:40 -07001253 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001254 paint);
1255
Hal Canary704cd322016-11-07 14:13:52 -05001256 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001257 {
reed70ee31b2015-12-10 13:44:45 -08001258 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001259 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001260 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001261 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001262 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001263 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1264 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001265 return;
reed61f501f2015-04-29 08:34:00 -07001266 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001267 }
robertphillips5139e502016-07-19 05:10:40 -07001268 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001269
Hal Canary704cd322016-11-07 14:13:52 -05001270 DeviceCM* layer =
1271 new DeviceCM(newDevice.get(), paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001272
1273 layer->fNext = fMCRec->fTopLayer;
1274 fMCRec->fLayer = layer;
1275 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001276
1277 if (rec.fBackdrop) {
Hal Canary704cd322016-11-07 14:13:52 -05001278 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(),
reeda2217ef2016-07-20 06:04:34 -07001279 fMCRec->fMatrix, this->getClipStack());
1280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001281}
1282
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001283int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001284 if (0xFF == alpha) {
1285 return this->saveLayer(bounds, nullptr);
1286 } else {
1287 SkPaint tmpPaint;
1288 tmpPaint.setAlpha(alpha);
1289 return this->saveLayer(bounds, &tmpPaint);
1290 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001291}
1292
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293void SkCanvas::internalRestore() {
1294 SkASSERT(fMCStack.count() != 0);
1295
1296 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297
reed687fa1c2015-04-07 08:00:56 -07001298 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001299
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001300 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001301 DeviceCM* layer = fMCRec->fLayer; // may be null
1302 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001303 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304
1305 // now do the normal restore()
1306 fMCRec->~MCRec(); // balanced in save()
1307 fMCStack.pop_back();
1308 fMCRec = (MCRec*)fMCStack.back();
1309
1310 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1311 since if we're being recorded, we don't want to record this (the
1312 recorder will have already recorded the restore).
1313 */
bsalomon49f085d2014-09-05 13:34:00 -07001314 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001316 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001317 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001318 // restore what we smashed in internalSaveLayer
1319 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001320 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001321 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001322 delete layer;
reedb679ca82015-04-07 04:40:48 -07001323 } else {
1324 // we're at the root
reeda499f902015-05-01 09:34:31 -07001325 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001326 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001327 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001328 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001329 }
msarettfbfa2582016-08-12 08:29:08 -07001330
1331 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001332 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001333 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1334 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001335}
1336
reede8f30622016-03-23 18:59:25 -07001337sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001338 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001339 props = &fProps;
1340 }
1341 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001342}
1343
reede8f30622016-03-23 18:59:25 -07001344sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001345 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001346 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001347}
1348
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001349SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001350 return this->onImageInfo();
1351}
1352
1353SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001354 SkBaseDevice* dev = this->getDevice();
1355 if (dev) {
1356 return dev->imageInfo();
1357 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001358 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001359 }
1360}
1361
brianosman898235c2016-04-06 07:38:23 -07001362bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001363 return this->onGetProps(props);
1364}
1365
1366bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001367 SkBaseDevice* dev = this->getDevice();
1368 if (dev) {
1369 if (props) {
1370 *props = fProps;
1371 }
1372 return true;
1373 } else {
1374 return false;
1375 }
1376}
1377
reed6ceeebd2016-03-09 14:26:26 -08001378bool SkCanvas::peekPixels(SkPixmap* pmap) {
1379 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001380}
1381
reed884e97c2015-05-26 11:31:54 -07001382bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001383 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001384 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001385}
1386
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001387void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001388 SkPixmap pmap;
1389 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001390 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001391 }
1392 if (info) {
1393 *info = pmap.info();
1394 }
1395 if (rowBytes) {
1396 *rowBytes = pmap.rowBytes();
1397 }
1398 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001399 *origin = this->getTopDevice(false)->getOrigin();
1400 }
reed884e97c2015-05-26 11:31:54 -07001401 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001402}
1403
reed884e97c2015-05-26 11:31:54 -07001404bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001405 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001406 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001407}
1408
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410
reed7503d602016-07-15 14:23:29 -07001411void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001413 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 paint = &tmp;
1415 }
reed@google.com4b226022011-01-11 18:32:13 +00001416
reed@google.com8926b162012-03-23 15:36:36 +00001417 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001418
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001420 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001421 paint = &looper.paint();
1422 SkImageFilter* filter = paint->getImageFilter();
1423 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Robert Phillips833dcf42016-11-18 08:44:13 -05001424 if (filter) {
1425 sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
1426 if (specialImage) {
1427 dstDev->drawSpecial(iter, specialImage.get(), pos.x(), pos.y(), *paint);
1428 }
reed@google.com76dd2772012-01-05 21:15:07 +00001429 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001430 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001431 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 }
reeda2217ef2016-07-20 06:04:34 -07001433
reed@google.com4e2b3d32011-04-07 14:18:59 +00001434 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001435}
1436
reed32704672015-12-16 08:27:10 -08001437/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001438
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001439void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001440 if (dx || dy) {
1441 this->checkForDeferredSave();
1442 fDeviceCMDirty = true;
1443 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001444
reedfe69b502016-09-12 06:31:48 -07001445 // Translate shouldn't affect the is-scale-translateness of the matrix.
1446 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001447
reedfe69b502016-09-12 06:31:48 -07001448 this->didTranslate(dx,dy);
1449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450}
1451
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001452void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001453 SkMatrix m;
1454 m.setScale(sx, sy);
1455 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456}
1457
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001458void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001459 SkMatrix m;
1460 m.setRotate(degrees);
1461 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462}
1463
bungeman7438bfc2016-07-12 15:01:19 -07001464void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1465 SkMatrix m;
1466 m.setRotate(degrees, px, py);
1467 this->concat(m);
1468}
1469
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001470void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001471 SkMatrix m;
1472 m.setSkew(sx, sy);
1473 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001474}
1475
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001476void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001477 if (matrix.isIdentity()) {
1478 return;
1479 }
1480
reed2ff1fce2014-12-11 07:07:37 -08001481 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001483 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001484 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001485 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001486}
1487
reed8c30a812016-04-20 16:36:51 -07001488void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001490 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001491 fIsScaleTranslate = matrix.isScaleTranslate();
reed8c30a812016-04-20 16:36:51 -07001492}
1493
1494void SkCanvas::setMatrix(const SkMatrix& matrix) {
1495 this->checkForDeferredSave();
1496 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001497 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498}
1499
reed@android.com8a1c16f2008-12-17 15:59:43 +00001500void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001501 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001502}
1503
vjiaoblack95302da2016-07-21 10:25:54 -07001504#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001505void SkCanvas::translateZ(SkScalar z) {
1506 this->checkForDeferredSave();
1507 this->fMCRec->fCurDrawDepth += z;
1508 this->didTranslateZ(z);
1509}
1510
1511SkScalar SkCanvas::getZ() const {
1512 return this->fMCRec->fCurDrawDepth;
1513}
1514
vjiaoblack95302da2016-07-21 10:25:54 -07001515void SkCanvas::setLights(sk_sp<SkLights> lights) {
1516 this->fLights = lights;
1517}
1518
1519sk_sp<SkLights> SkCanvas::getLights() const {
1520 return this->fLights;
1521}
1522#endif
1523
reed@android.com8a1c16f2008-12-17 15:59:43 +00001524//////////////////////////////////////////////////////////////////////////////
1525
Mike Reedc1f77742016-12-09 09:00:50 -05001526void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001527 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001528 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1529 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001530}
1531
Mike Reedc1f77742016-12-09 09:00:50 -05001532void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001533 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reedc64eff52015-11-21 12:39:45 -08001534 AutoValidateClip avc(this);
Brian Salomona3b45d42016-10-03 11:36:16 -04001535 fClipStack->clipRect(rect, fMCRec->fMatrix, op, isAA);
1536 fMCRec->fRasterClip.op(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1537 isAA);
reedc64eff52015-11-21 12:39:45 -08001538 fDeviceCMDirty = true;
msarettfbfa2582016-08-12 08:29:08 -07001539 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001540}
1541
Mike Reedc1f77742016-12-09 09:00:50 -05001542void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001543 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001544 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001545 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001546 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1547 } else {
1548 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001549 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001550}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001551
Mike Reedc1f77742016-12-09 09:00:50 -05001552void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001553 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001554
Brian Salomona3b45d42016-10-03 11:36:16 -04001555 fDeviceCMDirty = true;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001556
Brian Salomona3b45d42016-10-03 11:36:16 -04001557 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1558 fClipStack->clipRRect(rrect, fMCRec->fMatrix, op, isAA);
1559 fMCRec->fRasterClip.op(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1560 isAA);
1561 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1562 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001563}
1564
Mike Reedc1f77742016-12-09 09:00:50 -05001565void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001566 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001567 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001568
1569 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1570 SkRect r;
1571 if (path.isRect(&r)) {
1572 this->onClipRect(r, op, edgeStyle);
1573 return;
1574 }
1575 SkRRect rrect;
1576 if (path.isOval(&r)) {
1577 rrect.setOval(r);
1578 this->onClipRRect(rrect, op, edgeStyle);
1579 return;
1580 }
1581 if (path.isRRect(&rrect)) {
1582 this->onClipRRect(rrect, op, edgeStyle);
1583 return;
1584 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001585 }
robertphillips39f05382015-11-24 09:30:12 -08001586
1587 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001588}
1589
Mike Reedc1f77742016-12-09 09:00:50 -05001590void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001591 AutoValidateClip avc(this);
1592
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593 fDeviceCMDirty = true;
Brian Salomona3b45d42016-10-03 11:36:16 -04001594 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001595
Brian Salomona3b45d42016-10-03 11:36:16 -04001596 fClipStack->clipPath(path, fMCRec->fMatrix, op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001597
Brian Salomona3b45d42016-10-03 11:36:16 -04001598 const SkPath* rasterClipPath = &path;
1599 const SkMatrix* matrix = &fMCRec->fMatrix;
1600 SkPath tempPath;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001601 if (fAllowSimplifyClip) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001602 isAA = getClipStack()->asPath(&tempPath);
1603 rasterClipPath = &tempPath;
1604 matrix = &SkMatrix::I();
Mike Reedc1f77742016-12-09 09:00:50 -05001605 op = kReplace_SkClipOp;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001606 }
Brian Salomona3b45d42016-10-03 11:36:16 -04001607 fMCRec->fRasterClip.op(*rasterClipPath, *matrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1608 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001609 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610}
1611
Mike Reedc1f77742016-12-09 09:00:50 -05001612void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001613 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001614 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001615}
1616
Mike Reedc1f77742016-12-09 09:00:50 -05001617void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001618 AutoValidateClip avc(this);
1619
reed@android.com8a1c16f2008-12-17 15:59:43 +00001620 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621
reed@google.com5c3d1472011-02-22 19:12:23 +00001622 // todo: signal fClipStack that we have a region, and therefore (I guess)
1623 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001624 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001625
reed73603f32016-09-20 08:42:38 -07001626 fMCRec->fRasterClip.op(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001627 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628}
1629
reed@google.com819c9212011-02-23 18:56:55 +00001630#ifdef SK_DEBUG
1631void SkCanvas::validateClip() const {
1632 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001633 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001634 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001635 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001636 return;
1637 }
1638
reed@google.com819c9212011-02-23 18:56:55 +00001639 SkIRect ir;
1640 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001641 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001642
reed687fa1c2015-04-07 08:00:56 -07001643 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001644 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001645 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001646 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001647 case SkClipStack::Element::kRect_Type:
1648 element->getRect().round(&ir);
reed73603f32016-09-20 08:42:38 -07001649 tmpClip.op(ir, (SkRegion::Op)element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001650 break;
1651 case SkClipStack::Element::kEmpty_Type:
1652 tmpClip.setEmpty();
1653 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001654 default: {
1655 SkPath path;
1656 element->asPath(&path);
Brian Salomona3b45d42016-10-03 11:36:16 -04001657 tmpClip.op(path, SkMatrix::I(), this->getTopLayerBounds(),
1658 (SkRegion::Op)element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001659 break;
1660 }
reed@google.com819c9212011-02-23 18:56:55 +00001661 }
1662 }
reed@google.com819c9212011-02-23 18:56:55 +00001663}
1664#endif
1665
reed@google.com90c07ea2012-04-13 13:50:27 +00001666void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001667 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001668 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001669
halcanary96fcdcc2015-08-27 07:41:13 -07001670 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001671 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001672 }
1673}
1674
reed@google.com5c3d1472011-02-22 19:12:23 +00001675///////////////////////////////////////////////////////////////////////////////
1676
reed@google.com754de5f2014-02-24 19:38:20 +00001677bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001678 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001679}
1680
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001681bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001682 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001683}
1684
msarettfbfa2582016-08-12 08:29:08 -07001685static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1686#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1687 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1688 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1689 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1690 return 0xF != _mm_movemask_ps(mask);
1691#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1692 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1693 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1694 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1695 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1696#else
1697 SkRect devRectAsRect;
1698 SkRect devClipAsRect;
1699 devRect.store(&devRectAsRect.fLeft);
1700 devClip.store(&devClipAsRect.fLeft);
1701 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1702#endif
1703}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001704
msarettfbfa2582016-08-12 08:29:08 -07001705// It's important for this function to not be inlined. Otherwise the compiler will share code
1706// between the fast path and the slow path, resulting in two slow paths.
1707static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1708 const SkMatrix& matrix) {
1709 SkRect deviceRect;
1710 matrix.mapRect(&deviceRect, src);
1711 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1712}
1713
1714bool SkCanvas::quickReject(const SkRect& src) const {
1715#ifdef SK_DEBUG
1716 // Verify that fDeviceClipBounds are set properly.
1717 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001718 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001719 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001720 } else {
msarettfbfa2582016-08-12 08:29:08 -07001721 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001722 }
msarettfbfa2582016-08-12 08:29:08 -07001723
msarett9637ea92016-08-18 14:03:30 -07001724 // Verify that fIsScaleTranslate is set properly.
1725 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001726#endif
1727
msarett9637ea92016-08-18 14:03:30 -07001728 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001729 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1730 }
1731
1732 // We inline the implementation of mapScaleTranslate() for the fast path.
1733 float sx = fMCRec->fMatrix.getScaleX();
1734 float sy = fMCRec->fMatrix.getScaleY();
1735 float tx = fMCRec->fMatrix.getTranslateX();
1736 float ty = fMCRec->fMatrix.getTranslateY();
1737 Sk4f scale(sx, sy, sx, sy);
1738 Sk4f trans(tx, ty, tx, ty);
1739
1740 // Apply matrix.
1741 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1742
1743 // Make sure left < right, top < bottom.
1744 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1745 Sk4f min = Sk4f::Min(ltrb, rblt);
1746 Sk4f max = Sk4f::Max(ltrb, rblt);
1747 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1748 // ARM this sequence generates the fastest (a single instruction).
1749 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1750
1751 // Check if the device rect is NaN or outside the clip.
1752 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753}
1754
reed@google.com3b3e8952012-08-16 20:53:31 +00001755bool SkCanvas::quickReject(const SkPath& path) const {
1756 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001757}
1758
reed@google.com3b3e8952012-08-16 20:53:31 +00001759bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001760 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001761 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001762 return false;
1763 }
1764
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001765 SkMatrix inverse;
1766 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001767 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001768 if (bounds) {
1769 bounds->setEmpty();
1770 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001771 return false;
1772 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001773
bsalomon49f085d2014-09-05 13:34:00 -07001774 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001775 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001776 // adjust it outwards in case we are antialiasing
1777 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001778
reed@google.com8f4d2302013-12-17 16:44:46 +00001779 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1780 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001781 inverse.mapRect(bounds, r);
1782 }
1783 return true;
1784}
1785
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001786bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001787 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001788 if (clip.isEmpty()) {
1789 if (bounds) {
1790 bounds->setEmpty();
1791 }
1792 return false;
1793 }
1794
bsalomon49f085d2014-09-05 13:34:00 -07001795 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001796 *bounds = clip.getBounds();
1797 }
1798 return true;
1799}
1800
reed@android.com8a1c16f2008-12-17 15:59:43 +00001801const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001802 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001803}
1804
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001805const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001806 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001807}
1808
Brian Osman11052242016-10-27 14:47:55 -04001809GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001810 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001811 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001812}
1813
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001814GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001815 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001816 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001817}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001818
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001819void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1820 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001821 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001822 if (outer.isEmpty()) {
1823 return;
1824 }
1825 if (inner.isEmpty()) {
1826 this->drawRRect(outer, paint);
1827 return;
1828 }
1829
1830 // We don't have this method (yet), but technically this is what we should
1831 // be able to assert...
1832 // SkASSERT(outer.contains(inner));
1833 //
1834 // For now at least check for containment of bounds
1835 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1836
1837 this->onDrawDRRect(outer, inner, paint);
1838}
1839
reed41af9662015-01-05 07:49:08 -08001840// These need to stop being virtual -- clients need to override the onDraw... versions
1841
1842void SkCanvas::drawPaint(const SkPaint& paint) {
1843 this->onDrawPaint(paint);
1844}
1845
1846void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1847 this->onDrawRect(r, paint);
1848}
1849
msarettdca352e2016-08-26 06:37:45 -07001850void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
1851 if (region.isEmpty()) {
1852 return;
1853 }
1854
1855 if (region.isRect()) {
1856 return this->drawIRect(region.getBounds(), paint);
1857 }
1858
1859 this->onDrawRegion(region, paint);
1860}
1861
reed41af9662015-01-05 07:49:08 -08001862void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1863 this->onDrawOval(r, paint);
1864}
1865
1866void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1867 this->onDrawRRect(rrect, paint);
1868}
1869
1870void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1871 this->onDrawPoints(mode, count, pts, paint);
1872}
1873
1874void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001875 const SkPoint texs[], const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08001876 const uint16_t indices[], int indexCount, const SkPaint& paint) {
Mike Reedfaba3712016-11-03 14:45:31 -04001877 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, bmode,
reed41af9662015-01-05 07:49:08 -08001878 indices, indexCount, paint);
1879}
1880
1881void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1882 this->onDrawPath(path, paint);
1883}
1884
reeda85d4d02015-05-06 12:56:48 -07001885void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001886 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001887 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001888}
1889
reede47829b2015-08-06 10:02:53 -07001890void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1891 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001892 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001893 if (dst.isEmpty() || src.isEmpty()) {
1894 return;
1895 }
1896 this->onDrawImageRect(image, &src, dst, paint, constraint);
1897}
reed41af9662015-01-05 07:49:08 -08001898
reed84984ef2015-07-17 07:09:43 -07001899void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1900 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001901 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001902 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001903}
1904
reede47829b2015-08-06 10:02:53 -07001905void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1906 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001907 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001908 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1909 constraint);
1910}
reede47829b2015-08-06 10:02:53 -07001911
reed4c21dc52015-06-25 12:32:03 -07001912void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1913 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001914 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001915 if (dst.isEmpty()) {
1916 return;
1917 }
msarett552bca92016-08-03 06:53:26 -07001918 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1919 this->onDrawImageNine(image, center, dst, paint);
1920 } else {
reede47829b2015-08-06 10:02:53 -07001921 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001922 }
reed4c21dc52015-06-25 12:32:03 -07001923}
1924
msarett16882062016-08-16 09:31:08 -07001925void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1926 const SkPaint* paint) {
1927 RETURN_ON_NULL(image);
1928 if (dst.isEmpty()) {
1929 return;
1930 }
msarett71df2d72016-09-30 12:41:42 -07001931
1932 SkIRect bounds;
1933 Lattice latticePlusBounds = lattice;
1934 if (!latticePlusBounds.fBounds) {
1935 bounds = SkIRect::MakeWH(image->width(), image->height());
1936 latticePlusBounds.fBounds = &bounds;
1937 }
1938
1939 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
1940 this->onDrawImageLattice(image, latticePlusBounds, dst, paint);
msarett16882062016-08-16 09:31:08 -07001941 } else {
1942 this->drawImageRect(image, dst, paint);
1943 }
1944}
1945
reed41af9662015-01-05 07:49:08 -08001946void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001947 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001948 return;
1949 }
reed41af9662015-01-05 07:49:08 -08001950 this->onDrawBitmap(bitmap, dx, dy, paint);
1951}
1952
reede47829b2015-08-06 10:02:53 -07001953void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001954 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001955 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001956 return;
1957 }
reede47829b2015-08-06 10:02:53 -07001958 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001959}
1960
reed84984ef2015-07-17 07:09:43 -07001961void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1962 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001963 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001964}
1965
reede47829b2015-08-06 10:02:53 -07001966void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1967 SrcRectConstraint constraint) {
1968 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1969 constraint);
1970}
reede47829b2015-08-06 10:02:53 -07001971
reed41af9662015-01-05 07:49:08 -08001972void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1973 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001974 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001975 return;
1976 }
msarett552bca92016-08-03 06:53:26 -07001977 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
1978 this->onDrawBitmapNine(bitmap, center, dst, paint);
1979 } else {
reeda5517e22015-07-14 10:54:12 -07001980 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001981 }
reed41af9662015-01-05 07:49:08 -08001982}
1983
msarettc573a402016-08-02 08:05:56 -07001984void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
1985 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07001986 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07001987 return;
1988 }
msarett71df2d72016-09-30 12:41:42 -07001989
1990 SkIRect bounds;
1991 Lattice latticePlusBounds = lattice;
1992 if (!latticePlusBounds.fBounds) {
1993 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1994 latticePlusBounds.fBounds = &bounds;
1995 }
1996
1997 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
1998 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, paint);
msarett552bca92016-08-03 06:53:26 -07001999 } else {
msarett16882062016-08-16 09:31:08 -07002000 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002001 }
msarettc573a402016-08-02 08:05:56 -07002002}
2003
reed71c3c762015-06-24 10:29:17 -07002004void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002005 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002006 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002007 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002008 if (count <= 0) {
2009 return;
2010 }
2011 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002012 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002013 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002014}
2015
reedf70b5312016-03-04 16:36:20 -08002016void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2017 if (key) {
2018 this->onDrawAnnotation(rect, key, value);
2019 }
2020}
2021
reede47829b2015-08-06 10:02:53 -07002022void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2023 const SkPaint* paint, SrcRectConstraint constraint) {
2024 if (src) {
2025 this->drawImageRect(image, *src, dst, paint, constraint);
2026 } else {
2027 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2028 dst, paint, constraint);
2029 }
2030}
2031void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2032 const SkPaint* paint, SrcRectConstraint constraint) {
2033 if (src) {
2034 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2035 } else {
2036 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2037 dst, paint, constraint);
2038 }
2039}
2040
tomhudsoncb3bd182016-05-18 07:24:16 -07002041void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2042 SkIRect layer_bounds = this->getTopLayerBounds();
2043 if (matrix) {
2044 *matrix = this->getTotalMatrix();
2045 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2046 }
2047 if (clip_bounds) {
2048 this->getClipDeviceBounds(clip_bounds);
2049 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2050 }
2051}
2052
reed@android.com8a1c16f2008-12-17 15:59:43 +00002053//////////////////////////////////////////////////////////////////////////////
2054// These are the virtual drawing methods
2055//////////////////////////////////////////////////////////////////////////////
2056
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002057void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002058 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002059 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2060 }
2061}
2062
reed41af9662015-01-05 07:49:08 -08002063void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002064 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002065 this->internalDrawPaint(paint);
2066}
2067
2068void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002069 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002070
2071 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002072 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002073 }
2074
reed@google.com4e2b3d32011-04-07 14:18:59 +00002075 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002076}
2077
reed41af9662015-01-05 07:49:08 -08002078void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2079 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002080 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081 if ((long)count <= 0) {
2082 return;
2083 }
2084
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002085 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002086 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002087 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002088 // special-case 2 points (common for drawing a single line)
2089 if (2 == count) {
2090 r.set(pts[0], pts[1]);
2091 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002092 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002093 }
senorblanco87e066e2015-10-28 11:23:36 -07002094 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2095 return;
2096 }
2097 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002098 }
reed@google.coma584aed2012-05-16 14:06:02 +00002099
halcanary96fcdcc2015-08-27 07:41:13 -07002100 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002101
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002102 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002103
reed@android.com8a1c16f2008-12-17 15:59:43 +00002104 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002105 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002106 }
reed@google.com4b226022011-01-11 18:32:13 +00002107
reed@google.com4e2b3d32011-04-07 14:18:59 +00002108 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109}
2110
reed4a167172016-08-18 17:15:25 -07002111static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
2112 return ((intptr_t)paint.getImageFilter() |
2113#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
2114 (intptr_t)canvas->getDrawFilter() |
2115#endif
2116 (intptr_t)paint.getLooper() ) != 0;
2117}
2118
reed41af9662015-01-05 07:49:08 -08002119void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002120 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002121 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002122 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002123 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002124 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2125 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2126 SkRect tmp(r);
2127 tmp.sort();
2128
senorblanco87e066e2015-10-28 11:23:36 -07002129 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2130 return;
2131 }
2132 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002133 }
reed@google.com4b226022011-01-11 18:32:13 +00002134
reed4a167172016-08-18 17:15:25 -07002135 if (needs_autodrawlooper(this, paint)) {
2136 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002137
reed4a167172016-08-18 17:15:25 -07002138 while (iter.next()) {
2139 iter.fDevice->drawRect(iter, r, looper.paint());
2140 }
2141
2142 LOOPER_END
2143 } else {
2144 this->predrawNotify(bounds, &paint, false);
2145 SkDrawIter iter(this);
2146 while (iter.next()) {
2147 iter.fDevice->drawRect(iter, r, paint);
2148 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002150}
2151
msarett44df6512016-08-25 13:54:30 -07002152void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
2153 SkRect storage;
2154 SkRect regionRect = SkRect::Make(region.getBounds());
2155 const SkRect* bounds = nullptr;
2156 if (paint.canComputeFastBounds()) {
2157 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2158 return;
2159 }
2160 bounds = &regionRect;
2161 }
2162
2163 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
2164
2165 while (iter.next()) {
2166 iter.fDevice->drawRegion(iter, region, looper.paint());
2167 }
2168
2169 LOOPER_END
2170}
2171
reed41af9662015-01-05 07:49:08 -08002172void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002173 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002174 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002175 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002176 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002177 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2178 return;
2179 }
2180 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002181 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002182
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002183 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002184
2185 while (iter.next()) {
2186 iter.fDevice->drawOval(iter, oval, looper.paint());
2187 }
2188
2189 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002190}
2191
bsalomonac3aa242016-08-19 11:25:19 -07002192void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2193 SkScalar sweepAngle, bool useCenter,
2194 const SkPaint& paint) {
2195 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
2196 const SkRect* bounds = nullptr;
2197 if (paint.canComputeFastBounds()) {
2198 SkRect storage;
2199 // Note we're using the entire oval as the bounds.
2200 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2201 return;
2202 }
2203 bounds = &oval;
2204 }
2205
2206 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
2207
2208 while (iter.next()) {
2209 iter.fDevice->drawArc(iter, oval, startAngle, sweepAngle, useCenter, looper.paint());
2210 }
2211
2212 LOOPER_END
2213}
2214
reed41af9662015-01-05 07:49:08 -08002215void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002216 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002217 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002218 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002219 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002220 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2221 return;
2222 }
2223 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002224 }
2225
2226 if (rrect.isRect()) {
2227 // call the non-virtual version
2228 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002229 return;
2230 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002231 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002232 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2233 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002234 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002235
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002236 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002237
2238 while (iter.next()) {
2239 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2240 }
2241
2242 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002243}
2244
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002245void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2246 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002247 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002248 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002249 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002250 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2251 return;
2252 }
2253 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002254 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002255
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002256 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002257
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002258 while (iter.next()) {
2259 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2260 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002261
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002262 LOOPER_END
2263}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002264
reed41af9662015-01-05 07:49:08 -08002265void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002266 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002267 if (!path.isFinite()) {
2268 return;
2269 }
2270
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002271 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002272 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002273 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002274 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002275 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2276 return;
2277 }
2278 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002280
2281 const SkRect& r = path.getBounds();
2282 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002283 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002284 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002285 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002286 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002287 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002288
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002289 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290
2291 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002292 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002293 }
2294
reed@google.com4e2b3d32011-04-07 14:18:59 +00002295 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002296}
2297
reed262a71b2015-12-05 13:07:27 -08002298bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002299 if (!paint.getImageFilter()) {
2300 return false;
2301 }
2302
2303 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002304 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002305 return false;
2306 }
2307
2308 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2309 // Once we can filter and the filter will return a result larger than itself, we should be
2310 // able to remove this constraint.
2311 // skbug.com/4526
2312 //
2313 SkPoint pt;
2314 ctm.mapXY(x, y, &pt);
2315 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2316 return ir.contains(fMCRec->fRasterClip.getBounds());
2317}
2318
reeda85d4d02015-05-06 12:56:48 -07002319void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002320 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002321 SkRect bounds = SkRect::MakeXYWH(x, y,
2322 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002323 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002324 SkRect tmp = bounds;
2325 if (paint) {
2326 paint->computeFastBounds(tmp, &tmp);
2327 }
2328 if (this->quickReject(tmp)) {
2329 return;
2330 }
reeda85d4d02015-05-06 12:56:48 -07002331 }
halcanary9d524f22016-03-29 09:03:52 -07002332
reeda85d4d02015-05-06 12:56:48 -07002333 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002334 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002335 paint = lazy.init();
2336 }
reed262a71b2015-12-05 13:07:27 -08002337
reeda2217ef2016-07-20 06:04:34 -07002338 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002339 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2340 *paint);
2341 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002342 special = this->getDevice()->makeSpecial(image);
2343 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002344 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002345 }
2346 }
2347
reed262a71b2015-12-05 13:07:27 -08002348 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2349
reeda85d4d02015-05-06 12:56:48 -07002350 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002351 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002352 if (special) {
2353 SkPoint pt;
2354 iter.fMatrix->mapXY(x, y, &pt);
2355 iter.fDevice->drawSpecial(iter, special.get(),
2356 SkScalarRoundToInt(pt.fX),
2357 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002358 } else {
2359 iter.fDevice->drawImage(iter, image, x, y, pnt);
2360 }
reeda85d4d02015-05-06 12:56:48 -07002361 }
halcanary9d524f22016-03-29 09:03:52 -07002362
reeda85d4d02015-05-06 12:56:48 -07002363 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002364}
2365
reed41af9662015-01-05 07:49:08 -08002366void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002367 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002368 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002369 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002370 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002371 if (paint) {
2372 paint->computeFastBounds(dst, &storage);
2373 }
2374 if (this->quickReject(storage)) {
2375 return;
2376 }
reeda85d4d02015-05-06 12:56:48 -07002377 }
2378 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002379 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002380 paint = lazy.init();
2381 }
halcanary9d524f22016-03-29 09:03:52 -07002382
senorblancoc41e7e12015-12-07 12:51:30 -08002383 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002384 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002385
reeda85d4d02015-05-06 12:56:48 -07002386 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002387 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002388 }
halcanary9d524f22016-03-29 09:03:52 -07002389
reeda85d4d02015-05-06 12:56:48 -07002390 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002391}
2392
reed41af9662015-01-05 07:49:08 -08002393void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002394 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002395 SkDEBUGCODE(bitmap.validate();)
2396
reed33366972015-10-08 09:22:02 -07002397 if (bitmap.drawsNothing()) {
2398 return;
2399 }
2400
2401 SkLazyPaint lazy;
2402 if (nullptr == paint) {
2403 paint = lazy.init();
2404 }
2405
2406 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2407
2408 SkRect storage;
2409 const SkRect* bounds = nullptr;
2410 if (paint->canComputeFastBounds()) {
2411 bitmap.getBounds(&storage);
2412 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002413 SkRect tmp = storage;
2414 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2415 return;
2416 }
2417 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002418 }
reed@google.com4b226022011-01-11 18:32:13 +00002419
reeda2217ef2016-07-20 06:04:34 -07002420 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002421 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2422 *paint);
2423 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002424 special = this->getDevice()->makeSpecial(bitmap);
2425 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002426 drawAsSprite = false;
2427 }
2428 }
2429
reed262a71b2015-12-05 13:07:27 -08002430 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002431
2432 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002433 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002434 if (special) {
reed262a71b2015-12-05 13:07:27 -08002435 SkPoint pt;
2436 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002437 iter.fDevice->drawSpecial(iter, special.get(),
2438 SkScalarRoundToInt(pt.fX),
2439 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002440 } else {
2441 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2442 }
reed33366972015-10-08 09:22:02 -07002443 }
msarettfbfa2582016-08-12 08:29:08 -07002444
reed33366972015-10-08 09:22:02 -07002445 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002446}
2447
reed@google.com9987ec32011-09-07 11:57:52 +00002448// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002449void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002450 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002451 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002452 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002453 return;
2454 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002455
halcanary96fcdcc2015-08-27 07:41:13 -07002456 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002457 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002458 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2459 return;
2460 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002461 }
reed@google.com3d608122011-11-21 15:16:16 +00002462
reed@google.com33535f32012-09-25 15:37:50 +00002463 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002464 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002465 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002466 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002467
senorblancoc41e7e12015-12-07 12:51:30 -08002468 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002469 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002470
reed@google.com33535f32012-09-25 15:37:50 +00002471 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002472 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002473 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002474
reed@google.com33535f32012-09-25 15:37:50 +00002475 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002476}
2477
reed41af9662015-01-05 07:49:08 -08002478void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002479 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002480 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002481 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002482 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002483}
2484
reed4c21dc52015-06-25 12:32:03 -07002485void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2486 const SkPaint* paint) {
2487 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002488
halcanary96fcdcc2015-08-27 07:41:13 -07002489 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002490 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002491 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2492 return;
2493 }
reed@google.com3d608122011-11-21 15:16:16 +00002494 }
halcanary9d524f22016-03-29 09:03:52 -07002495
reed4c21dc52015-06-25 12:32:03 -07002496 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002497 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002498 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002499 }
halcanary9d524f22016-03-29 09:03:52 -07002500
senorblancoc41e7e12015-12-07 12:51:30 -08002501 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002502
reed4c21dc52015-06-25 12:32:03 -07002503 while (iter.next()) {
2504 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002505 }
halcanary9d524f22016-03-29 09:03:52 -07002506
reed4c21dc52015-06-25 12:32:03 -07002507 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002508}
2509
reed41af9662015-01-05 07:49:08 -08002510void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2511 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002512 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002513 SkDEBUGCODE(bitmap.validate();)
2514
halcanary96fcdcc2015-08-27 07:41:13 -07002515 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002516 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002517 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2518 return;
2519 }
reed4c21dc52015-06-25 12:32:03 -07002520 }
halcanary9d524f22016-03-29 09:03:52 -07002521
reed4c21dc52015-06-25 12:32:03 -07002522 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002523 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002524 paint = lazy.init();
2525 }
halcanary9d524f22016-03-29 09:03:52 -07002526
senorblancoc41e7e12015-12-07 12:51:30 -08002527 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002528
reed4c21dc52015-06-25 12:32:03 -07002529 while (iter.next()) {
2530 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2531 }
halcanary9d524f22016-03-29 09:03:52 -07002532
reed4c21dc52015-06-25 12:32:03 -07002533 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002534}
2535
msarett16882062016-08-16 09:31:08 -07002536void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2537 const SkPaint* paint) {
2538 if (nullptr == paint || paint->canComputeFastBounds()) {
2539 SkRect storage;
2540 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2541 return;
2542 }
2543 }
2544
2545 SkLazyPaint lazy;
2546 if (nullptr == paint) {
2547 paint = lazy.init();
2548 }
2549
2550 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2551
2552 while (iter.next()) {
2553 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2554 }
2555
2556 LOOPER_END
2557}
2558
2559void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2560 const SkRect& dst, const SkPaint* paint) {
2561 if (nullptr == paint || paint->canComputeFastBounds()) {
2562 SkRect storage;
2563 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2564 return;
2565 }
2566 }
2567
2568 SkLazyPaint lazy;
2569 if (nullptr == paint) {
2570 paint = lazy.init();
2571 }
2572
2573 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2574
2575 while (iter.next()) {
2576 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2577 }
2578
2579 LOOPER_END
2580}
2581
reed@google.comf67e4cf2011-03-15 20:56:58 +00002582class SkDeviceFilteredPaint {
2583public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002584 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002585 uint32_t filteredFlags = device->filterTextFlags(paint);
2586 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002587 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002588 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002589 fPaint = newPaint;
2590 } else {
2591 fPaint = &paint;
2592 }
2593 }
2594
reed@google.comf67e4cf2011-03-15 20:56:58 +00002595 const SkPaint& paint() const { return *fPaint; }
2596
2597private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002598 const SkPaint* fPaint;
2599 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002600};
2601
bungeman@google.com52c748b2011-08-22 21:30:43 +00002602void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2603 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002604 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002605 draw.fDevice->drawRect(draw, r, paint);
2606 } else {
2607 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002608 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002609 draw.fDevice->drawRect(draw, r, p);
2610 }
2611}
2612
2613void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2614 const char text[], size_t byteLength,
2615 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002616 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002617
2618 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002619 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002620 draw.fRC->isEmpty() ||
reed374772b2016-10-05 17:33:02 -07002621 (paint.getAlpha() == 0 && paint.isSrcOver())) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002622 return;
2623 }
2624
2625 SkScalar width = 0;
2626 SkPoint start;
2627
2628 start.set(0, 0); // to avoid warning
2629 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2630 SkPaint::kStrikeThruText_Flag)) {
2631 width = paint.measureText(text, byteLength);
2632
2633 SkScalar offsetX = 0;
2634 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2635 offsetX = SkScalarHalf(width);
2636 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2637 offsetX = width;
2638 }
2639 start.set(x - offsetX, y);
2640 }
2641
2642 if (0 == width) {
2643 return;
2644 }
2645
2646 uint32_t flags = paint.getFlags();
2647
2648 if (flags & (SkPaint::kUnderlineText_Flag |
2649 SkPaint::kStrikeThruText_Flag)) {
2650 SkScalar textSize = paint.getTextSize();
2651 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2652 SkRect r;
2653
2654 r.fLeft = start.fX;
2655 r.fRight = start.fX + width;
2656
2657 if (flags & SkPaint::kUnderlineText_Flag) {
2658 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2659 start.fY);
2660 r.fTop = offset;
2661 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002662 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002663 }
2664 if (flags & SkPaint::kStrikeThruText_Flag) {
2665 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2666 start.fY);
2667 r.fTop = offset;
2668 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002669 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002670 }
2671 }
2672}
2673
reed@google.come0d9ce82014-04-23 04:00:17 +00002674void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2675 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002676 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002677
2678 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002679 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002680 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002681 DrawTextDecorations(iter, dfp.paint(),
2682 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002683 }
2684
reed@google.com4e2b3d32011-04-07 14:18:59 +00002685 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002686}
2687
reed@google.come0d9ce82014-04-23 04:00:17 +00002688void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2689 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002690 SkPoint textOffset = SkPoint::Make(0, 0);
2691
halcanary96fcdcc2015-08-27 07:41:13 -07002692 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002693
reed@android.com8a1c16f2008-12-17 15:59:43 +00002694 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002695 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002696 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002697 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002698 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002699
reed@google.com4e2b3d32011-04-07 14:18:59 +00002700 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002701}
2702
reed@google.come0d9ce82014-04-23 04:00:17 +00002703void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2704 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002705
2706 SkPoint textOffset = SkPoint::Make(0, constY);
2707
halcanary96fcdcc2015-08-27 07:41:13 -07002708 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002709
reed@android.com8a1c16f2008-12-17 15:59:43 +00002710 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002711 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002712 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002713 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002714 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002715
reed@google.com4e2b3d32011-04-07 14:18:59 +00002716 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002717}
2718
reed@google.come0d9ce82014-04-23 04:00:17 +00002719void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2720 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002721 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002722
reed@android.com8a1c16f2008-12-17 15:59:43 +00002723 while (iter.next()) {
2724 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002725 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002726 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002727
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002728 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002729}
2730
reed45561a02016-07-07 12:47:17 -07002731void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2732 const SkRect* cullRect, const SkPaint& paint) {
2733 if (cullRect && this->quickReject(*cullRect)) {
2734 return;
2735 }
2736
2737 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2738
2739 while (iter.next()) {
2740 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2741 }
2742
2743 LOOPER_END
2744}
2745
fmalita00d5c2c2014-08-21 08:53:26 -07002746void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2747 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002748
fmalita85d5eb92015-03-04 11:20:12 -08002749 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002750 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002751 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002752 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002753 SkRect tmp;
2754 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2755 return;
2756 }
2757 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002758 }
2759
fmalita024f9962015-03-03 19:08:17 -08002760 // We cannot filter in the looper as we normally do, because the paint is
2761 // incomplete at this point (text-related attributes are embedded within blob run paints).
2762 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002763 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002764
fmalita85d5eb92015-03-04 11:20:12 -08002765 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002766
fmalitaaa1b9122014-08-28 14:32:24 -07002767 while (iter.next()) {
2768 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002769 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002770 }
2771
fmalitaaa1b9122014-08-28 14:32:24 -07002772 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002773
2774 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002775}
2776
reed@google.come0d9ce82014-04-23 04:00:17 +00002777// These will become non-virtual, so they always call the (virtual) onDraw... method
2778void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2779 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002780 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002781 if (byteLength) {
2782 this->onDrawText(text, byteLength, x, y, paint);
2783 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002784}
2785void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2786 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002787 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002788 if (byteLength) {
2789 this->onDrawPosText(text, byteLength, pos, paint);
2790 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002791}
2792void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2793 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002794 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002795 if (byteLength) {
2796 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2797 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002798}
2799void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2800 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002801 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002802 if (byteLength) {
2803 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2804 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002805}
reed45561a02016-07-07 12:47:17 -07002806void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2807 const SkRect* cullRect, const SkPaint& paint) {
2808 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2809 if (byteLength) {
2810 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2811 }
2812}
fmalita00d5c2c2014-08-21 08:53:26 -07002813void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2814 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002815 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002816 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002817 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002818}
reed@google.come0d9ce82014-04-23 04:00:17 +00002819
reed41af9662015-01-05 07:49:08 -08002820void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2821 const SkPoint verts[], const SkPoint texs[],
Mike Reedfaba3712016-11-03 14:45:31 -04002822 const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08002823 const uint16_t indices[], int indexCount,
2824 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002825 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002826 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002827
reed@android.com8a1c16f2008-12-17 15:59:43 +00002828 while (iter.next()) {
2829 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
Mike Reedfaba3712016-11-03 14:45:31 -04002830 colors, bmode, indices, indexCount,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002831 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002832 }
reed@google.com4b226022011-01-11 18:32:13 +00002833
reed@google.com4e2b3d32011-04-07 14:18:59 +00002834 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002835}
2836
dandovb3c9d1c2014-08-12 08:34:29 -07002837void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002838 const SkPoint texCoords[4], SkBlendMode bmode,
2839 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002840 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002841 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002842 return;
2843 }
mtklein6cfa73a2014-08-13 13:33:49 -07002844
Mike Reedfaba3712016-11-03 14:45:31 -04002845 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002846}
2847
2848void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002849 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002850 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002851 // Since a patch is always within the convex hull of the control points, we discard it when its
2852 // bounding rectangle is completely outside the current clip.
2853 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002854 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002855 if (this->quickReject(bounds)) {
2856 return;
2857 }
mtklein6cfa73a2014-08-13 13:33:49 -07002858
halcanary96fcdcc2015-08-27 07:41:13 -07002859 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002860
dandovecfff212014-08-04 10:02:00 -07002861 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002862 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002863 }
mtklein6cfa73a2014-08-13 13:33:49 -07002864
dandovecfff212014-08-04 10:02:00 -07002865 LOOPER_END
2866}
2867
reeda8db7282015-07-07 10:22:31 -07002868void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002869 RETURN_ON_NULL(dr);
2870 if (x || y) {
2871 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2872 this->onDrawDrawable(dr, &matrix);
2873 } else {
2874 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002875 }
2876}
2877
reeda8db7282015-07-07 10:22:31 -07002878void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002879 RETURN_ON_NULL(dr);
2880 if (matrix && matrix->isIdentity()) {
2881 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002882 }
reede3b38ce2016-01-08 09:18:44 -08002883 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002884}
2885
2886void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002887 // drawable bounds are no longer reliable (e.g. android displaylist)
2888 // so don't use them for quick-reject
reeda8db7282015-07-07 10:22:31 -07002889 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002890}
2891
reed71c3c762015-06-24 10:29:17 -07002892void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002893 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002894 const SkRect* cull, const SkPaint* paint) {
2895 if (cull && this->quickReject(*cull)) {
2896 return;
2897 }
2898
2899 SkPaint pnt;
2900 if (paint) {
2901 pnt = *paint;
2902 }
halcanary9d524f22016-03-29 09:03:52 -07002903
halcanary96fcdcc2015-08-27 07:41:13 -07002904 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002905 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002906 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002907 }
2908 LOOPER_END
2909}
2910
reedf70b5312016-03-04 16:36:20 -08002911void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2912 SkASSERT(key);
2913
2914 SkPaint paint;
2915 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2916 while (iter.next()) {
2917 iter.fDevice->drawAnnotation(iter, rect, key, value);
2918 }
2919 LOOPER_END
2920}
2921
reed@android.com8a1c16f2008-12-17 15:59:43 +00002922//////////////////////////////////////////////////////////////////////////////
2923// These methods are NOT virtual, and therefore must call back into virtual
2924// methods, rather than actually drawing themselves.
2925//////////////////////////////////////////////////////////////////////////////
2926
reed374772b2016-10-05 17:33:02 -07002927void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002928 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002929 SkPaint paint;
2930
2931 paint.setARGB(a, r, g, b);
reed374772b2016-10-05 17:33:02 -07002932 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002933 this->drawPaint(paint);
2934}
2935
reed374772b2016-10-05 17:33:02 -07002936void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002937 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002938 SkPaint paint;
2939
2940 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002941 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002942 this->drawPaint(paint);
2943}
2944
2945void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002946 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002947 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002948
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949 pt.set(x, y);
2950 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2951}
2952
2953void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002954 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002955 SkPoint pt;
2956 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002957
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958 pt.set(x, y);
2959 paint.setColor(color);
2960 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2961}
2962
2963void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2964 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002965 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002966 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002967
reed@android.com8a1c16f2008-12-17 15:59:43 +00002968 pts[0].set(x0, y0);
2969 pts[1].set(x1, y1);
2970 this->drawPoints(kLines_PointMode, 2, pts, paint);
2971}
2972
2973void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2974 SkScalar right, SkScalar bottom,
2975 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002976 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002977 SkRect r;
2978
2979 r.set(left, top, right, bottom);
2980 this->drawRect(r, paint);
2981}
2982
2983void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2984 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002985 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002986 if (radius < 0) {
2987 radius = 0;
2988 }
2989
2990 SkRect r;
2991 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002992 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002993}
2994
2995void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2996 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002997 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002998 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002999 SkRRect rrect;
3000 rrect.setRectXY(r, rx, ry);
3001 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003002 } else {
3003 this->drawRect(r, paint);
3004 }
3005}
3006
reed@android.com8a1c16f2008-12-17 15:59:43 +00003007void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3008 SkScalar sweepAngle, bool useCenter,
3009 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003010 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
bsalomon21af9ca2016-08-25 12:29:23 -07003011 if (oval.isEmpty() || !sweepAngle) {
3012 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003013 }
bsalomon21af9ca2016-08-25 12:29:23 -07003014 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003015}
3016
3017void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3018 const SkPath& path, SkScalar hOffset,
3019 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003020 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003021 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003022
reed@android.com8a1c16f2008-12-17 15:59:43 +00003023 matrix.setTranslate(hOffset, vOffset);
3024 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3025}
3026
reed@android.comf76bacf2009-05-13 14:00:33 +00003027///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003028
3029/**
3030 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3031 * against the playback cost of recursing into the subpicture to get at its actual ops.
3032 *
3033 * For now we pick a conservatively small value, though measurement (and other heuristics like
3034 * the type of ops contained) may justify changing this value.
3035 */
3036#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003037
reedd5fa1a42014-08-09 11:08:05 -07003038void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003039 RETURN_ON_NULL(picture);
3040
reed1c2c4412015-04-30 13:09:24 -07003041 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003042 if (matrix && matrix->isIdentity()) {
3043 matrix = nullptr;
3044 }
3045 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3046 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3047 picture->playback(this);
3048 } else {
3049 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003050 }
3051}
robertphillips9b14f262014-06-04 05:40:44 -07003052
reedd5fa1a42014-08-09 11:08:05 -07003053void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3054 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003055 if (!paint || paint->canComputeFastBounds()) {
3056 SkRect bounds = picture->cullRect();
3057 if (paint) {
3058 paint->computeFastBounds(bounds, &bounds);
3059 }
3060 if (matrix) {
3061 matrix->mapRect(&bounds);
3062 }
3063 if (this->quickReject(bounds)) {
3064 return;
3065 }
3066 }
3067
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003068 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003069 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003070}
3071
vjiaoblack95302da2016-07-21 10:25:54 -07003072#ifdef SK_EXPERIMENTAL_SHADOWING
3073void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3074 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003075 const SkPaint* paint,
3076 const SkShadowParams& params) {
vjiaoblack95302da2016-07-21 10:25:54 -07003077 RETURN_ON_NULL(picture);
3078
3079 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3080
vjiaoblacke6f5d562016-08-25 06:30:23 -07003081 this->onDrawShadowedPicture(picture, matrix, paint, params);
vjiaoblack95302da2016-07-21 10:25:54 -07003082}
3083
3084void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3085 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003086 const SkPaint* paint,
3087 const SkShadowParams& params) {
vjiaoblack904527d2016-08-09 09:32:09 -07003088 if (!paint || paint->canComputeFastBounds()) {
3089 SkRect bounds = picture->cullRect();
3090 if (paint) {
3091 paint->computeFastBounds(bounds, &bounds);
3092 }
3093 if (matrix) {
3094 matrix->mapRect(&bounds);
3095 }
3096 if (this->quickReject(bounds)) {
3097 return;
3098 }
3099 }
3100
3101 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3102
vjiaoblacke6f5d562016-08-25 06:30:23 -07003103 sk_sp<SkImage> povDepthMap;
3104 sk_sp<SkImage> diffuseMap;
3105
vjiaoblack904527d2016-08-09 09:32:09 -07003106 // povDepthMap
3107 {
3108 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003109 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3110 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003111 sk_sp<SkLights> povLight = builder.finish();
3112
3113 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3114 picture->cullRect().height(),
3115 kBGRA_8888_SkColorType,
3116 kOpaque_SkAlphaType);
3117
3118 // Create a new surface (that matches the backend of canvas)
3119 // to create the povDepthMap
3120 sk_sp<SkSurface> surf(this->makeSurface(info));
3121
3122 // Wrap another SPFCanvas around the surface
3123 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3124 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3125
3126 // set the depth map canvas to have the light as the user's POV
3127 depthMapCanvas->setLights(std::move(povLight));
3128
3129 depthMapCanvas->drawPicture(picture);
vjiaoblack904527d2016-08-09 09:32:09 -07003130 povDepthMap = surf->makeImageSnapshot();
3131 }
3132
3133 // diffuseMap
3134 {
3135 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3136 picture->cullRect().height(),
3137 kBGRA_8888_SkColorType,
3138 kOpaque_SkAlphaType);
3139
3140 sk_sp<SkSurface> surf(this->makeSurface(info));
3141 surf->getCanvas()->drawPicture(picture);
3142
3143 diffuseMap = surf->makeImageSnapshot();
3144 }
vjiaoblack904527d2016-08-09 09:32:09 -07003145
3146 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3147 SkShader::kClamp_TileMode);
vjiaoblack904527d2016-08-09 09:32:09 -07003148 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3149 SkShader::kClamp_TileMode);
vjiaoblackb2796fd2016-09-09 09:22:39 -07003150
3151 // TODO: pass the depth to the shader in vertices, or uniforms
3152 // so we don't have to render depth and color separately
3153 for (int i = 0; i < fLights->numLights(); ++i) {
3154 // skip over ambient lights; they don't cast shadows
3155 // lights that have shadow maps do not need updating (because lights are immutable)
3156 sk_sp<SkImage> depthMap;
3157 SkISize shMapSize;
3158
3159 if (fLights->light(i).getShadowMap() != nullptr) {
3160 continue;
3161 }
3162
3163 if (fLights->light(i).isRadial()) {
3164 shMapSize.fHeight = 1;
3165 shMapSize.fWidth = (int) picture->cullRect().width();
3166
3167 SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1,
3168 kBGRA_8888_SkColorType,
3169 kOpaque_SkAlphaType);
3170
3171 // Create new surface (that matches the backend of canvas)
3172 // for each shadow map
3173 sk_sp<SkSurface> surf(this->makeSurface(info));
3174
3175 // Wrap another SPFCanvas around the surface
3176 SkCanvas* depthMapCanvas = surf->getCanvas();
3177
3178 SkLights::Builder builder;
3179 builder.add(fLights->light(i));
3180 sk_sp<SkLights> curLight = builder.finish();
3181
3182 sk_sp<SkShader> shadowMapShader;
3183 shadowMapShader = SkRadialShadowMapShader::Make(
3184 povDepthShader, curLight,
3185 (int) picture->cullRect().width(),
3186 (int) picture->cullRect().height());
3187
3188 SkPaint shadowMapPaint;
3189 shadowMapPaint.setShader(std::move(shadowMapShader));
3190
3191 depthMapCanvas->setLights(curLight);
3192
3193 depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(),
3194 diffuseMap->height()),
3195 shadowMapPaint);
3196
3197 depthMap = surf->makeImageSnapshot();
3198
3199 } else {
3200 // TODO: compute the correct size of the depth map from the light properties
3201 // TODO: maybe add a kDepth_8_SkColorType
3202 // TODO: find actual max depth of picture
3203 shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3204 fLights->light(i), 255,
3205 (int) picture->cullRect().width(),
3206 (int) picture->cullRect().height());
3207
3208 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3209 kBGRA_8888_SkColorType,
3210 kOpaque_SkAlphaType);
3211
3212 // Create a new surface (that matches the backend of canvas)
3213 // for each shadow map
3214 sk_sp<SkSurface> surf(this->makeSurface(info));
3215
3216 // Wrap another SPFCanvas around the surface
3217 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3218 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3219 depthMapCanvas->setShadowParams(params);
3220
3221 // set the depth map canvas to have the light we're drawing.
3222 SkLights::Builder builder;
3223 builder.add(fLights->light(i));
3224 sk_sp<SkLights> curLight = builder.finish();
3225 depthMapCanvas->setLights(std::move(curLight));
3226
3227 depthMapCanvas->drawPicture(picture);
3228 depthMap = surf->makeImageSnapshot();
3229 }
3230
3231 if (params.fType == SkShadowParams::kNoBlur_ShadowType) {
3232 fLights->light(i).setShadowMap(std::move(depthMap));
3233 } else if (params.fType == SkShadowParams::kVariance_ShadowType) {
3234 // we blur the variance map
3235 SkPaint blurPaint;
3236 blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius,
3237 params.fShadowRadius, nullptr));
3238
3239 SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3240 kBGRA_8888_SkColorType,
3241 kOpaque_SkAlphaType);
3242
3243 sk_sp<SkSurface> blurSurf(this->makeSurface(blurInfo));
3244
3245 blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
3246
3247 fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
3248 }
3249 }
3250
3251 SkPaint shadowPaint;
vjiaoblack904527d2016-08-09 09:32:09 -07003252 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3253 std::move(diffuseShader),
vjiaoblackb2796fd2016-09-09 09:22:39 -07003254 fLights,
vjiaoblack904527d2016-08-09 09:32:09 -07003255 diffuseMap->width(),
vjiaoblacke6f5d562016-08-25 06:30:23 -07003256 diffuseMap->height(),
3257 params);
vjiaoblack904527d2016-08-09 09:32:09 -07003258
3259 shadowPaint.setShader(shadowShader);
3260
3261 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003262}
3263#endif
3264
reed@android.com8a1c16f2008-12-17 15:59:43 +00003265///////////////////////////////////////////////////////////////////////////////
3266///////////////////////////////////////////////////////////////////////////////
3267
reed3aafe112016-08-18 12:45:34 -07003268SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003269 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003270
3271 SkASSERT(canvas);
3272
reed3aafe112016-08-18 12:45:34 -07003273 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003274 fDone = !fImpl->next();
3275}
3276
3277SkCanvas::LayerIter::~LayerIter() {
3278 fImpl->~SkDrawIter();
3279}
3280
3281void SkCanvas::LayerIter::next() {
3282 fDone = !fImpl->next();
3283}
3284
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003285SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003286 return fImpl->getDevice();
3287}
3288
3289const SkMatrix& SkCanvas::LayerIter::matrix() const {
3290 return fImpl->getMatrix();
3291}
3292
3293const SkPaint& SkCanvas::LayerIter::paint() const {
3294 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003295 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003296 paint = &fDefaultPaint;
3297 }
3298 return *paint;
3299}
3300
reed1e7f5e72016-04-27 07:49:17 -07003301const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003302int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3303int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003304
3305///////////////////////////////////////////////////////////////////////////////
3306
fmalitac3b589a2014-06-05 12:40:07 -07003307SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003308
3309///////////////////////////////////////////////////////////////////////////////
3310
3311static bool supported_for_raster_canvas(const SkImageInfo& info) {
3312 switch (info.alphaType()) {
3313 case kPremul_SkAlphaType:
3314 case kOpaque_SkAlphaType:
3315 break;
3316 default:
3317 return false;
3318 }
3319
3320 switch (info.colorType()) {
3321 case kAlpha_8_SkColorType:
3322 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003323 case kN32_SkColorType:
junov9e3dbdf2016-10-13 13:14:27 -07003324 case kRGBA_F16_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003325 break;
3326 default:
3327 return false;
3328 }
3329
3330 return true;
3331}
3332
Mike Reed5df49342016-11-12 08:06:55 -06003333std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
3334 size_t rowBytes) {
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003335 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003336 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003337 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003338
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003339 SkBitmap bitmap;
3340 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003341 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003342 }
Mike Reed5df49342016-11-12 08:06:55 -06003343 return skstd::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003344}
reedd5fa1a42014-08-09 11:08:05 -07003345
3346///////////////////////////////////////////////////////////////////////////////
3347
3348SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003349 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003350 : fCanvas(canvas)
3351 , fSaveCount(canvas->getSaveCount())
3352{
bsalomon49f085d2014-09-05 13:34:00 -07003353 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003354 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003355 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003356 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003357 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003358 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003359 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003360 canvas->save();
3361 }
mtklein6cfa73a2014-08-13 13:33:49 -07003362
bsalomon49f085d2014-09-05 13:34:00 -07003363 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003364 canvas->concat(*matrix);
3365 }
3366}
3367
3368SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3369 fCanvas->restoreToCount(fSaveCount);
3370}
reede8f30622016-03-23 18:59:25 -07003371
Florin Malitaee424ac2016-12-01 12:47:59 -05003372///////////////////////////////////////////////////////////////////////////////
3373
3374SkNoDrawCanvas::SkNoDrawCanvas(int width, int height)
3375 : INHERITED(SkIRect::MakeWH(width, height), kConservativeRasterClip_InitFlag) {}
3376
Florin Malita439ace92016-12-02 12:05:41 -05003377SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds)
3378 : INHERITED(bounds, kConservativeRasterClip_InitFlag) {}
3379
Florin Malitaee424ac2016-12-01 12:47:59 -05003380SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
3381 (void)this->INHERITED::getSaveLayerStrategy(rec);
3382 return kNoLayer_SaveLayerStrategy;
3383}
3384
3385///////////////////////////////////////////////////////////////////////////////
reed73603f32016-09-20 08:42:38 -07003386
reed73603f32016-09-20 08:42:38 -07003387static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3388static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3389static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3390static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3391static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3392static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");