blob: 7db8601846915e3ff7f2b564b3cdd5c226c365f4 [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"
joshualitt5f5a8d72015-02-25 14:09:45 -080017#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070018#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080019#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070020#include "SkImageFilter.h"
21#include "SkImageFilterCache.h"
msarettc573a402016-08-02 08:05:56 -070022#include "SkLatticeIter.h"
reed262a71b2015-12-05 13:07:27 -080023#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000024#include "SkMetaData.h"
msarettfbfa2582016-08-12 08:29:08 -070025#include "SkNx.h"
reedc83a2972015-07-16 07:40:45 -070026#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070027#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkPicture.h"
vjiaoblackb2796fd2016-09-09 09:22:39 -070029#include "SkRadialShadowMapShader.h"
reed@google.com00177082011-10-12 14:34:30 +000030#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080031#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000032#include "SkRRect.h"
vjiaoblack904527d2016-08-09 09:32:09 -070033#include "SkShadowPaintFilterCanvas.h"
34#include "SkShadowShader.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000035#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080036#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000037#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070038#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000039#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000040#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080041#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070042#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000043
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000044#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080045#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000046#include "GrRenderTarget.h"
bsalomon614d8f92016-07-13 15:42:40 -070047#include "SkGrPriv.h"
vjiaoblacke6f5d562016-08-25 06:30:23 -070048
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000049#endif
50
reede3b38ce2016-01-08 09:18:44 -080051#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
52
reedc83a2972015-07-16 07:40:45 -070053/*
54 * Return true if the drawing this rect would hit every pixels in the canvas.
55 *
56 * Returns false if
57 * - rect does not contain the canvas' bounds
58 * - paint is not fill
59 * - paint would blur or otherwise change the coverage of the rect
60 */
61bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
62 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070063 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
64 (int)kNone_ShaderOverrideOpacity,
65 "need_matching_enums0");
66 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
67 (int)kOpaque_ShaderOverrideOpacity,
68 "need_matching_enums1");
69 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
70 (int)kNotOpaque_ShaderOverrideOpacity,
71 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070072
73 const SkISize size = this->getBaseLayerSize();
74 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
75 if (!this->getClipStack()->quickContains(bounds)) {
76 return false;
77 }
78
79 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070080 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070081 return false; // conservative
82 }
halcanaryc5769b22016-08-10 07:13:21 -070083
84 SkRect devRect;
85 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
86 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070087 return false;
88 }
89 }
90
91 if (paint) {
92 SkPaint::Style paintStyle = paint->getStyle();
93 if (!(paintStyle == SkPaint::kFill_Style ||
94 paintStyle == SkPaint::kStrokeAndFill_Style)) {
95 return false;
96 }
97 if (paint->getMaskFilter() || paint->getLooper()
98 || paint->getPathEffect() || paint->getImageFilter()) {
99 return false; // conservative
100 }
101 }
102 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
103}
104
105///////////////////////////////////////////////////////////////////////////////////////////////////
106
reedd990e2f2014-12-22 11:58:30 -0800107static bool gIgnoreSaveLayerBounds;
108void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
109 gIgnoreSaveLayerBounds = ignore;
110}
111bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
112 return gIgnoreSaveLayerBounds;
113}
114
reed0acf1b42014-12-22 16:12:38 -0800115static bool gTreatSpriteAsBitmap;
116void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
117 gTreatSpriteAsBitmap = spriteAsBitmap;
118}
119bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
120 return gTreatSpriteAsBitmap;
121}
122
reed@google.comda17f752012-08-16 18:27:05 +0000123// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000124//#define SK_TRACE_SAVERESTORE
125
126#ifdef SK_TRACE_SAVERESTORE
127 static int gLayerCounter;
128 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
129 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
130
131 static int gRecCounter;
132 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
133 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
134
135 static int gCanvasCounter;
136 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
137 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
138#else
139 #define inc_layer()
140 #define dec_layer()
141 #define inc_rec()
142 #define dec_rec()
143 #define inc_canvas()
144 #define dec_canvas()
145#endif
146
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000147typedef SkTLazy<SkPaint> SkLazyPaint;
148
reedc83a2972015-07-16 07:40:45 -0700149void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000150 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700151 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
152 ? SkSurface::kDiscard_ContentChangeMode
153 : SkSurface::kRetain_ContentChangeMode);
154 }
155}
156
157void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
158 ShaderOverrideOpacity overrideOpacity) {
159 if (fSurfaceBase) {
160 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
161 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
162 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
163 // and therefore we don't care which mode we're in.
164 //
165 if (fSurfaceBase->outstandingImageSnapshot()) {
166 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
167 mode = SkSurface::kDiscard_ContentChangeMode;
168 }
169 }
170 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000171 }
172}
173
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000176/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 The clip/matrix/proc are fields that reflect the top of the save/restore
178 stack. Whenever the canvas changes, it marks a dirty flag, and then before
179 these are used (assuming we're not on a layer) we rebuild these cache
180 values: they reflect the top of the save stack, but translated and clipped
181 by the device's XY offset and bitmap-bounds.
182*/
183struct DeviceCM {
184 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000185 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000186 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000187 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700188 const SkMatrix* fMatrix;
189 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700190 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191
reed96e657d2015-03-10 17:30:07 -0700192 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed7503d602016-07-15 14:23:29 -0700193 bool conservativeRasterClip, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700194 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700195 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700196 , fStashedMatrix(stashed)
reedd9544982014-09-09 18:46:22 -0700197 {
reed2c9e2002016-07-25 08:05:22 -0700198 SkSafeRef(device);
reed@google.com4b226022011-01-11 18:32:13 +0000199 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700200 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000201 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000203 ~DeviceCM() {
reed2c9e2002016-07-25 08:05:22 -0700204 SkSafeUnref(fDevice);
halcanary385fe4d2015-08-26 13:07:48 -0700205 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000206 }
reed@google.com4b226022011-01-11 18:32:13 +0000207
mtkleinfeaadee2015-04-08 11:25:48 -0700208 void reset(const SkIRect& bounds) {
209 SkASSERT(!fPaint);
210 SkASSERT(!fNext);
211 SkASSERT(fDevice);
212 fClip.setRect(bounds);
213 }
214
reed@google.com045e62d2011-10-24 12:19:46 +0000215 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
reedde6c5312016-09-02 12:10:07 -0700216 SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000217 int x = fDevice->getOrigin().x();
218 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 int width = fDevice->width();
220 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000221
reed@android.com8a1c16f2008-12-17 15:59:43 +0000222 if ((x | y) == 0) {
223 fMatrix = &totalMatrix;
224 fClip = totalClip;
225 } else {
226 fMatrixStorage = totalMatrix;
227 fMatrixStorage.postTranslate(SkIntToScalar(-x),
228 SkIntToScalar(-y));
229 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000230
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 totalClip.translate(-x, -y, &fClip);
232 }
233
reed@google.com045e62d2011-10-24 12:19:46 +0000234 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000235
236 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000237
reed@android.com8a1c16f2008-12-17 15:59:43 +0000238 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000239 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 SkRegion::kDifference_Op);
241 }
reed@google.com4b226022011-01-11 18:32:13 +0000242
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243#ifdef SK_DEBUG
244 if (!fClip.isEmpty()) {
245 SkIRect deviceR;
246 deviceR.set(0, 0, width, height);
247 SkASSERT(deviceR.contains(fClip.getBounds()));
248 }
249#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000250 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251};
252
253/* This is the record we keep for each save/restore level in the stack.
254 Since a level optionally copies the matrix and/or stack, we have pointers
255 for these fields. If the value is copied for this level, the copy is
256 stored in the ...Storage field, and the pointer points to that. If the
257 value is not copied for this level, we ignore ...Storage, and just point
258 at the corresponding value in the previous level in the stack.
259*/
260class SkCanvas::MCRec {
261public:
reed1f836ee2014-07-07 07:49:34 -0700262 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700263 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 /* If there are any layers in the stack, this points to the top-most
265 one that is at or below this level in the stack (so we know what
266 bitmap/device to draw into from this level. This value is NOT
267 reference counted, since the real owner is either our fLayer field,
268 or a previous one in a lower level.)
269 */
reed2ff1fce2014-12-11 07:07:37 -0800270 DeviceCM* fTopLayer;
271 SkRasterClip fRasterClip;
272 SkMatrix fMatrix;
273 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274
vjiaoblacke5de1302016-07-13 14:05:28 -0700275 // This is the current cumulative depth (aggregate of all done translateZ calls)
276 SkScalar fCurDrawDepth;
277
reedd9544982014-09-09 18:46:22 -0700278 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700279 fFilter = nullptr;
280 fLayer = nullptr;
281 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800282 fMatrix.reset();
283 fDeferredSaveCount = 0;
vjiaoblacke5de1302016-07-13 14:05:28 -0700284 fCurDrawDepth = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700285
reedd9544982014-09-09 18:46:22 -0700286 // don't bother initializing fNext
287 inc_rec();
288 }
vjiaoblacke5de1302016-07-13 14:05:28 -0700289 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
290 fCurDrawDepth(prev.fCurDrawDepth) {
reedd9544982014-09-09 18:46:22 -0700291 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700292 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700293 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800294 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700295
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296 // don't bother initializing fNext
297 inc_rec();
298 }
299 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000300 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700301 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302 dec_rec();
303 }
mtkleinfeaadee2015-04-08 11:25:48 -0700304
305 void reset(const SkIRect& bounds) {
306 SkASSERT(fLayer);
307 SkASSERT(fDeferredSaveCount == 0);
308
309 fMatrix.reset();
310 fRasterClip.setRect(bounds);
311 fLayer->reset(bounds);
312 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313};
314
reed02f9ed72016-09-06 09:06:18 -0700315static SkIRect compute_device_bounds(SkBaseDevice* device) {
316 return SkIRect::MakeXYWH(device->getOrigin().x(), device->getOrigin().y(),
317 device->width(), device->height());
318}
319
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320class SkDrawIter : public SkDraw {
321public:
reed3aafe112016-08-18 12:45:34 -0700322 SkDrawIter(SkCanvas* canvas) {
junov@google.com4370aed2012-01-18 16:21:08 +0000323 canvas = canvas->canvasForDrawIter();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000324 canvas->updateDeviceCMCache();
325
reed687fa1c2015-04-07 08:00:56 -0700326 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000327 fCurrLayer = canvas->fMCRec->fTopLayer;
reed02f9ed72016-09-06 09:06:18 -0700328
329 fMultiDeviceCS = nullptr;
330 if (fCurrLayer->fNext) {
331 fMultiDeviceCS = canvas->fClipStack;
332 fMultiDeviceCS->save();
333 }
334 }
335
336 ~SkDrawIter() {
337 if (fMultiDeviceCS) {
338 fMultiDeviceCS->restore();
339 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 }
reed@google.com4b226022011-01-11 18:32:13 +0000341
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 bool next() {
reed02f9ed72016-09-06 09:06:18 -0700343 if (fMultiDeviceCS && fDevice) {
344 // remove the previous device's bounds
reed73603f32016-09-20 08:42:38 -0700345 fMultiDeviceCS->clipDevRect(compute_device_bounds(fDevice), SkCanvas::kDifference_Op);
reed02f9ed72016-09-06 09:06:18 -0700346 }
347
reed@android.com8a1c16f2008-12-17 15:59:43 +0000348 // skip over recs with empty clips
reed3aafe112016-08-18 12:45:34 -0700349 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
350 fCurrLayer = fCurrLayer->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000351 }
352
reed@google.comf68c5e22012-02-24 16:38:58 +0000353 const DeviceCM* rec = fCurrLayer;
354 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000355
356 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000357 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000358 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700359 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700360 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700361 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000363 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000364
365 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700366 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000367
reed@android.com8a1c16f2008-12-17 15:59:43 +0000368 return true;
369 }
370 return false;
371 }
reed@google.com4b226022011-01-11 18:32:13 +0000372
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000373 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700374 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000375 int getX() const { return fDevice->getOrigin().x(); }
376 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000379
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000381 const DeviceCM* fCurrLayer;
382 const SkPaint* fPaint; // May be null.
reed02f9ed72016-09-06 09:06:18 -0700383 SkClipStack* fMultiDeviceCS;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000384
385 typedef SkDraw INHERITED;
386};
387
388/////////////////////////////////////////////////////////////////////////////
389
reeddbc3cef2015-04-29 12:18:57 -0700390static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
391 return lazy->isValid() ? lazy->get() : lazy->set(orig);
392}
393
394/**
395 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700396 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700397 */
reedd053ce92016-03-22 10:17:23 -0700398static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700399 SkImageFilter* imgf = paint.getImageFilter();
400 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700401 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700402 }
403
reedd053ce92016-03-22 10:17:23 -0700404 SkColorFilter* imgCFPtr;
405 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700406 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700407 }
reedd053ce92016-03-22 10:17:23 -0700408 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700409
410 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700411 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700412 // there is no existing paint colorfilter, so we can just return the imagefilter's
413 return imgCF;
414 }
415
416 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
417 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700418 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700419}
420
senorblanco87e066e2015-10-28 11:23:36 -0700421/**
422 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
423 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
424 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
425 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
426 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
427 * conservative "effective" bounds based on the settings in the paint... with one exception. This
428 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
429 * deliberately ignored.
430 */
431static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
432 const SkRect& rawBounds,
433 SkRect* storage) {
434 SkPaint tmpUnfiltered(paint);
435 tmpUnfiltered.setImageFilter(nullptr);
436 if (tmpUnfiltered.canComputeFastBounds()) {
437 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
438 } else {
439 return rawBounds;
440 }
441}
442
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443class AutoDrawLooper {
444public:
senorblanco87e066e2015-10-28 11:23:36 -0700445 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
446 // paint. It's used to determine the size of the offscreen layer for filters.
447 // If null, the clip will be used instead.
reed3aafe112016-08-18 12:45:34 -0700448 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700449 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000450 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800451#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800453#else
454 fFilter = nullptr;
455#endif
reed4a8126e2014-09-22 07:29:03 -0700456 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000457 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700458 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000459 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000460
reedd053ce92016-03-22 10:17:23 -0700461 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700462 if (simplifiedCF) {
463 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700464 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700465 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700466 fPaint = paint;
467 }
468
469 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700470 /**
471 * We implement ImageFilters for a given draw by creating a layer, then applying the
472 * imagefilter to the pixels of that layer (its backing surface/image), and then
473 * we call restore() to xfer that layer to the main canvas.
474 *
475 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
476 * 2. Generate the src pixels:
477 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
478 * return (fPaint). We then draw the primitive (using srcover) into a cleared
479 * buffer/surface.
480 * 3. Restore the layer created in #1
481 * The imagefilter is passed the buffer/surface from the layer (now filled with the
482 * src pixels of the primitive). It returns a new "filtered" buffer, which we
483 * draw onto the previous layer using the xfermode from the original paint.
484 */
reed@google.com8926b162012-03-23 15:36:36 +0000485 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700486 tmp.setImageFilter(fPaint->getImageFilter());
Mike Reedce02e712016-10-03 18:02:50 +0000487 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700488 SkRect storage;
489 if (rawBounds) {
490 // Make rawBounds include all paint outsets except for those due to image filters.
491 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
492 }
reedbfd5f172016-01-07 11:28:08 -0800493 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700494 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700495 fTempLayerForImageFilter = true;
496 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000497 }
498
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000499 if (SkDrawLooper* looper = paint.getLooper()) {
500 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
501 looper->contextSize());
502 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000503 fIsSimple = false;
504 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700505 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000506 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700507 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000508 }
509 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000510
reed@android.com8a1c16f2008-12-17 15:59:43 +0000511 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700512 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000513 fCanvas->internalRestore();
514 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000515 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000516 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000517
reed@google.com4e2b3d32011-04-07 14:18:59 +0000518 const SkPaint& paint() const {
519 SkASSERT(fPaint);
520 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000522
reed@google.com129ec222012-05-15 13:24:09 +0000523 bool next(SkDrawFilter::Type drawType) {
524 if (fDone) {
525 return false;
526 } else if (fIsSimple) {
527 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000528 return !fPaint->nothingToDraw();
529 } else {
530 return this->doNext(drawType);
531 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000532 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000533
reed@android.com8a1c16f2008-12-17 15:59:43 +0000534private:
reeddbc3cef2015-04-29 12:18:57 -0700535 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
536 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000537 SkCanvas* fCanvas;
538 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000539 SkDrawFilter* fFilter;
540 const SkPaint* fPaint;
541 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700542 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000543 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000544 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000545 SkDrawLooper::Context* fLooperContext;
546 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000547
548 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000549};
550
reed@google.com129ec222012-05-15 13:24:09 +0000551bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700552 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000553 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700554 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000555
reeddbc3cef2015-04-29 12:18:57 -0700556 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
557 *fLazyPaintInit.get() : fOrigPaint);
reed@google.com129ec222012-05-15 13:24:09 +0000558
reed5c476fb2015-04-20 08:04:21 -0700559 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700560 paint->setImageFilter(nullptr);
Mike Reedce02e712016-10-03 18:02:50 +0000561 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000562 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000563
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000564 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000565 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000566 return false;
567 }
568 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000569 if (!fFilter->filter(paint, drawType)) {
570 fDone = true;
571 return false;
572 }
halcanary96fcdcc2015-08-27 07:41:13 -0700573 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000574 // no looper means we only draw once
575 fDone = true;
576 }
577 }
578 fPaint = paint;
579
580 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000581 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000582 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000583 }
584
585 // call this after any possible paint modifiers
586 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700587 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000588 return false;
589 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000590 return true;
591}
592
reed@android.com8a1c16f2008-12-17 15:59:43 +0000593////////// macros to place around the internal draw calls //////////////////
594
reed3aafe112016-08-18 12:45:34 -0700595#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
596 this->predrawNotify(); \
597 AutoDrawLooper looper(this, paint, skipLayerForFilter, bounds); \
598 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
reed262a71b2015-12-05 13:07:27 -0800599 SkDrawIter iter(this);
600
601
reed@google.com8926b162012-03-23 15:36:36 +0000602#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000603 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700604 AutoDrawLooper looper(this, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000605 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000606 SkDrawIter iter(this);
607
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000608#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000609 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700610 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000611 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000612 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000613
reedc83a2972015-07-16 07:40:45 -0700614#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
615 this->predrawNotify(bounds, &paint, auxOpaque); \
reed3aafe112016-08-18 12:45:34 -0700616 AutoDrawLooper looper(this, paint, false, bounds); \
reedc83a2972015-07-16 07:40:45 -0700617 while (looper.next(type)) { \
618 SkDrawIter iter(this);
619
reed@google.com4e2b3d32011-04-07 14:18:59 +0000620#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000621
622////////////////////////////////////////////////////////////////////////////
623
msarettfbfa2582016-08-12 08:29:08 -0700624static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
625 if (bounds.isEmpty()) {
626 return SkRect::MakeEmpty();
627 }
628
629 // Expand bounds out by 1 in case we are anti-aliasing. We store the
630 // bounds as floats to enable a faster quick reject implementation.
631 SkRect dst;
632 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
633 return dst;
634}
635
mtkleinfeaadee2015-04-08 11:25:48 -0700636void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
637 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700638 fClipStack->reset();
639 fMCRec->reset(bounds);
640
641 // We're peering through a lot of structs here. Only at this scope do we
642 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
643 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
msarettfbfa2582016-08-12 08:29:08 -0700644 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700645 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700646}
647
reedd9544982014-09-09 18:46:22 -0700648SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800649 if (device && device->forceConservativeRasterClip()) {
650 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
651 }
652 // Since init() is only called once by our constructors, it is safe to perform this
653 // const-cast.
654 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
655
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000656 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700657 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800658 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700659 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700660#ifdef SK_EXPERIMENTAL_SHADOWING
661 fLights = nullptr;
662#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000663
halcanary385fe4d2015-08-26 13:07:48 -0700664 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700665
reed@android.com8a1c16f2008-12-17 15:59:43 +0000666 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700667 new (fMCRec) MCRec(fConservativeRasterClip);
msarett9637ea92016-08-18 14:03:30 -0700668 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000669
reeda499f902015-05-01 09:34:31 -0700670 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
671 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700672 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700673 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700674
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676
halcanary96fcdcc2015-08-27 07:41:13 -0700677 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000678
reedf92c8662014-08-18 08:02:43 -0700679 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700680 // The root device and the canvas should always have the same pixel geometry
681 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700682 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800683 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700684 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700685 }
msarettfbfa2582016-08-12 08:29:08 -0700686
reedf92c8662014-08-18 08:02:43 -0700687 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000688}
689
reed@google.comcde92112011-07-06 20:00:52 +0000690SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000691 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700692 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800693 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000694{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000695 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000696
halcanary96fcdcc2015-08-27 07:41:13 -0700697 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000698}
699
reedd9544982014-09-09 18:46:22 -0700700static SkBitmap make_nopixels(int width, int height) {
701 SkBitmap bitmap;
702 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
703 return bitmap;
704}
705
706class SkNoPixelsBitmapDevice : public SkBitmapDevice {
707public:
robertphillipsfcf78292015-06-19 11:49:52 -0700708 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
709 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800710 {
711 this->setOrigin(bounds.x(), bounds.y());
712 }
reedd9544982014-09-09 18:46:22 -0700713
714private:
piotaixrb5fae932014-09-24 13:03:30 -0700715
reedd9544982014-09-09 18:46:22 -0700716 typedef SkBitmapDevice INHERITED;
717};
718
reed96a857e2015-01-25 10:33:58 -0800719SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000720 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800721 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800722 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000723{
724 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700725
halcanary385fe4d2015-08-26 13:07:48 -0700726 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
727 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700728}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000729
reed78e27682014-11-19 08:04:34 -0800730SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700731 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700732 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800733 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700734{
735 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700736
halcanary385fe4d2015-08-26 13:07:48 -0700737 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700738}
739
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000740SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000741 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700742 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800743 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000744{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000745 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700746
reedd9544982014-09-09 18:46:22 -0700747 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000748}
749
robertphillipsfcf78292015-06-19 11:49:52 -0700750SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
751 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700752 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800753 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700754{
755 inc_canvas();
756
757 this->init(device, flags);
758}
759
reed4a8126e2014-09-22 07:29:03 -0700760SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700761 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700762 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800763 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700764{
765 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700766
halcanary385fe4d2015-08-26 13:07:48 -0700767 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700768 this->init(device, kDefault_InitFlags);
769}
reed29c857d2014-09-21 10:25:07 -0700770
reed4a8126e2014-09-22 07:29:03 -0700771SkCanvas::SkCanvas(const SkBitmap& bitmap)
772 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
773 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800774 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700775{
776 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700777
halcanary385fe4d2015-08-26 13:07:48 -0700778 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700779 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000780}
781
782SkCanvas::~SkCanvas() {
783 // free up the contents of our deque
784 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000785
reed@android.com8a1c16f2008-12-17 15:59:43 +0000786 this->internalRestore(); // restore the last, since we're going away
787
halcanary385fe4d2015-08-26 13:07:48 -0700788 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000789
reed@android.com8a1c16f2008-12-17 15:59:43 +0000790 dec_canvas();
791}
792
fmalita53d9f1c2016-01-25 06:23:54 -0800793#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000794SkDrawFilter* SkCanvas::getDrawFilter() const {
795 return fMCRec->fFilter;
796}
797
798SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700799 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000800 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
801 return filter;
802}
fmalita77650002016-01-21 18:47:11 -0800803#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000804
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000805SkMetaData& SkCanvas::getMetaData() {
806 // metadata users are rare, so we lazily allocate it. If that changes we
807 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700808 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000809 fMetaData = new SkMetaData;
810 }
811 return *fMetaData;
812}
813
reed@android.com8a1c16f2008-12-17 15:59:43 +0000814///////////////////////////////////////////////////////////////////////////////
815
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000816void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700817 this->onFlush();
818}
819
820void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000821 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000822 if (device) {
823 device->flush();
824 }
825}
826
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000827SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000828 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000829 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
830}
831
senorblancoafc7cce2016-02-02 18:44:15 -0800832SkIRect SkCanvas::getTopLayerBounds() const {
833 SkBaseDevice* d = this->getTopDevice();
834 if (!d) {
835 return SkIRect::MakeEmpty();
836 }
837 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
838}
839
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000840SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000842 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000843 SkASSERT(rec && rec->fLayer);
844 return rec->fLayer->fDevice;
845}
846
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000847SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000848 if (updateMatrixClip) {
849 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
850 }
reed@google.com9266fed2011-03-30 00:18:03 +0000851 return fMCRec->fTopLayer->fDevice;
852}
853
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000854bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700855 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000856 return false;
857 }
858
859 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700860 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700861 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000862 return false;
863 }
864 weAllocated = true;
865 }
866
reedcf01e312015-05-23 19:14:51 -0700867 SkAutoPixmapUnlock unlocker;
868 if (bitmap->requestLock(&unlocker)) {
869 const SkPixmap& pm = unlocker.pixmap();
870 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
871 return true;
872 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000873 }
874
875 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700876 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000877 }
878 return false;
879}
reed@google.com51df9e32010-12-23 19:29:18 +0000880
bsalomon@google.comc6980972011-11-02 19:57:21 +0000881bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000882 SkIRect r = srcRect;
883 const SkISize size = this->getBaseLayerSize();
884 if (!r.intersect(0, 0, size.width(), size.height())) {
885 bitmap->reset();
886 return false;
887 }
888
reed84825042014-09-02 12:50:45 -0700889 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000890 // bitmap will already be reset.
891 return false;
892 }
893 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
894 bitmap->reset();
895 return false;
896 }
897 return true;
898}
899
reed96472de2014-12-10 09:53:42 -0800900bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000901 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000902 if (!device) {
903 return false;
904 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000905 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800906
reed96472de2014-12-10 09:53:42 -0800907 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
908 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000909 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000910 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000911
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000912 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800913 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000914}
915
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000916bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700917 SkAutoPixmapUnlock unlocker;
918 if (bitmap.requestLock(&unlocker)) {
919 const SkPixmap& pm = unlocker.pixmap();
920 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000921 }
922 return false;
923}
924
925bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
926 int x, int y) {
927 switch (origInfo.colorType()) {
928 case kUnknown_SkColorType:
929 case kIndex_8_SkColorType:
930 return false;
931 default:
932 break;
933 }
halcanary96fcdcc2015-08-27 07:41:13 -0700934 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000935 return false;
936 }
937
938 const SkISize size = this->getBaseLayerSize();
939 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
940 if (!target.intersect(0, 0, size.width(), size.height())) {
941 return false;
942 }
943
944 SkBaseDevice* device = this->getDevice();
945 if (!device) {
946 return false;
947 }
948
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000949 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700950 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000951
952 // if x or y are negative, then we have to adjust pixels
953 if (x > 0) {
954 x = 0;
955 }
956 if (y > 0) {
957 y = 0;
958 }
959 // here x,y are either 0 or negative
960 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
961
reed4af35f32014-06-27 17:47:49 -0700962 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700963 const bool completeOverwrite = info.dimensions() == size;
964 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700965
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000966 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000967 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000968}
reed@google.com51df9e32010-12-23 19:29:18 +0000969
junov@google.com4370aed2012-01-18 16:21:08 +0000970SkCanvas* SkCanvas::canvasForDrawIter() {
971 return this;
972}
973
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974//////////////////////////////////////////////////////////////////////////////
975
reed@android.com8a1c16f2008-12-17 15:59:43 +0000976void SkCanvas::updateDeviceCMCache() {
977 if (fDeviceCMDirty) {
978 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700979 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000981
halcanary96fcdcc2015-08-27 07:41:13 -0700982 if (nullptr == layer->fNext) { // only one layer
reedde6c5312016-09-02 12:10:07 -0700983 layer->updateMC(totalMatrix, totalClip, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000985 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 do {
reedde6c5312016-09-02 12:10:07 -0700987 layer->updateMC(totalMatrix, clip, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700988 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989 }
990 fDeviceCMDirty = false;
991 }
992}
993
reed@android.com8a1c16f2008-12-17 15:59:43 +0000994///////////////////////////////////////////////////////////////////////////////
995
reed2ff1fce2014-12-11 07:07:37 -0800996void SkCanvas::checkForDeferredSave() {
997 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800998 this->doSave();
999 }
1000}
1001
reedf0090cb2014-11-26 08:55:51 -08001002int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001003#ifdef SK_DEBUG
1004 int count = 0;
1005 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1006 for (;;) {
1007 const MCRec* rec = (const MCRec*)iter.next();
1008 if (!rec) {
1009 break;
1010 }
1011 count += 1 + rec->fDeferredSaveCount;
1012 }
1013 SkASSERT(count == fSaveCount);
1014#endif
1015 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001016}
1017
1018int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001019 fSaveCount += 1;
1020 fMCRec->fDeferredSaveCount += 1;
1021 return this->getSaveCount() - 1; // return our prev value
1022}
1023
1024void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001025 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001026
1027 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1028 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001029 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001030}
1031
1032void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001033 if (fMCRec->fDeferredSaveCount > 0) {
1034 SkASSERT(fSaveCount > 1);
1035 fSaveCount -= 1;
1036 fMCRec->fDeferredSaveCount -= 1;
1037 } else {
1038 // check for underflow
1039 if (fMCStack.count() > 1) {
1040 this->willRestore();
1041 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001042 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001043 this->internalRestore();
1044 this->didRestore();
1045 }
reedf0090cb2014-11-26 08:55:51 -08001046 }
1047}
1048
1049void SkCanvas::restoreToCount(int count) {
1050 // sanity check
1051 if (count < 1) {
1052 count = 1;
1053 }
mtkleinf0f14112014-12-12 08:46:25 -08001054
reedf0090cb2014-11-26 08:55:51 -08001055 int n = this->getSaveCount() - count;
1056 for (int i = 0; i < n; ++i) {
1057 this->restore();
1058 }
1059}
1060
reed2ff1fce2014-12-11 07:07:37 -08001061void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001063 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001065
reed687fa1c2015-04-07 08:00:56 -07001066 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001067}
1068
reed4960eee2015-12-18 07:09:18 -08001069bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed4960eee2015-12-18 07:09:18 -08001070 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001071}
1072
reed4960eee2015-12-18 07:09:18 -08001073bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001074 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001075 SkIRect clipBounds;
1076 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001077 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001078 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001079
reed96e657d2015-03-10 17:30:07 -07001080 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1081
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001082 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001083 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001084 if (bounds && !imageFilter->canComputeFastBounds()) {
1085 bounds = nullptr;
1086 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001087 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001088 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001089 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001091
reed96e657d2015-03-10 17:30:07 -07001092 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001093 r.roundOut(&ir);
1094 // early exit if the layer's bounds are clipped out
1095 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001096 if (BoundsAffectsClip(saveLayerFlags)) {
reed1f836ee2014-07-07 07:49:34 -07001097 fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001098 fDeviceClipBounds.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001099 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001100 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101 }
1102 } else { // no user bounds, so just use the clip
1103 ir = clipBounds;
1104 }
reed180aec42015-03-11 10:39:04 -07001105 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001106
reed4960eee2015-12-18 07:09:18 -08001107 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001108 // Simplify the current clips since they will be applied properly during restore()
reed73603f32016-09-20 08:42:38 -07001109 fClipStack->clipDevRect(ir, kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001110 fMCRec->fRasterClip.setRect(ir);
msarettfbfa2582016-08-12 08:29:08 -07001111 fDeviceClipBounds = qr_clip_bounds(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001112 }
1113
1114 if (intersection) {
1115 *intersection = ir;
1116 }
1117 return true;
1118}
1119
reed4960eee2015-12-18 07:09:18 -08001120
reed4960eee2015-12-18 07:09:18 -08001121int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1122 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001123}
1124
reed70ee31b2015-12-10 13:44:45 -08001125int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001126 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1127}
1128
1129int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1130 SaveLayerRec rec(origRec);
1131 if (gIgnoreSaveLayerBounds) {
1132 rec.fBounds = nullptr;
1133 }
1134 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1135 fSaveCount += 1;
1136 this->internalSaveLayer(rec, strategy);
1137 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001138}
1139
reeda2217ef2016-07-20 06:04:34 -07001140void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1141 SkBaseDevice* dst, const SkMatrix& ctm,
1142 const SkClipStack* clipStack) {
1143 SkDraw draw;
1144 SkRasterClip rc;
1145 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1146 if (!dst->accessPixels(&draw.fDst)) {
1147 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001148 }
reeda2217ef2016-07-20 06:04:34 -07001149 draw.fMatrix = &SkMatrix::I();
1150 draw.fRC = &rc;
1151 draw.fClipStack = clipStack;
1152 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001153
1154 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001155 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001156
1157 int x = src->getOrigin().x() - dst->getOrigin().x();
1158 int y = src->getOrigin().y() - dst->getOrigin().y();
1159 auto special = src->snapSpecial();
1160 if (special) {
1161 dst->drawSpecial(draw, special.get(), x, y, p);
1162 }
robertphillips7354a4b2015-12-16 05:08:27 -08001163}
reed70ee31b2015-12-10 13:44:45 -08001164
reed129ed1c2016-02-22 06:42:31 -08001165static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1166 const SkPaint* paint) {
1167 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1168 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001169 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001170 const bool hasImageFilter = paint && paint->getImageFilter();
1171
1172 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1173 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1174 // force to L32
1175 return SkImageInfo::MakeN32(w, h, alphaType);
1176 } else {
1177 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001178 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001179 }
1180}
1181
reed4960eee2015-12-18 07:09:18 -08001182void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1183 const SkRect* bounds = rec.fBounds;
1184 const SkPaint* paint = rec.fPaint;
1185 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1186
reed8c30a812016-04-20 16:36:51 -07001187 SkLazyPaint lazyP;
1188 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1189 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001190 SkMatrix remainder;
1191 SkSize scale;
1192 /*
1193 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1194 * but they do handle scaling. To accommodate this, we do the following:
1195 *
1196 * 1. Stash off the current CTM
1197 * 2. Decompose the CTM into SCALE and REMAINDER
1198 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1199 * contains the REMAINDER
1200 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1201 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1202 * of the original imagefilter, and draw that (via drawSprite)
1203 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1204 *
1205 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1206 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1207 */
reed96a04f32016-04-25 09:25:15 -07001208 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001209 stashedMatrix.decomposeScale(&scale, &remainder))
1210 {
1211 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1212 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1213 SkPaint* p = lazyP.set(*paint);
1214 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1215 SkFilterQuality::kLow_SkFilterQuality,
1216 sk_ref_sp(imageFilter)));
1217 imageFilter = p->getImageFilter();
1218 paint = p;
1219 }
reed8c30a812016-04-20 16:36:51 -07001220
junov@chromium.orga907ac32012-02-24 21:54:07 +00001221 // do this before we create the layer. We don't call the public save() since
1222 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001223 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001224
1225 fDeviceCMDirty = true;
1226
1227 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001228 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001229 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001230 }
1231
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001232 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1233 // the clipRectBounds() call above?
1234 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001235 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001236 }
1237
reed4960eee2015-12-18 07:09:18 -08001238 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001239 SkPixelGeometry geo = fProps.pixelGeometry();
1240 if (paint) {
reed76033be2015-03-14 10:54:31 -07001241 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001242 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001243 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001244 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001245 }
1246 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001247
robertphillips5139e502016-07-19 05:10:40 -07001248 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001249 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001250 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001251 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001252 }
reedb2db8982014-11-13 12:41:02 -08001253
robertphillips5139e502016-07-19 05:10:40 -07001254 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001255 paint);
1256
robertphillips5139e502016-07-19 05:10:40 -07001257 SkAutoTUnref<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001258 {
reed70ee31b2015-12-10 13:44:45 -08001259 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001260 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001261 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001262 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001263 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001264 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1265 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001266 SkErrorInternals::SetError(kInternalError_SkError,
1267 "Unable to create device for layer.");
1268 return;
reed61f501f2015-04-29 08:34:00 -07001269 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001270 }
robertphillips5139e502016-07-19 05:10:40 -07001271 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001272
robertphillips5139e502016-07-19 05:10:40 -07001273 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001274
1275 layer->fNext = fMCRec->fTopLayer;
1276 fMCRec->fLayer = layer;
1277 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001278
1279 if (rec.fBackdrop) {
1280 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice,
1281 fMCRec->fMatrix, this->getClipStack());
1282 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001283}
1284
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001285int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001286 if (0xFF == alpha) {
1287 return this->saveLayer(bounds, nullptr);
1288 } else {
1289 SkPaint tmpPaint;
1290 tmpPaint.setAlpha(alpha);
1291 return this->saveLayer(bounds, &tmpPaint);
1292 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001293}
1294
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295void SkCanvas::internalRestore() {
1296 SkASSERT(fMCStack.count() != 0);
1297
1298 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299
reed687fa1c2015-04-07 08:00:56 -07001300 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001301
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001302 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303 DeviceCM* layer = fMCRec->fLayer; // may be null
1304 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001305 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001306
1307 // now do the normal restore()
1308 fMCRec->~MCRec(); // balanced in save()
1309 fMCStack.pop_back();
1310 fMCRec = (MCRec*)fMCStack.back();
1311
1312 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1313 since if we're being recorded, we don't want to record this (the
1314 recorder will have already recorded the restore).
1315 */
bsalomon49f085d2014-09-05 13:34:00 -07001316 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001318 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001319 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001320 // restore what we smashed in internalSaveLayer
1321 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001322 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001324 delete layer;
reedb679ca82015-04-07 04:40:48 -07001325 } else {
1326 // we're at the root
reeda499f902015-05-01 09:34:31 -07001327 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001328 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001329 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001331 }
msarettfbfa2582016-08-12 08:29:08 -07001332
1333 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001334 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001335 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1336 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001337}
1338
reede8f30622016-03-23 18:59:25 -07001339sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001340 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001341 props = &fProps;
1342 }
1343 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001344}
1345
reede8f30622016-03-23 18:59:25 -07001346sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001347 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001348 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001349}
1350
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001351SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001352 return this->onImageInfo();
1353}
1354
1355SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001356 SkBaseDevice* dev = this->getDevice();
1357 if (dev) {
1358 return dev->imageInfo();
1359 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001360 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001361 }
1362}
1363
brianosman898235c2016-04-06 07:38:23 -07001364bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001365 return this->onGetProps(props);
1366}
1367
1368bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001369 SkBaseDevice* dev = this->getDevice();
1370 if (dev) {
1371 if (props) {
1372 *props = fProps;
1373 }
1374 return true;
1375 } else {
1376 return false;
1377 }
1378}
1379
reed6ceeebd2016-03-09 14:26:26 -08001380#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001381const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001382 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001383 if (this->peekPixels(&pmap)) {
1384 if (info) {
1385 *info = pmap.info();
1386 }
1387 if (rowBytes) {
1388 *rowBytes = pmap.rowBytes();
1389 }
1390 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001391 }
reed6ceeebd2016-03-09 14:26:26 -08001392 return nullptr;
1393}
1394#endif
1395
1396bool SkCanvas::peekPixels(SkPixmap* pmap) {
1397 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001398}
1399
reed884e97c2015-05-26 11:31:54 -07001400bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001401 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001402 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001403}
1404
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001405void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001406 SkPixmap pmap;
1407 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001408 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001409 }
1410 if (info) {
1411 *info = pmap.info();
1412 }
1413 if (rowBytes) {
1414 *rowBytes = pmap.rowBytes();
1415 }
1416 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001417 *origin = this->getTopDevice(false)->getOrigin();
1418 }
reed884e97c2015-05-26 11:31:54 -07001419 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001420}
1421
reed884e97c2015-05-26 11:31:54 -07001422bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001423 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001424 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001425}
1426
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428
reed7503d602016-07-15 14:23:29 -07001429void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001430 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001431 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001432 paint = &tmp;
1433 }
reed@google.com4b226022011-01-11 18:32:13 +00001434
reed@google.com8926b162012-03-23 15:36:36 +00001435 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001436
reed@android.com8a1c16f2008-12-17 15:59:43 +00001437 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001438 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001439 paint = &looper.paint();
1440 SkImageFilter* filter = paint->getImageFilter();
1441 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001442 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001443 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001444 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001445 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001446 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447 }
reeda2217ef2016-07-20 06:04:34 -07001448
reed@google.com4e2b3d32011-04-07 14:18:59 +00001449 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450}
1451
reed32704672015-12-16 08:27:10 -08001452/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001453
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001454void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001455 if (dx || dy) {
1456 this->checkForDeferredSave();
1457 fDeviceCMDirty = true;
1458 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001459
reedfe69b502016-09-12 06:31:48 -07001460 // Translate shouldn't affect the is-scale-translateness of the matrix.
1461 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001462
reedfe69b502016-09-12 06:31:48 -07001463 this->didTranslate(dx,dy);
1464 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001465}
1466
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001467void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001468 SkMatrix m;
1469 m.setScale(sx, sy);
1470 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471}
1472
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001473void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001474 SkMatrix m;
1475 m.setRotate(degrees);
1476 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001477}
1478
bungeman7438bfc2016-07-12 15:01:19 -07001479void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1480 SkMatrix m;
1481 m.setRotate(degrees, px, py);
1482 this->concat(m);
1483}
1484
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001485void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001486 SkMatrix m;
1487 m.setSkew(sx, sy);
1488 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001489}
1490
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001491void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001492 if (matrix.isIdentity()) {
1493 return;
1494 }
1495
reed2ff1fce2014-12-11 07:07:37 -08001496 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001497 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001498 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001499 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001500 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001501}
1502
reed8c30a812016-04-20 16:36:51 -07001503void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001504 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001505 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001506 fIsScaleTranslate = matrix.isScaleTranslate();
reed8c30a812016-04-20 16:36:51 -07001507}
1508
1509void SkCanvas::setMatrix(const SkMatrix& matrix) {
1510 this->checkForDeferredSave();
1511 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001512 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001513}
1514
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001516 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517}
1518
vjiaoblack95302da2016-07-21 10:25:54 -07001519#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001520void SkCanvas::translateZ(SkScalar z) {
1521 this->checkForDeferredSave();
1522 this->fMCRec->fCurDrawDepth += z;
1523 this->didTranslateZ(z);
1524}
1525
1526SkScalar SkCanvas::getZ() const {
1527 return this->fMCRec->fCurDrawDepth;
1528}
1529
vjiaoblack95302da2016-07-21 10:25:54 -07001530void SkCanvas::setLights(sk_sp<SkLights> lights) {
1531 this->fLights = lights;
1532}
1533
1534sk_sp<SkLights> SkCanvas::getLights() const {
1535 return this->fLights;
1536}
1537#endif
1538
reed@android.com8a1c16f2008-12-17 15:59:43 +00001539//////////////////////////////////////////////////////////////////////////////
1540
reed73603f32016-09-20 08:42:38 -07001541void SkCanvas::clipRect(const SkRect& rect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001542 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001543 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1544 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001545}
1546
reed73603f32016-09-20 08:42:38 -07001547void SkCanvas::onClipRect(const SkRect& rect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001548 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reedc64eff52015-11-21 12:39:45 -08001549 AutoValidateClip avc(this);
Brian Salomona3b45d42016-10-03 11:36:16 -04001550 fClipStack->clipRect(rect, fMCRec->fMatrix, op, isAA);
1551 fMCRec->fRasterClip.op(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1552 isAA);
reedc64eff52015-11-21 12:39:45 -08001553 fDeviceCMDirty = true;
msarettfbfa2582016-08-12 08:29:08 -07001554 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001555}
1556
reed73603f32016-09-20 08:42:38 -07001557void SkCanvas::clipRRect(const SkRRect& rrect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001558 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001559 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001560 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001561 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1562 } else {
1563 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001564 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001565}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001566
reed73603f32016-09-20 08:42:38 -07001567void SkCanvas::onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001568 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001569
Brian Salomona3b45d42016-10-03 11:36:16 -04001570 fDeviceCMDirty = true;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001571
Brian Salomona3b45d42016-10-03 11:36:16 -04001572 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1573 fClipStack->clipRRect(rrect, fMCRec->fMatrix, op, isAA);
1574 fMCRec->fRasterClip.op(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1575 isAA);
1576 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1577 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001578}
1579
reed73603f32016-09-20 08:42:38 -07001580void SkCanvas::clipPath(const SkPath& path, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001581 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001582 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001583
1584 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1585 SkRect r;
1586 if (path.isRect(&r)) {
1587 this->onClipRect(r, op, edgeStyle);
1588 return;
1589 }
1590 SkRRect rrect;
1591 if (path.isOval(&r)) {
1592 rrect.setOval(r);
1593 this->onClipRRect(rrect, op, edgeStyle);
1594 return;
1595 }
1596 if (path.isRRect(&rrect)) {
1597 this->onClipRRect(rrect, op, edgeStyle);
1598 return;
1599 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600 }
robertphillips39f05382015-11-24 09:30:12 -08001601
1602 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001603}
1604
reed73603f32016-09-20 08:42:38 -07001605void SkCanvas::onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001606 AutoValidateClip avc(this);
1607
reed@android.com8a1c16f2008-12-17 15:59:43 +00001608 fDeviceCMDirty = true;
Brian Salomona3b45d42016-10-03 11:36:16 -04001609 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001610
Brian Salomona3b45d42016-10-03 11:36:16 -04001611 fClipStack->clipPath(path, fMCRec->fMatrix, op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612
Brian Salomona3b45d42016-10-03 11:36:16 -04001613 const SkPath* rasterClipPath = &path;
1614 const SkMatrix* matrix = &fMCRec->fMatrix;
1615 SkPath tempPath;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001616 if (fAllowSimplifyClip) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001617 isAA = getClipStack()->asPath(&tempPath);
1618 rasterClipPath = &tempPath;
1619 matrix = &SkMatrix::I();
reed73603f32016-09-20 08:42:38 -07001620 op = kReplace_Op;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001621 }
Brian Salomona3b45d42016-10-03 11:36:16 -04001622 fMCRec->fRasterClip.op(*rasterClipPath, *matrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1623 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001624 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001625}
1626
reed73603f32016-09-20 08:42:38 -07001627void SkCanvas::clipRegion(const SkRegion& rgn, ClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001628 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001629 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001630}
1631
reed73603f32016-09-20 08:42:38 -07001632void SkCanvas::onClipRegion(const SkRegion& rgn, ClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001633 AutoValidateClip avc(this);
1634
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001636
reed@google.com5c3d1472011-02-22 19:12:23 +00001637 // todo: signal fClipStack that we have a region, and therefore (I guess)
1638 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001639 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001640
reed73603f32016-09-20 08:42:38 -07001641 fMCRec->fRasterClip.op(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001642 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001643}
1644
reed@google.com819c9212011-02-23 18:56:55 +00001645#ifdef SK_DEBUG
1646void SkCanvas::validateClip() const {
1647 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001648 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001649 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001650 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001651 return;
1652 }
1653
reed@google.com819c9212011-02-23 18:56:55 +00001654 SkIRect ir;
1655 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001656 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001657
reed687fa1c2015-04-07 08:00:56 -07001658 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001659 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001660 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001661 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001662 case SkClipStack::Element::kRect_Type:
1663 element->getRect().round(&ir);
reed73603f32016-09-20 08:42:38 -07001664 tmpClip.op(ir, (SkRegion::Op)element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001665 break;
1666 case SkClipStack::Element::kEmpty_Type:
1667 tmpClip.setEmpty();
1668 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001669 default: {
1670 SkPath path;
1671 element->asPath(&path);
Brian Salomona3b45d42016-10-03 11:36:16 -04001672 tmpClip.op(path, SkMatrix::I(), this->getTopLayerBounds(),
1673 (SkRegion::Op)element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001674 break;
1675 }
reed@google.com819c9212011-02-23 18:56:55 +00001676 }
1677 }
reed@google.com819c9212011-02-23 18:56:55 +00001678}
1679#endif
1680
reed@google.com90c07ea2012-04-13 13:50:27 +00001681void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001682 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001683 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001684
halcanary96fcdcc2015-08-27 07:41:13 -07001685 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001686 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001687 }
1688}
1689
reed@google.com5c3d1472011-02-22 19:12:23 +00001690///////////////////////////////////////////////////////////////////////////////
1691
reed@google.com754de5f2014-02-24 19:38:20 +00001692bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001693 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001694}
1695
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001696bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001697 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001698}
1699
msarettfbfa2582016-08-12 08:29:08 -07001700static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1701#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1702 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1703 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1704 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1705 return 0xF != _mm_movemask_ps(mask);
1706#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1707 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1708 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1709 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1710 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1711#else
1712 SkRect devRectAsRect;
1713 SkRect devClipAsRect;
1714 devRect.store(&devRectAsRect.fLeft);
1715 devClip.store(&devClipAsRect.fLeft);
1716 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1717#endif
1718}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001719
msarettfbfa2582016-08-12 08:29:08 -07001720// It's important for this function to not be inlined. Otherwise the compiler will share code
1721// between the fast path and the slow path, resulting in two slow paths.
1722static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1723 const SkMatrix& matrix) {
1724 SkRect deviceRect;
1725 matrix.mapRect(&deviceRect, src);
1726 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1727}
1728
1729bool SkCanvas::quickReject(const SkRect& src) const {
1730#ifdef SK_DEBUG
1731 // Verify that fDeviceClipBounds are set properly.
1732 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001733 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001734 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001735 } else {
msarettfbfa2582016-08-12 08:29:08 -07001736 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001737 }
msarettfbfa2582016-08-12 08:29:08 -07001738
msarett9637ea92016-08-18 14:03:30 -07001739 // Verify that fIsScaleTranslate is set properly.
1740 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001741#endif
1742
msarett9637ea92016-08-18 14:03:30 -07001743 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001744 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1745 }
1746
1747 // We inline the implementation of mapScaleTranslate() for the fast path.
1748 float sx = fMCRec->fMatrix.getScaleX();
1749 float sy = fMCRec->fMatrix.getScaleY();
1750 float tx = fMCRec->fMatrix.getTranslateX();
1751 float ty = fMCRec->fMatrix.getTranslateY();
1752 Sk4f scale(sx, sy, sx, sy);
1753 Sk4f trans(tx, ty, tx, ty);
1754
1755 // Apply matrix.
1756 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1757
1758 // Make sure left < right, top < bottom.
1759 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1760 Sk4f min = Sk4f::Min(ltrb, rblt);
1761 Sk4f max = Sk4f::Max(ltrb, rblt);
1762 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1763 // ARM this sequence generates the fastest (a single instruction).
1764 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1765
1766 // Check if the device rect is NaN or outside the clip.
1767 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768}
1769
reed@google.com3b3e8952012-08-16 20:53:31 +00001770bool SkCanvas::quickReject(const SkPath& path) const {
1771 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772}
1773
reed@google.com3b3e8952012-08-16 20:53:31 +00001774bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001775 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001776 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777 return false;
1778 }
1779
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001780 SkMatrix inverse;
1781 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001782 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001783 if (bounds) {
1784 bounds->setEmpty();
1785 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001786 return false;
1787 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788
bsalomon49f085d2014-09-05 13:34:00 -07001789 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001790 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001791 // adjust it outwards in case we are antialiasing
1792 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001793
reed@google.com8f4d2302013-12-17 16:44:46 +00001794 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1795 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796 inverse.mapRect(bounds, r);
1797 }
1798 return true;
1799}
1800
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001801bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001802 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001803 if (clip.isEmpty()) {
1804 if (bounds) {
1805 bounds->setEmpty();
1806 }
1807 return false;
1808 }
1809
bsalomon49f085d2014-09-05 13:34:00 -07001810 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001811 *bounds = clip.getBounds();
1812 }
1813 return true;
1814}
1815
reed@android.com8a1c16f2008-12-17 15:59:43 +00001816const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001817 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001818}
1819
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001820const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001821 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001822}
1823
robertphillips175dd9b2016-04-28 14:32:04 -07001824GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001825 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001826 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001827}
1828
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001829GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001830 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001831 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001832}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001833
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001834void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1835 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001836 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001837 if (outer.isEmpty()) {
1838 return;
1839 }
1840 if (inner.isEmpty()) {
1841 this->drawRRect(outer, paint);
1842 return;
1843 }
1844
1845 // We don't have this method (yet), but technically this is what we should
1846 // be able to assert...
1847 // SkASSERT(outer.contains(inner));
1848 //
1849 // For now at least check for containment of bounds
1850 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1851
1852 this->onDrawDRRect(outer, inner, paint);
1853}
1854
reed41af9662015-01-05 07:49:08 -08001855// These need to stop being virtual -- clients need to override the onDraw... versions
1856
1857void SkCanvas::drawPaint(const SkPaint& paint) {
1858 this->onDrawPaint(paint);
1859}
1860
1861void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1862 this->onDrawRect(r, paint);
1863}
1864
msarettdca352e2016-08-26 06:37:45 -07001865void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
1866 if (region.isEmpty()) {
1867 return;
1868 }
1869
1870 if (region.isRect()) {
1871 return this->drawIRect(region.getBounds(), paint);
1872 }
1873
1874 this->onDrawRegion(region, paint);
1875}
1876
reed41af9662015-01-05 07:49:08 -08001877void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1878 this->onDrawOval(r, paint);
1879}
1880
1881void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1882 this->onDrawRRect(rrect, paint);
1883}
1884
1885void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1886 this->onDrawPoints(mode, count, pts, paint);
1887}
1888
1889void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1890 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1891 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1892 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1893 indices, indexCount, paint);
1894}
1895
1896void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1897 this->onDrawPath(path, paint);
1898}
1899
reeda85d4d02015-05-06 12:56:48 -07001900void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001901 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001902 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001903}
1904
reede47829b2015-08-06 10:02:53 -07001905void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1906 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001907 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001908 if (dst.isEmpty() || src.isEmpty()) {
1909 return;
1910 }
1911 this->onDrawImageRect(image, &src, dst, paint, constraint);
1912}
reed41af9662015-01-05 07:49:08 -08001913
reed84984ef2015-07-17 07:09:43 -07001914void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1915 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001916 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001917 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001918}
1919
reede47829b2015-08-06 10:02:53 -07001920void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1921 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001922 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001923 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1924 constraint);
1925}
reede47829b2015-08-06 10:02:53 -07001926
reed4c21dc52015-06-25 12:32:03 -07001927void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1928 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001929 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001930 if (dst.isEmpty()) {
1931 return;
1932 }
msarett552bca92016-08-03 06:53:26 -07001933 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1934 this->onDrawImageNine(image, center, dst, paint);
1935 } else {
reede47829b2015-08-06 10:02:53 -07001936 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001937 }
reed4c21dc52015-06-25 12:32:03 -07001938}
1939
msarett16882062016-08-16 09:31:08 -07001940void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1941 const SkPaint* paint) {
1942 RETURN_ON_NULL(image);
1943 if (dst.isEmpty()) {
1944 return;
1945 }
msarett71df2d72016-09-30 12:41:42 -07001946
1947 SkIRect bounds;
1948 Lattice latticePlusBounds = lattice;
1949 if (!latticePlusBounds.fBounds) {
1950 bounds = SkIRect::MakeWH(image->width(), image->height());
1951 latticePlusBounds.fBounds = &bounds;
1952 }
1953
1954 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
1955 this->onDrawImageLattice(image, latticePlusBounds, dst, paint);
msarett16882062016-08-16 09:31:08 -07001956 } else {
1957 this->drawImageRect(image, dst, paint);
1958 }
1959}
1960
reed41af9662015-01-05 07:49:08 -08001961void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001962 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001963 return;
1964 }
reed41af9662015-01-05 07:49:08 -08001965 this->onDrawBitmap(bitmap, dx, dy, paint);
1966}
1967
reede47829b2015-08-06 10:02:53 -07001968void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001969 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001970 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001971 return;
1972 }
reede47829b2015-08-06 10:02:53 -07001973 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001974}
1975
reed84984ef2015-07-17 07:09:43 -07001976void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1977 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001978 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001979}
1980
reede47829b2015-08-06 10:02:53 -07001981void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1982 SrcRectConstraint constraint) {
1983 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1984 constraint);
1985}
reede47829b2015-08-06 10:02:53 -07001986
reed41af9662015-01-05 07:49:08 -08001987void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1988 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001989 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001990 return;
1991 }
msarett552bca92016-08-03 06:53:26 -07001992 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
1993 this->onDrawBitmapNine(bitmap, center, dst, paint);
1994 } else {
reeda5517e22015-07-14 10:54:12 -07001995 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001996 }
reed41af9662015-01-05 07:49:08 -08001997}
1998
msarettc573a402016-08-02 08:05:56 -07001999void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2000 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07002001 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002002 return;
2003 }
msarett71df2d72016-09-30 12:41:42 -07002004
2005 SkIRect bounds;
2006 Lattice latticePlusBounds = lattice;
2007 if (!latticePlusBounds.fBounds) {
2008 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2009 latticePlusBounds.fBounds = &bounds;
2010 }
2011
2012 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
2013 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, paint);
msarett552bca92016-08-03 06:53:26 -07002014 } else {
msarett16882062016-08-16 09:31:08 -07002015 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002016 }
msarettc573a402016-08-02 08:05:56 -07002017}
2018
reed71c3c762015-06-24 10:29:17 -07002019void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2020 const SkColor colors[], int count, SkXfermode::Mode mode,
2021 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002022 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002023 if (count <= 0) {
2024 return;
2025 }
2026 SkASSERT(atlas);
2027 SkASSERT(xform);
2028 SkASSERT(tex);
2029 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2030}
2031
reedf70b5312016-03-04 16:36:20 -08002032void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2033 if (key) {
2034 this->onDrawAnnotation(rect, key, value);
2035 }
2036}
2037
reede47829b2015-08-06 10:02:53 -07002038void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2039 const SkPaint* paint, SrcRectConstraint constraint) {
2040 if (src) {
2041 this->drawImageRect(image, *src, dst, paint, constraint);
2042 } else {
2043 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2044 dst, paint, constraint);
2045 }
2046}
2047void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2048 const SkPaint* paint, SrcRectConstraint constraint) {
2049 if (src) {
2050 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2051 } else {
2052 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2053 dst, paint, constraint);
2054 }
2055}
2056
tomhudsoncb3bd182016-05-18 07:24:16 -07002057void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2058 SkIRect layer_bounds = this->getTopLayerBounds();
2059 if (matrix) {
2060 *matrix = this->getTotalMatrix();
2061 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2062 }
2063 if (clip_bounds) {
2064 this->getClipDeviceBounds(clip_bounds);
2065 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2066 }
2067}
2068
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069//////////////////////////////////////////////////////////////////////////////
2070// These are the virtual drawing methods
2071//////////////////////////////////////////////////////////////////////////////
2072
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002073void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002074 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002075 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2076 }
2077}
2078
reed41af9662015-01-05 07:49:08 -08002079void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002080 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002081 this->internalDrawPaint(paint);
2082}
2083
2084void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002085 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086
2087 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002088 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 }
2090
reed@google.com4e2b3d32011-04-07 14:18:59 +00002091 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092}
2093
reed41af9662015-01-05 07:49:08 -08002094void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2095 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002096 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097 if ((long)count <= 0) {
2098 return;
2099 }
2100
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002101 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002102 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002103 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002104 // special-case 2 points (common for drawing a single line)
2105 if (2 == count) {
2106 r.set(pts[0], pts[1]);
2107 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002108 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002109 }
senorblanco87e066e2015-10-28 11:23:36 -07002110 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2111 return;
2112 }
2113 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002114 }
reed@google.coma584aed2012-05-16 14:06:02 +00002115
halcanary96fcdcc2015-08-27 07:41:13 -07002116 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002118 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002119
reed@android.com8a1c16f2008-12-17 15:59:43 +00002120 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002121 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122 }
reed@google.com4b226022011-01-11 18:32:13 +00002123
reed@google.com4e2b3d32011-04-07 14:18:59 +00002124 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002125}
2126
reed4a167172016-08-18 17:15:25 -07002127static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
2128 return ((intptr_t)paint.getImageFilter() |
2129#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
2130 (intptr_t)canvas->getDrawFilter() |
2131#endif
2132 (intptr_t)paint.getLooper() ) != 0;
2133}
2134
reed41af9662015-01-05 07:49:08 -08002135void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002136 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002137 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002138 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002139 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002140 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2141 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2142 SkRect tmp(r);
2143 tmp.sort();
2144
senorblanco87e066e2015-10-28 11:23:36 -07002145 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2146 return;
2147 }
2148 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002149 }
reed@google.com4b226022011-01-11 18:32:13 +00002150
reed4a167172016-08-18 17:15:25 -07002151 if (needs_autodrawlooper(this, paint)) {
2152 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002153
reed4a167172016-08-18 17:15:25 -07002154 while (iter.next()) {
2155 iter.fDevice->drawRect(iter, r, looper.paint());
2156 }
2157
2158 LOOPER_END
2159 } else {
2160 this->predrawNotify(bounds, &paint, false);
2161 SkDrawIter iter(this);
2162 while (iter.next()) {
2163 iter.fDevice->drawRect(iter, r, paint);
2164 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002165 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002166}
2167
msarett44df6512016-08-25 13:54:30 -07002168void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
2169 SkRect storage;
2170 SkRect regionRect = SkRect::Make(region.getBounds());
2171 const SkRect* bounds = nullptr;
2172 if (paint.canComputeFastBounds()) {
2173 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2174 return;
2175 }
2176 bounds = &regionRect;
2177 }
2178
2179 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
2180
2181 while (iter.next()) {
2182 iter.fDevice->drawRegion(iter, region, looper.paint());
2183 }
2184
2185 LOOPER_END
2186}
2187
reed41af9662015-01-05 07:49:08 -08002188void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002189 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002190 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002191 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002192 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002193 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2194 return;
2195 }
2196 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002197 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002198
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002199 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002200
2201 while (iter.next()) {
2202 iter.fDevice->drawOval(iter, oval, looper.paint());
2203 }
2204
2205 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002206}
2207
bsalomonac3aa242016-08-19 11:25:19 -07002208void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2209 SkScalar sweepAngle, bool useCenter,
2210 const SkPaint& paint) {
2211 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
2212 const SkRect* bounds = nullptr;
2213 if (paint.canComputeFastBounds()) {
2214 SkRect storage;
2215 // Note we're using the entire oval as the bounds.
2216 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2217 return;
2218 }
2219 bounds = &oval;
2220 }
2221
2222 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
2223
2224 while (iter.next()) {
2225 iter.fDevice->drawArc(iter, oval, startAngle, sweepAngle, useCenter, looper.paint());
2226 }
2227
2228 LOOPER_END
2229}
2230
reed41af9662015-01-05 07:49:08 -08002231void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002232 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002233 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002234 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002235 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002236 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2237 return;
2238 }
2239 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002240 }
2241
2242 if (rrect.isRect()) {
2243 // call the non-virtual version
2244 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002245 return;
2246 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002247 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002248 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2249 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002250 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002251
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002252 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002253
2254 while (iter.next()) {
2255 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2256 }
2257
2258 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002259}
2260
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002261void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2262 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002263 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002264 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002265 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002266 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2267 return;
2268 }
2269 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002270 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002271
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002272 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002273
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002274 while (iter.next()) {
2275 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2276 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002277
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002278 LOOPER_END
2279}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002280
reed41af9662015-01-05 07:49:08 -08002281void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002282 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002283 if (!path.isFinite()) {
2284 return;
2285 }
2286
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002287 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002288 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002289 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002290 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002291 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2292 return;
2293 }
2294 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002295 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002296
2297 const SkRect& r = path.getBounds();
2298 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002299 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002300 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002301 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002302 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002303 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002304
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002305 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306
2307 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002308 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309 }
2310
reed@google.com4e2b3d32011-04-07 14:18:59 +00002311 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002312}
2313
reed262a71b2015-12-05 13:07:27 -08002314bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002315 if (!paint.getImageFilter()) {
2316 return false;
2317 }
2318
2319 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002320 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002321 return false;
2322 }
2323
2324 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2325 // Once we can filter and the filter will return a result larger than itself, we should be
2326 // able to remove this constraint.
2327 // skbug.com/4526
2328 //
2329 SkPoint pt;
2330 ctm.mapXY(x, y, &pt);
2331 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2332 return ir.contains(fMCRec->fRasterClip.getBounds());
2333}
2334
reeda85d4d02015-05-06 12:56:48 -07002335void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002336 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002337 SkRect bounds = SkRect::MakeXYWH(x, y,
2338 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002339 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002340 SkRect tmp = bounds;
2341 if (paint) {
2342 paint->computeFastBounds(tmp, &tmp);
2343 }
2344 if (this->quickReject(tmp)) {
2345 return;
2346 }
reeda85d4d02015-05-06 12:56:48 -07002347 }
halcanary9d524f22016-03-29 09:03:52 -07002348
reeda85d4d02015-05-06 12:56:48 -07002349 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002350 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002351 paint = lazy.init();
2352 }
reed262a71b2015-12-05 13:07:27 -08002353
reeda2217ef2016-07-20 06:04:34 -07002354 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002355 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2356 *paint);
2357 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002358 special = this->getDevice()->makeSpecial(image);
2359 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002360 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002361 }
2362 }
2363
reed262a71b2015-12-05 13:07:27 -08002364 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2365
reeda85d4d02015-05-06 12:56:48 -07002366 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002367 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002368 if (special) {
2369 SkPoint pt;
2370 iter.fMatrix->mapXY(x, y, &pt);
2371 iter.fDevice->drawSpecial(iter, special.get(),
2372 SkScalarRoundToInt(pt.fX),
2373 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002374 } else {
2375 iter.fDevice->drawImage(iter, image, x, y, pnt);
2376 }
reeda85d4d02015-05-06 12:56:48 -07002377 }
halcanary9d524f22016-03-29 09:03:52 -07002378
reeda85d4d02015-05-06 12:56:48 -07002379 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002380}
2381
reed41af9662015-01-05 07:49:08 -08002382void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002383 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002384 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002385 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002386 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002387 if (paint) {
2388 paint->computeFastBounds(dst, &storage);
2389 }
2390 if (this->quickReject(storage)) {
2391 return;
2392 }
reeda85d4d02015-05-06 12:56:48 -07002393 }
2394 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002395 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002396 paint = lazy.init();
2397 }
halcanary9d524f22016-03-29 09:03:52 -07002398
senorblancoc41e7e12015-12-07 12:51:30 -08002399 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002400 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002401
reeda85d4d02015-05-06 12:56:48 -07002402 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002403 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002404 }
halcanary9d524f22016-03-29 09:03:52 -07002405
reeda85d4d02015-05-06 12:56:48 -07002406 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002407}
2408
reed41af9662015-01-05 07:49:08 -08002409void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002410 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002411 SkDEBUGCODE(bitmap.validate();)
2412
reed33366972015-10-08 09:22:02 -07002413 if (bitmap.drawsNothing()) {
2414 return;
2415 }
2416
2417 SkLazyPaint lazy;
2418 if (nullptr == paint) {
2419 paint = lazy.init();
2420 }
2421
2422 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2423
2424 SkRect storage;
2425 const SkRect* bounds = nullptr;
2426 if (paint->canComputeFastBounds()) {
2427 bitmap.getBounds(&storage);
2428 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002429 SkRect tmp = storage;
2430 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2431 return;
2432 }
2433 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002434 }
reed@google.com4b226022011-01-11 18:32:13 +00002435
reeda2217ef2016-07-20 06:04:34 -07002436 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002437 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2438 *paint);
2439 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002440 special = this->getDevice()->makeSpecial(bitmap);
2441 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002442 drawAsSprite = false;
2443 }
2444 }
2445
reed262a71b2015-12-05 13:07:27 -08002446 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002447
2448 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002449 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002450 if (special) {
reed262a71b2015-12-05 13:07:27 -08002451 SkPoint pt;
2452 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002453 iter.fDevice->drawSpecial(iter, special.get(),
2454 SkScalarRoundToInt(pt.fX),
2455 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002456 } else {
2457 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2458 }
reed33366972015-10-08 09:22:02 -07002459 }
msarettfbfa2582016-08-12 08:29:08 -07002460
reed33366972015-10-08 09:22:02 -07002461 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462}
2463
reed@google.com9987ec32011-09-07 11:57:52 +00002464// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002465void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002466 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002467 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002468 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002469 return;
2470 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002471
halcanary96fcdcc2015-08-27 07:41:13 -07002472 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002473 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002474 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2475 return;
2476 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002477 }
reed@google.com3d608122011-11-21 15:16:16 +00002478
reed@google.com33535f32012-09-25 15:37:50 +00002479 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002480 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002481 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002482 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002483
senorblancoc41e7e12015-12-07 12:51:30 -08002484 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002485 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002486
reed@google.com33535f32012-09-25 15:37:50 +00002487 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002488 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002489 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002490
reed@google.com33535f32012-09-25 15:37:50 +00002491 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002492}
2493
reed41af9662015-01-05 07:49:08 -08002494void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002495 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002496 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002497 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002498 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002499}
2500
reed4c21dc52015-06-25 12:32:03 -07002501void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2502 const SkPaint* paint) {
2503 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002504
halcanary96fcdcc2015-08-27 07:41:13 -07002505 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002506 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002507 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2508 return;
2509 }
reed@google.com3d608122011-11-21 15:16:16 +00002510 }
halcanary9d524f22016-03-29 09:03:52 -07002511
reed4c21dc52015-06-25 12:32:03 -07002512 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002513 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002514 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002515 }
halcanary9d524f22016-03-29 09:03:52 -07002516
senorblancoc41e7e12015-12-07 12:51:30 -08002517 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002518
reed4c21dc52015-06-25 12:32:03 -07002519 while (iter.next()) {
2520 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002521 }
halcanary9d524f22016-03-29 09:03:52 -07002522
reed4c21dc52015-06-25 12:32:03 -07002523 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002524}
2525
reed41af9662015-01-05 07:49:08 -08002526void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2527 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002528 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002529 SkDEBUGCODE(bitmap.validate();)
2530
halcanary96fcdcc2015-08-27 07:41:13 -07002531 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002532 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002533 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2534 return;
2535 }
reed4c21dc52015-06-25 12:32:03 -07002536 }
halcanary9d524f22016-03-29 09:03:52 -07002537
reed4c21dc52015-06-25 12:32:03 -07002538 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002539 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002540 paint = lazy.init();
2541 }
halcanary9d524f22016-03-29 09:03:52 -07002542
senorblancoc41e7e12015-12-07 12:51:30 -08002543 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002544
reed4c21dc52015-06-25 12:32:03 -07002545 while (iter.next()) {
2546 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2547 }
halcanary9d524f22016-03-29 09:03:52 -07002548
reed4c21dc52015-06-25 12:32:03 -07002549 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002550}
2551
msarett16882062016-08-16 09:31:08 -07002552void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2553 const SkPaint* paint) {
2554 if (nullptr == paint || paint->canComputeFastBounds()) {
2555 SkRect storage;
2556 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2557 return;
2558 }
2559 }
2560
2561 SkLazyPaint lazy;
2562 if (nullptr == paint) {
2563 paint = lazy.init();
2564 }
2565
2566 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2567
2568 while (iter.next()) {
2569 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2570 }
2571
2572 LOOPER_END
2573}
2574
2575void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2576 const SkRect& dst, const SkPaint* paint) {
2577 if (nullptr == paint || paint->canComputeFastBounds()) {
2578 SkRect storage;
2579 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2580 return;
2581 }
2582 }
2583
2584 SkLazyPaint lazy;
2585 if (nullptr == paint) {
2586 paint = lazy.init();
2587 }
2588
2589 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2590
2591 while (iter.next()) {
2592 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2593 }
2594
2595 LOOPER_END
2596}
2597
reed@google.comf67e4cf2011-03-15 20:56:58 +00002598class SkDeviceFilteredPaint {
2599public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002600 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002601 uint32_t filteredFlags = device->filterTextFlags(paint);
2602 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002603 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002604 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002605 fPaint = newPaint;
2606 } else {
2607 fPaint = &paint;
2608 }
2609 }
2610
reed@google.comf67e4cf2011-03-15 20:56:58 +00002611 const SkPaint& paint() const { return *fPaint; }
2612
2613private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002614 const SkPaint* fPaint;
2615 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002616};
2617
bungeman@google.com52c748b2011-08-22 21:30:43 +00002618void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2619 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002620 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002621 draw.fDevice->drawRect(draw, r, paint);
2622 } else {
2623 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002624 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002625 draw.fDevice->drawRect(draw, r, p);
2626 }
2627}
2628
2629void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2630 const char text[], size_t byteLength,
2631 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002632 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002633
2634 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002635 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002636 draw.fRC->isEmpty() ||
Mike Reedce02e712016-10-03 18:02:50 +00002637 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002638 return;
2639 }
2640
2641 SkScalar width = 0;
2642 SkPoint start;
2643
2644 start.set(0, 0); // to avoid warning
2645 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2646 SkPaint::kStrikeThruText_Flag)) {
2647 width = paint.measureText(text, byteLength);
2648
2649 SkScalar offsetX = 0;
2650 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2651 offsetX = SkScalarHalf(width);
2652 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2653 offsetX = width;
2654 }
2655 start.set(x - offsetX, y);
2656 }
2657
2658 if (0 == width) {
2659 return;
2660 }
2661
2662 uint32_t flags = paint.getFlags();
2663
2664 if (flags & (SkPaint::kUnderlineText_Flag |
2665 SkPaint::kStrikeThruText_Flag)) {
2666 SkScalar textSize = paint.getTextSize();
2667 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2668 SkRect r;
2669
2670 r.fLeft = start.fX;
2671 r.fRight = start.fX + width;
2672
2673 if (flags & SkPaint::kUnderlineText_Flag) {
2674 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2675 start.fY);
2676 r.fTop = offset;
2677 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002678 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002679 }
2680 if (flags & SkPaint::kStrikeThruText_Flag) {
2681 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2682 start.fY);
2683 r.fTop = offset;
2684 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002685 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002686 }
2687 }
2688}
2689
reed@google.come0d9ce82014-04-23 04:00:17 +00002690void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2691 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002692 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002693
2694 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002695 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002696 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002697 DrawTextDecorations(iter, dfp.paint(),
2698 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002699 }
2700
reed@google.com4e2b3d32011-04-07 14:18:59 +00002701 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002702}
2703
reed@google.come0d9ce82014-04-23 04:00:17 +00002704void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2705 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002706 SkPoint textOffset = SkPoint::Make(0, 0);
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, &pos->fX, 2, 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::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2720 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002721
2722 SkPoint textOffset = SkPoint::Make(0, constY);
2723
halcanary96fcdcc2015-08-27 07:41:13 -07002724 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002725
reed@android.com8a1c16f2008-12-17 15:59:43 +00002726 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002727 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002728 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002729 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002730 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002731
reed@google.com4e2b3d32011-04-07 14:18:59 +00002732 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002733}
2734
reed@google.come0d9ce82014-04-23 04:00:17 +00002735void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2736 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002737 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002738
reed@android.com8a1c16f2008-12-17 15:59:43 +00002739 while (iter.next()) {
2740 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002741 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002742 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002743
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002744 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002745}
2746
reed45561a02016-07-07 12:47:17 -07002747void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2748 const SkRect* cullRect, const SkPaint& paint) {
2749 if (cullRect && this->quickReject(*cullRect)) {
2750 return;
2751 }
2752
2753 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2754
2755 while (iter.next()) {
2756 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2757 }
2758
2759 LOOPER_END
2760}
2761
fmalita00d5c2c2014-08-21 08:53:26 -07002762void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2763 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002764
fmalita85d5eb92015-03-04 11:20:12 -08002765 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002766 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002767 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002768 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002769 SkRect tmp;
2770 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2771 return;
2772 }
2773 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002774 }
2775
fmalita024f9962015-03-03 19:08:17 -08002776 // We cannot filter in the looper as we normally do, because the paint is
2777 // incomplete at this point (text-related attributes are embedded within blob run paints).
2778 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002779 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002780
fmalita85d5eb92015-03-04 11:20:12 -08002781 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002782
fmalitaaa1b9122014-08-28 14:32:24 -07002783 while (iter.next()) {
2784 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002785 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002786 }
2787
fmalitaaa1b9122014-08-28 14:32:24 -07002788 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002789
2790 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002791}
2792
reed@google.come0d9ce82014-04-23 04:00:17 +00002793// These will become non-virtual, so they always call the (virtual) onDraw... method
2794void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2795 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002796 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002797 if (byteLength) {
2798 this->onDrawText(text, byteLength, x, y, paint);
2799 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002800}
2801void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2802 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002803 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002804 if (byteLength) {
2805 this->onDrawPosText(text, byteLength, pos, paint);
2806 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002807}
2808void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2809 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002810 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002811 if (byteLength) {
2812 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2813 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002814}
2815void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2816 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002817 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002818 if (byteLength) {
2819 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2820 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002821}
reed45561a02016-07-07 12:47:17 -07002822void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2823 const SkRect* cullRect, const SkPaint& paint) {
2824 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2825 if (byteLength) {
2826 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2827 }
2828}
fmalita00d5c2c2014-08-21 08:53:26 -07002829void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2830 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002831 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002832 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002833 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002834}
reed@google.come0d9ce82014-04-23 04:00:17 +00002835
reed41af9662015-01-05 07:49:08 -08002836void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2837 const SkPoint verts[], const SkPoint texs[],
2838 const SkColor colors[], SkXfermode* xmode,
2839 const uint16_t indices[], int indexCount,
2840 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002841 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002842 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002843
reed@android.com8a1c16f2008-12-17 15:59:43 +00002844 while (iter.next()) {
2845 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002846 colors, xmode, indices, indexCount,
2847 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002848 }
reed@google.com4b226022011-01-11 18:32:13 +00002849
reed@google.com4e2b3d32011-04-07 14:18:59 +00002850 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002851}
2852
dandovb3c9d1c2014-08-12 08:34:29 -07002853void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2854 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002855 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002856 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002857 return;
2858 }
mtklein6cfa73a2014-08-13 13:33:49 -07002859
msarett9340c262016-09-22 05:20:21 -07002860 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2861}
2862
2863void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2864 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002865 // Since a patch is always within the convex hull of the control points, we discard it when its
2866 // bounding rectangle is completely outside the current clip.
2867 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002868 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002869 if (this->quickReject(bounds)) {
2870 return;
2871 }
mtklein6cfa73a2014-08-13 13:33:49 -07002872
halcanary96fcdcc2015-08-27 07:41:13 -07002873 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002874
dandovecfff212014-08-04 10:02:00 -07002875 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002876 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002877 }
mtklein6cfa73a2014-08-13 13:33:49 -07002878
dandovecfff212014-08-04 10:02:00 -07002879 LOOPER_END
2880}
2881
reeda8db7282015-07-07 10:22:31 -07002882void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002883 RETURN_ON_NULL(dr);
2884 if (x || y) {
2885 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2886 this->onDrawDrawable(dr, &matrix);
2887 } else {
2888 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002889 }
2890}
2891
reeda8db7282015-07-07 10:22:31 -07002892void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002893 RETURN_ON_NULL(dr);
2894 if (matrix && matrix->isIdentity()) {
2895 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002896 }
reede3b38ce2016-01-08 09:18:44 -08002897 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002898}
2899
2900void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002901 // drawable bounds are no longer reliable (e.g. android displaylist)
2902 // so don't use them for quick-reject
reeda8db7282015-07-07 10:22:31 -07002903 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002904}
2905
reed71c3c762015-06-24 10:29:17 -07002906void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2907 const SkColor colors[], int count, SkXfermode::Mode mode,
2908 const SkRect* cull, const SkPaint* paint) {
2909 if (cull && this->quickReject(*cull)) {
2910 return;
2911 }
2912
2913 SkPaint pnt;
2914 if (paint) {
2915 pnt = *paint;
2916 }
halcanary9d524f22016-03-29 09:03:52 -07002917
halcanary96fcdcc2015-08-27 07:41:13 -07002918 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002919 while (iter.next()) {
2920 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2921 }
2922 LOOPER_END
2923}
2924
reedf70b5312016-03-04 16:36:20 -08002925void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2926 SkASSERT(key);
2927
2928 SkPaint paint;
2929 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2930 while (iter.next()) {
2931 iter.fDevice->drawAnnotation(iter, rect, key, value);
2932 }
2933 LOOPER_END
2934}
2935
reed@android.com8a1c16f2008-12-17 15:59:43 +00002936//////////////////////////////////////////////////////////////////////////////
2937// These methods are NOT virtual, and therefore must call back into virtual
2938// methods, rather than actually drawing themselves.
2939//////////////////////////////////////////////////////////////////////////////
2940
Mike Reedce02e712016-10-03 18:02:50 +00002941void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
2942 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002943 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002944 SkPaint paint;
2945
2946 paint.setARGB(a, r, g, b);
Mike Reedce02e712016-10-03 18:02:50 +00002947 if (SkXfermode::kSrcOver_Mode != mode) {
2948 paint.setXfermodeMode(mode);
2949 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002950 this->drawPaint(paint);
2951}
2952
Mike Reedce02e712016-10-03 18:02:50 +00002953void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002954 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002955 SkPaint paint;
2956
2957 paint.setColor(c);
Mike Reedce02e712016-10-03 18:02:50 +00002958 if (SkXfermode::kSrcOver_Mode != mode) {
2959 paint.setXfermodeMode(mode);
2960 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002961 this->drawPaint(paint);
2962}
2963
2964void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002965 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002966 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002967
reed@android.com8a1c16f2008-12-17 15:59:43 +00002968 pt.set(x, y);
2969 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2970}
2971
2972void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002973 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002974 SkPoint pt;
2975 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002976
reed@android.com8a1c16f2008-12-17 15:59:43 +00002977 pt.set(x, y);
2978 paint.setColor(color);
2979 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2980}
2981
2982void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2983 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002984 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002985 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002986
reed@android.com8a1c16f2008-12-17 15:59:43 +00002987 pts[0].set(x0, y0);
2988 pts[1].set(x1, y1);
2989 this->drawPoints(kLines_PointMode, 2, pts, paint);
2990}
2991
2992void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2993 SkScalar right, SkScalar bottom,
2994 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002995 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002996 SkRect r;
2997
2998 r.set(left, top, right, bottom);
2999 this->drawRect(r, paint);
3000}
3001
3002void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
3003 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003004 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003005 if (radius < 0) {
3006 radius = 0;
3007 }
3008
3009 SkRect r;
3010 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003011 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003012}
3013
3014void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3015 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003016 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003017 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003018 SkRRect rrect;
3019 rrect.setRectXY(r, rx, ry);
3020 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003021 } else {
3022 this->drawRect(r, paint);
3023 }
3024}
3025
reed@android.com8a1c16f2008-12-17 15:59:43 +00003026void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3027 SkScalar sweepAngle, bool useCenter,
3028 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003029 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
bsalomon21af9ca2016-08-25 12:29:23 -07003030 if (oval.isEmpty() || !sweepAngle) {
3031 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003032 }
bsalomon21af9ca2016-08-25 12:29:23 -07003033 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003034}
3035
3036void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3037 const SkPath& path, SkScalar hOffset,
3038 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003039 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003040 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003041
reed@android.com8a1c16f2008-12-17 15:59:43 +00003042 matrix.setTranslate(hOffset, vOffset);
3043 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3044}
3045
reed@android.comf76bacf2009-05-13 14:00:33 +00003046///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003047
3048/**
3049 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3050 * against the playback cost of recursing into the subpicture to get at its actual ops.
3051 *
3052 * For now we pick a conservatively small value, though measurement (and other heuristics like
3053 * the type of ops contained) may justify changing this value.
3054 */
3055#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003056
reedd5fa1a42014-08-09 11:08:05 -07003057void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003058 RETURN_ON_NULL(picture);
3059
reed1c2c4412015-04-30 13:09:24 -07003060 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003061 if (matrix && matrix->isIdentity()) {
3062 matrix = nullptr;
3063 }
3064 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3065 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3066 picture->playback(this);
3067 } else {
3068 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003069 }
3070}
robertphillips9b14f262014-06-04 05:40:44 -07003071
reedd5fa1a42014-08-09 11:08:05 -07003072void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3073 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003074 if (!paint || paint->canComputeFastBounds()) {
3075 SkRect bounds = picture->cullRect();
3076 if (paint) {
3077 paint->computeFastBounds(bounds, &bounds);
3078 }
3079 if (matrix) {
3080 matrix->mapRect(&bounds);
3081 }
3082 if (this->quickReject(bounds)) {
3083 return;
3084 }
3085 }
3086
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003087 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003088 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003089}
3090
vjiaoblack95302da2016-07-21 10:25:54 -07003091#ifdef SK_EXPERIMENTAL_SHADOWING
3092void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3093 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003094 const SkPaint* paint,
3095 const SkShadowParams& params) {
vjiaoblack95302da2016-07-21 10:25:54 -07003096 RETURN_ON_NULL(picture);
3097
3098 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3099
vjiaoblacke6f5d562016-08-25 06:30:23 -07003100 this->onDrawShadowedPicture(picture, matrix, paint, params);
vjiaoblack95302da2016-07-21 10:25:54 -07003101}
3102
3103void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3104 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003105 const SkPaint* paint,
3106 const SkShadowParams& params) {
vjiaoblack904527d2016-08-09 09:32:09 -07003107 if (!paint || paint->canComputeFastBounds()) {
3108 SkRect bounds = picture->cullRect();
3109 if (paint) {
3110 paint->computeFastBounds(bounds, &bounds);
3111 }
3112 if (matrix) {
3113 matrix->mapRect(&bounds);
3114 }
3115 if (this->quickReject(bounds)) {
3116 return;
3117 }
3118 }
3119
3120 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3121
vjiaoblacke6f5d562016-08-25 06:30:23 -07003122 sk_sp<SkImage> povDepthMap;
3123 sk_sp<SkImage> diffuseMap;
3124
vjiaoblack904527d2016-08-09 09:32:09 -07003125 // povDepthMap
3126 {
3127 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003128 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3129 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003130 sk_sp<SkLights> povLight = builder.finish();
3131
3132 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3133 picture->cullRect().height(),
3134 kBGRA_8888_SkColorType,
3135 kOpaque_SkAlphaType);
3136
3137 // Create a new surface (that matches the backend of canvas)
3138 // to create the povDepthMap
3139 sk_sp<SkSurface> surf(this->makeSurface(info));
3140
3141 // Wrap another SPFCanvas around the surface
3142 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3143 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3144
3145 // set the depth map canvas to have the light as the user's POV
3146 depthMapCanvas->setLights(std::move(povLight));
3147
3148 depthMapCanvas->drawPicture(picture);
vjiaoblack904527d2016-08-09 09:32:09 -07003149 povDepthMap = surf->makeImageSnapshot();
3150 }
3151
3152 // diffuseMap
3153 {
3154 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3155 picture->cullRect().height(),
3156 kBGRA_8888_SkColorType,
3157 kOpaque_SkAlphaType);
3158
3159 sk_sp<SkSurface> surf(this->makeSurface(info));
3160 surf->getCanvas()->drawPicture(picture);
3161
3162 diffuseMap = surf->makeImageSnapshot();
3163 }
vjiaoblack904527d2016-08-09 09:32:09 -07003164
3165 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3166 SkShader::kClamp_TileMode);
vjiaoblack904527d2016-08-09 09:32:09 -07003167 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3168 SkShader::kClamp_TileMode);
vjiaoblackb2796fd2016-09-09 09:22:39 -07003169
3170 // TODO: pass the depth to the shader in vertices, or uniforms
3171 // so we don't have to render depth and color separately
3172 for (int i = 0; i < fLights->numLights(); ++i) {
3173 // skip over ambient lights; they don't cast shadows
3174 // lights that have shadow maps do not need updating (because lights are immutable)
3175 sk_sp<SkImage> depthMap;
3176 SkISize shMapSize;
3177
3178 if (fLights->light(i).getShadowMap() != nullptr) {
3179 continue;
3180 }
3181
3182 if (fLights->light(i).isRadial()) {
3183 shMapSize.fHeight = 1;
3184 shMapSize.fWidth = (int) picture->cullRect().width();
3185
3186 SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1,
3187 kBGRA_8888_SkColorType,
3188 kOpaque_SkAlphaType);
3189
3190 // Create new surface (that matches the backend of canvas)
3191 // for each shadow map
3192 sk_sp<SkSurface> surf(this->makeSurface(info));
3193
3194 // Wrap another SPFCanvas around the surface
3195 SkCanvas* depthMapCanvas = surf->getCanvas();
3196
3197 SkLights::Builder builder;
3198 builder.add(fLights->light(i));
3199 sk_sp<SkLights> curLight = builder.finish();
3200
3201 sk_sp<SkShader> shadowMapShader;
3202 shadowMapShader = SkRadialShadowMapShader::Make(
3203 povDepthShader, curLight,
3204 (int) picture->cullRect().width(),
3205 (int) picture->cullRect().height());
3206
3207 SkPaint shadowMapPaint;
3208 shadowMapPaint.setShader(std::move(shadowMapShader));
3209
3210 depthMapCanvas->setLights(curLight);
3211
3212 depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(),
3213 diffuseMap->height()),
3214 shadowMapPaint);
3215
3216 depthMap = surf->makeImageSnapshot();
3217
3218 } else {
3219 // TODO: compute the correct size of the depth map from the light properties
3220 // TODO: maybe add a kDepth_8_SkColorType
3221 // TODO: find actual max depth of picture
3222 shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3223 fLights->light(i), 255,
3224 (int) picture->cullRect().width(),
3225 (int) picture->cullRect().height());
3226
3227 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3228 kBGRA_8888_SkColorType,
3229 kOpaque_SkAlphaType);
3230
3231 // Create a new surface (that matches the backend of canvas)
3232 // for each shadow map
3233 sk_sp<SkSurface> surf(this->makeSurface(info));
3234
3235 // Wrap another SPFCanvas around the surface
3236 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3237 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3238 depthMapCanvas->setShadowParams(params);
3239
3240 // set the depth map canvas to have the light we're drawing.
3241 SkLights::Builder builder;
3242 builder.add(fLights->light(i));
3243 sk_sp<SkLights> curLight = builder.finish();
3244 depthMapCanvas->setLights(std::move(curLight));
3245
3246 depthMapCanvas->drawPicture(picture);
3247 depthMap = surf->makeImageSnapshot();
3248 }
3249
3250 if (params.fType == SkShadowParams::kNoBlur_ShadowType) {
3251 fLights->light(i).setShadowMap(std::move(depthMap));
3252 } else if (params.fType == SkShadowParams::kVariance_ShadowType) {
3253 // we blur the variance map
3254 SkPaint blurPaint;
3255 blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius,
3256 params.fShadowRadius, nullptr));
3257
3258 SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3259 kBGRA_8888_SkColorType,
3260 kOpaque_SkAlphaType);
3261
3262 sk_sp<SkSurface> blurSurf(this->makeSurface(blurInfo));
3263
3264 blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
3265
3266 fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
3267 }
3268 }
3269
3270 SkPaint shadowPaint;
vjiaoblack904527d2016-08-09 09:32:09 -07003271 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3272 std::move(diffuseShader),
vjiaoblackb2796fd2016-09-09 09:22:39 -07003273 fLights,
vjiaoblack904527d2016-08-09 09:32:09 -07003274 diffuseMap->width(),
vjiaoblacke6f5d562016-08-25 06:30:23 -07003275 diffuseMap->height(),
3276 params);
vjiaoblack904527d2016-08-09 09:32:09 -07003277
3278 shadowPaint.setShader(shadowShader);
3279
3280 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003281}
3282#endif
3283
reed@android.com8a1c16f2008-12-17 15:59:43 +00003284///////////////////////////////////////////////////////////////////////////////
3285///////////////////////////////////////////////////////////////////////////////
3286
reed3aafe112016-08-18 12:45:34 -07003287SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003288 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003289
3290 SkASSERT(canvas);
3291
reed3aafe112016-08-18 12:45:34 -07003292 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003293 fDone = !fImpl->next();
3294}
3295
3296SkCanvas::LayerIter::~LayerIter() {
3297 fImpl->~SkDrawIter();
3298}
3299
3300void SkCanvas::LayerIter::next() {
3301 fDone = !fImpl->next();
3302}
3303
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003304SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003305 return fImpl->getDevice();
3306}
3307
3308const SkMatrix& SkCanvas::LayerIter::matrix() const {
3309 return fImpl->getMatrix();
3310}
3311
3312const SkPaint& SkCanvas::LayerIter::paint() const {
3313 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003314 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003315 paint = &fDefaultPaint;
3316 }
3317 return *paint;
3318}
3319
reed1e7f5e72016-04-27 07:49:17 -07003320const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003321int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3322int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003323
3324///////////////////////////////////////////////////////////////////////////////
3325
fmalitac3b589a2014-06-05 12:40:07 -07003326SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003327
3328///////////////////////////////////////////////////////////////////////////////
3329
3330static bool supported_for_raster_canvas(const SkImageInfo& info) {
3331 switch (info.alphaType()) {
3332 case kPremul_SkAlphaType:
3333 case kOpaque_SkAlphaType:
3334 break;
3335 default:
3336 return false;
3337 }
3338
3339 switch (info.colorType()) {
3340 case kAlpha_8_SkColorType:
3341 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003342 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003343 break;
3344 default:
3345 return false;
3346 }
3347
3348 return true;
3349}
3350
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003351SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3352 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003353 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003354 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003355
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003356 SkBitmap bitmap;
3357 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003358 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003359 }
halcanary385fe4d2015-08-26 13:07:48 -07003360 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003361}
reedd5fa1a42014-08-09 11:08:05 -07003362
3363///////////////////////////////////////////////////////////////////////////////
3364
3365SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003366 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003367 : fCanvas(canvas)
3368 , fSaveCount(canvas->getSaveCount())
3369{
bsalomon49f085d2014-09-05 13:34:00 -07003370 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003371 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003372 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003373 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003374 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003375 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003376 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003377 canvas->save();
3378 }
mtklein6cfa73a2014-08-13 13:33:49 -07003379
bsalomon49f085d2014-09-05 13:34:00 -07003380 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003381 canvas->concat(*matrix);
3382 }
3383}
3384
3385SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3386 fCanvas->restoreToCount(fSaveCount);
3387}
reede8f30622016-03-23 18:59:25 -07003388
3389#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3390SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3391 return this->makeSurface(info, props).release();
3392}
3393#endif
reed73603f32016-09-20 08:42:38 -07003394
3395/////////////////////////////////
3396
3397const SkCanvas::ClipOp SkCanvas::kDifference_Op;
3398const SkCanvas::ClipOp SkCanvas::kIntersect_Op;
3399const SkCanvas::ClipOp SkCanvas::kUnion_Op;
3400const SkCanvas::ClipOp SkCanvas::kXOR_Op;
3401const SkCanvas::ClipOp SkCanvas::kReverseDifference_Op;
3402const SkCanvas::ClipOp SkCanvas::kReplace_Op;
3403
3404static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3405static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3406static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3407static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3408static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3409static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");