blob: a8d73a982a5f591f116b3189d78fa64a5cb60860 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
piotaixrb5fae932014-09-24 13:03:30 -070017#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080018#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070019#include "SkImageFilter.h"
20#include "SkImageFilterCache.h"
msarettc573a402016-08-02 08:05:56 -070021#include "SkLatticeIter.h"
Mike Reed5df49342016-11-12 08:06:55 -060022#include "SkMakeUnique.h"
reed262a71b2015-12-05 13:07:27 -080023#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000024#include "SkMetaData.h"
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) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 canvas->updateDeviceCMCache();
324
bungeman6bd52842016-10-27 09:30:08 -0700325 fClipStack = canvas->getClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 fCurrLayer = canvas->fMCRec->fTopLayer;
reed02f9ed72016-09-06 09:06:18 -0700327
328 fMultiDeviceCS = nullptr;
329 if (fCurrLayer->fNext) {
bungeman6bd52842016-10-27 09:30:08 -0700330 fMultiDeviceCS = canvas->fClipStack.get();
reed02f9ed72016-09-06 09:06:18 -0700331 fMultiDeviceCS->save();
332 }
333 }
334
335 ~SkDrawIter() {
336 if (fMultiDeviceCS) {
337 fMultiDeviceCS->restore();
338 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339 }
reed@google.com4b226022011-01-11 18:32:13 +0000340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 bool next() {
reed02f9ed72016-09-06 09:06:18 -0700342 if (fMultiDeviceCS && fDevice) {
343 // remove the previous device's bounds
reed73603f32016-09-20 08:42:38 -0700344 fMultiDeviceCS->clipDevRect(compute_device_bounds(fDevice), SkCanvas::kDifference_Op);
reed02f9ed72016-09-06 09:06:18 -0700345 }
346
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 // skip over recs with empty clips
reed3aafe112016-08-18 12:45:34 -0700348 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
349 fCurrLayer = fCurrLayer->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 }
351
reed@google.comf68c5e22012-02-24 16:38:58 +0000352 const DeviceCM* rec = fCurrLayer;
353 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354
355 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000356 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700358 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700359 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700360 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000362 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363
364 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700365 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 return true;
368 }
369 return false;
370 }
reed@google.com4b226022011-01-11 18:32:13 +0000371
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000372 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700373 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000374 int getX() const { return fDevice->getOrigin().x(); }
375 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000378
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 const DeviceCM* fCurrLayer;
381 const SkPaint* fPaint; // May be null.
reed02f9ed72016-09-06 09:06:18 -0700382 SkClipStack* fMultiDeviceCS;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383
384 typedef SkDraw INHERITED;
385};
386
387/////////////////////////////////////////////////////////////////////////////
388
reeddbc3cef2015-04-29 12:18:57 -0700389static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
390 return lazy->isValid() ? lazy->get() : lazy->set(orig);
391}
392
393/**
394 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700395 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700396 */
reedd053ce92016-03-22 10:17:23 -0700397static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700398 SkImageFilter* imgf = paint.getImageFilter();
399 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700400 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700401 }
402
reedd053ce92016-03-22 10:17:23 -0700403 SkColorFilter* imgCFPtr;
404 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700405 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700406 }
reedd053ce92016-03-22 10:17:23 -0700407 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700408
409 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700410 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700411 // there is no existing paint colorfilter, so we can just return the imagefilter's
412 return imgCF;
413 }
414
415 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
416 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700417 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700418}
419
senorblanco87e066e2015-10-28 11:23:36 -0700420/**
421 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
422 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
423 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
424 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
425 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
426 * conservative "effective" bounds based on the settings in the paint... with one exception. This
427 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
428 * deliberately ignored.
429 */
430static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
431 const SkRect& rawBounds,
432 SkRect* storage) {
433 SkPaint tmpUnfiltered(paint);
434 tmpUnfiltered.setImageFilter(nullptr);
435 if (tmpUnfiltered.canComputeFastBounds()) {
436 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
437 } else {
438 return rawBounds;
439 }
440}
441
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442class AutoDrawLooper {
443public:
senorblanco87e066e2015-10-28 11:23:36 -0700444 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
445 // paint. It's used to determine the size of the offscreen layer for filters.
446 // If null, the clip will be used instead.
reed3aafe112016-08-18 12:45:34 -0700447 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700448 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000449 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800450#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800452#else
453 fFilter = nullptr;
454#endif
reed4a8126e2014-09-22 07:29:03 -0700455 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000456 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700457 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000458 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459
reedd053ce92016-03-22 10:17:23 -0700460 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700461 if (simplifiedCF) {
462 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700463 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700464 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700465 fPaint = paint;
466 }
467
468 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700469 /**
470 * We implement ImageFilters for a given draw by creating a layer, then applying the
471 * imagefilter to the pixels of that layer (its backing surface/image), and then
472 * we call restore() to xfer that layer to the main canvas.
473 *
474 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
475 * 2. Generate the src pixels:
476 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
477 * return (fPaint). We then draw the primitive (using srcover) into a cleared
478 * buffer/surface.
479 * 3. Restore the layer created in #1
480 * The imagefilter is passed the buffer/surface from the layer (now filled with the
481 * src pixels of the primitive). It returns a new "filtered" buffer, which we
482 * draw onto the previous layer using the xfermode from the original paint.
483 */
reed@google.com8926b162012-03-23 15:36:36 +0000484 SkPaint tmp;
Mike Reed5e257172016-11-01 11:22:05 -0400485 tmp.setImageFilter(sk_ref_sp(fPaint->getImageFilter()));
reed374772b2016-10-05 17:33:02 -0700486 tmp.setBlendMode(fPaint->getBlendMode());
senorblanco87e066e2015-10-28 11:23:36 -0700487 SkRect storage;
488 if (rawBounds) {
489 // Make rawBounds include all paint outsets except for those due to image filters.
490 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
491 }
reedbfd5f172016-01-07 11:28:08 -0800492 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700493 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700494 fTempLayerForImageFilter = true;
495 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000496 }
497
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000498 if (SkDrawLooper* looper = paint.getLooper()) {
mtkleinf982cb32016-11-13 09:55:05 -0800499 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
500 looper->contextSize());
501 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000502 fIsSimple = false;
503 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700504 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000505 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700506 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000507 }
508 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000509
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700511 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000512 fCanvas->internalRestore();
513 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000514 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000516
reed@google.com4e2b3d32011-04-07 14:18:59 +0000517 const SkPaint& paint() const {
518 SkASSERT(fPaint);
519 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000521
reed@google.com129ec222012-05-15 13:24:09 +0000522 bool next(SkDrawFilter::Type drawType) {
523 if (fDone) {
524 return false;
525 } else if (fIsSimple) {
526 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000527 return !fPaint->nothingToDraw();
528 } else {
529 return this->doNext(drawType);
530 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000531 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533private:
reeddbc3cef2015-04-29 12:18:57 -0700534 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
535 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000536 SkCanvas* fCanvas;
537 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000538 SkDrawFilter* fFilter;
539 const SkPaint* fPaint;
540 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700541 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000542 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000543 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000544 SkDrawLooper::Context* fLooperContext;
545 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000546
547 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548};
549
reed@google.com129ec222012-05-15 13:24:09 +0000550bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700551 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000552 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700553 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000554
reeddbc3cef2015-04-29 12:18:57 -0700555 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
556 *fLazyPaintInit.get() : fOrigPaint);
reed@google.com129ec222012-05-15 13:24:09 +0000557
reed5c476fb2015-04-20 08:04:21 -0700558 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700559 paint->setImageFilter(nullptr);
reed374772b2016-10-05 17:33:02 -0700560 paint->setBlendMode(SkBlendMode::kSrcOver);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000561 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000562
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000563 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000564 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000565 return false;
566 }
567 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000568 if (!fFilter->filter(paint, drawType)) {
569 fDone = true;
570 return false;
571 }
halcanary96fcdcc2015-08-27 07:41:13 -0700572 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000573 // no looper means we only draw once
574 fDone = true;
575 }
576 }
577 fPaint = paint;
578
579 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000580 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000581 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000582 }
583
584 // call this after any possible paint modifiers
585 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700586 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000587 return false;
588 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000589 return true;
590}
591
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592////////// macros to place around the internal draw calls //////////////////
593
reed3aafe112016-08-18 12:45:34 -0700594#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
595 this->predrawNotify(); \
596 AutoDrawLooper looper(this, paint, skipLayerForFilter, bounds); \
597 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
reed262a71b2015-12-05 13:07:27 -0800598 SkDrawIter iter(this);
599
600
reed@google.com8926b162012-03-23 15:36:36 +0000601#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000602 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700603 AutoDrawLooper looper(this, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000604 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000605 SkDrawIter iter(this);
606
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000607#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000608 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700609 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000610 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000612
reedc83a2972015-07-16 07:40:45 -0700613#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
614 this->predrawNotify(bounds, &paint, auxOpaque); \
reed3aafe112016-08-18 12:45:34 -0700615 AutoDrawLooper looper(this, paint, false, bounds); \
reedc83a2972015-07-16 07:40:45 -0700616 while (looper.next(type)) { \
617 SkDrawIter iter(this);
618
reed@google.com4e2b3d32011-04-07 14:18:59 +0000619#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620
621////////////////////////////////////////////////////////////////////////////
622
msarettfbfa2582016-08-12 08:29:08 -0700623static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
624 if (bounds.isEmpty()) {
625 return SkRect::MakeEmpty();
626 }
627
628 // Expand bounds out by 1 in case we are anti-aliasing. We store the
629 // bounds as floats to enable a faster quick reject implementation.
630 SkRect dst;
631 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
632 return dst;
633}
634
mtkleinfeaadee2015-04-08 11:25:48 -0700635void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
636 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700637 fClipStack->reset();
638 fMCRec->reset(bounds);
639
640 // We're peering through a lot of structs here. Only at this scope do we
641 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
642 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
msarettfbfa2582016-08-12 08:29:08 -0700643 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700644 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700645}
646
reedd9544982014-09-09 18:46:22 -0700647SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800648 if (device && device->forceConservativeRasterClip()) {
649 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
650 }
651 // Since init() is only called once by our constructors, it is safe to perform this
652 // const-cast.
653 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
654
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000655 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700656 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800657 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700658 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700659#ifdef SK_EXPERIMENTAL_SHADOWING
660 fLights = nullptr;
661#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662
halcanary385fe4d2015-08-26 13:07:48 -0700663 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700664
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700666 new (fMCRec) MCRec(fConservativeRasterClip);
msarett9637ea92016-08-18 14:03:30 -0700667 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668
reeda499f902015-05-01 09:34:31 -0700669 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
670 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700671 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700672 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700673
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675
halcanary96fcdcc2015-08-27 07:41:13 -0700676 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000677
reedf92c8662014-08-18 08:02:43 -0700678 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700679 // The root device and the canvas should always have the same pixel geometry
680 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700681 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800682 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700683 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700684 }
msarettfbfa2582016-08-12 08:29:08 -0700685
reedf92c8662014-08-18 08:02:43 -0700686 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
reed@google.comcde92112011-07-06 20:00:52 +0000689SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000690 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700691 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800692 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000693{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000694 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000695
halcanary96fcdcc2015-08-27 07:41:13 -0700696 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000697}
698
reedd9544982014-09-09 18:46:22 -0700699static SkBitmap make_nopixels(int width, int height) {
700 SkBitmap bitmap;
701 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
702 return bitmap;
703}
704
705class SkNoPixelsBitmapDevice : public SkBitmapDevice {
706public:
robertphillipsfcf78292015-06-19 11:49:52 -0700707 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
708 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800709 {
710 this->setOrigin(bounds.x(), bounds.y());
711 }
reedd9544982014-09-09 18:46:22 -0700712
713private:
piotaixrb5fae932014-09-24 13:03:30 -0700714
reedd9544982014-09-09 18:46:22 -0700715 typedef SkBitmapDevice INHERITED;
716};
717
reed96a857e2015-01-25 10:33:58 -0800718SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000719 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800720 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800721 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000722{
723 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700724
halcanary385fe4d2015-08-26 13:07:48 -0700725 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
726 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700727}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000728
reed78e27682014-11-19 08:04:34 -0800729SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700730 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700731 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800732 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700733{
734 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700735
halcanary385fe4d2015-08-26 13:07:48 -0700736 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700737}
738
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000739SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000740 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700741 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800742 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000743{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700745
reedd9544982014-09-09 18:46:22 -0700746 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747}
748
robertphillipsfcf78292015-06-19 11:49:52 -0700749SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
750 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700751 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800752 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700753{
754 inc_canvas();
755
756 this->init(device, flags);
757}
758
reed4a8126e2014-09-22 07:29:03 -0700759SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700760 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700761 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800762 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700763{
764 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700765
Hal Canary704cd322016-11-07 14:13:52 -0500766 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
767 this->init(device.get(), kDefault_InitFlags);
reed4a8126e2014-09-22 07:29:03 -0700768}
reed29c857d2014-09-21 10:25:07 -0700769
reed4a8126e2014-09-22 07:29:03 -0700770SkCanvas::SkCanvas(const SkBitmap& bitmap)
771 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
772 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800773 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700774{
775 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700776
Hal Canary704cd322016-11-07 14:13:52 -0500777 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
778 this->init(device.get(), kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779}
780
781SkCanvas::~SkCanvas() {
782 // free up the contents of our deque
783 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 this->internalRestore(); // restore the last, since we're going away
786
halcanary385fe4d2015-08-26 13:07:48 -0700787 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 dec_canvas();
790}
791
fmalita53d9f1c2016-01-25 06:23:54 -0800792#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793SkDrawFilter* SkCanvas::getDrawFilter() const {
794 return fMCRec->fFilter;
795}
796
797SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700798 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
800 return filter;
801}
fmalita77650002016-01-21 18:47:11 -0800802#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000804SkMetaData& SkCanvas::getMetaData() {
805 // metadata users are rare, so we lazily allocate it. If that changes we
806 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700807 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000808 fMetaData = new SkMetaData;
809 }
810 return *fMetaData;
811}
812
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813///////////////////////////////////////////////////////////////////////////////
814
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000815void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700816 this->onFlush();
817}
818
819void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000820 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000821 if (device) {
822 device->flush();
823 }
824}
825
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000826SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000827 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000828 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
829}
830
senorblancoafc7cce2016-02-02 18:44:15 -0800831SkIRect SkCanvas::getTopLayerBounds() const {
832 SkBaseDevice* d = this->getTopDevice();
833 if (!d) {
834 return SkIRect::MakeEmpty();
835 }
836 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
837}
838
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000839SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000841 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 SkASSERT(rec && rec->fLayer);
843 return rec->fLayer->fDevice;
844}
845
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000846SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000847 if (updateMatrixClip) {
848 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
849 }
reed@google.com9266fed2011-03-30 00:18:03 +0000850 return fMCRec->fTopLayer->fDevice;
851}
852
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000853bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700854 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000855 return false;
856 }
857
858 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700859 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700860 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000861 return false;
862 }
863 weAllocated = true;
864 }
865
reedcf01e312015-05-23 19:14:51 -0700866 SkAutoPixmapUnlock unlocker;
867 if (bitmap->requestLock(&unlocker)) {
868 const SkPixmap& pm = unlocker.pixmap();
869 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
870 return true;
871 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000872 }
873
874 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700875 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000876 }
877 return false;
878}
reed@google.com51df9e32010-12-23 19:29:18 +0000879
bsalomon@google.comc6980972011-11-02 19:57:21 +0000880bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000881 SkIRect r = srcRect;
882 const SkISize size = this->getBaseLayerSize();
883 if (!r.intersect(0, 0, size.width(), size.height())) {
884 bitmap->reset();
885 return false;
886 }
887
reed84825042014-09-02 12:50:45 -0700888 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000889 // bitmap will already be reset.
890 return false;
891 }
892 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
893 bitmap->reset();
894 return false;
895 }
896 return true;
897}
898
reed96472de2014-12-10 09:53:42 -0800899bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000900 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000901 if (!device) {
902 return false;
903 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000904 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800905
reed96472de2014-12-10 09:53:42 -0800906 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
907 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000908 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000909 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000910
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000911 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800912 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000913}
914
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000915bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700916 SkAutoPixmapUnlock unlocker;
917 if (bitmap.requestLock(&unlocker)) {
918 const SkPixmap& pm = unlocker.pixmap();
919 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000920 }
921 return false;
922}
923
924bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
925 int x, int y) {
926 switch (origInfo.colorType()) {
927 case kUnknown_SkColorType:
928 case kIndex_8_SkColorType:
929 return false;
930 default:
931 break;
932 }
halcanary96fcdcc2015-08-27 07:41:13 -0700933 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000934 return false;
935 }
936
937 const SkISize size = this->getBaseLayerSize();
938 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
939 if (!target.intersect(0, 0, size.width(), size.height())) {
940 return false;
941 }
942
943 SkBaseDevice* device = this->getDevice();
944 if (!device) {
945 return false;
946 }
947
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000948 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700949 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000950
951 // if x or y are negative, then we have to adjust pixels
952 if (x > 0) {
953 x = 0;
954 }
955 if (y > 0) {
956 y = 0;
957 }
958 // here x,y are either 0 or negative
959 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
960
reed4af35f32014-06-27 17:47:49 -0700961 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700962 const bool completeOverwrite = info.dimensions() == size;
963 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700964
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000965 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000966 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000967}
reed@google.com51df9e32010-12-23 19:29:18 +0000968
reed@android.com8a1c16f2008-12-17 15:59:43 +0000969//////////////////////////////////////////////////////////////////////////////
970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971void SkCanvas::updateDeviceCMCache() {
972 if (fDeviceCMDirty) {
973 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700974 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000976
halcanary96fcdcc2015-08-27 07:41:13 -0700977 if (nullptr == layer->fNext) { // only one layer
reedde6c5312016-09-02 12:10:07 -0700978 layer->updateMC(totalMatrix, totalClip, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000980 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 do {
reedde6c5312016-09-02 12:10:07 -0700982 layer->updateMC(totalMatrix, clip, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700983 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000984 }
985 fDeviceCMDirty = false;
986 }
987}
988
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989///////////////////////////////////////////////////////////////////////////////
990
reed2ff1fce2014-12-11 07:07:37 -0800991void SkCanvas::checkForDeferredSave() {
992 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800993 this->doSave();
994 }
995}
996
reedf0090cb2014-11-26 08:55:51 -0800997int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800998#ifdef SK_DEBUG
999 int count = 0;
1000 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1001 for (;;) {
1002 const MCRec* rec = (const MCRec*)iter.next();
1003 if (!rec) {
1004 break;
1005 }
1006 count += 1 + rec->fDeferredSaveCount;
1007 }
1008 SkASSERT(count == fSaveCount);
1009#endif
1010 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001011}
1012
1013int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001014 fSaveCount += 1;
1015 fMCRec->fDeferredSaveCount += 1;
1016 return this->getSaveCount() - 1; // return our prev value
1017}
1018
1019void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001020 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001021
1022 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1023 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001024 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001025}
1026
1027void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001028 if (fMCRec->fDeferredSaveCount > 0) {
1029 SkASSERT(fSaveCount > 1);
1030 fSaveCount -= 1;
1031 fMCRec->fDeferredSaveCount -= 1;
1032 } else {
1033 // check for underflow
1034 if (fMCStack.count() > 1) {
1035 this->willRestore();
1036 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001037 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001038 this->internalRestore();
1039 this->didRestore();
1040 }
reedf0090cb2014-11-26 08:55:51 -08001041 }
1042}
1043
1044void SkCanvas::restoreToCount(int count) {
1045 // sanity check
1046 if (count < 1) {
1047 count = 1;
1048 }
mtkleinf0f14112014-12-12 08:46:25 -08001049
reedf0090cb2014-11-26 08:55:51 -08001050 int n = this->getSaveCount() - count;
1051 for (int i = 0; i < n; ++i) {
1052 this->restore();
1053 }
1054}
1055
reed2ff1fce2014-12-11 07:07:37 -08001056void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001057 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001058 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001060
reed687fa1c2015-04-07 08:00:56 -07001061 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001062}
1063
reed4960eee2015-12-18 07:09:18 -08001064bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed4960eee2015-12-18 07:09:18 -08001065 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066}
1067
reed4960eee2015-12-18 07:09:18 -08001068bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001069 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001070 SkIRect clipBounds;
1071 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001072 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001073 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001074
reed96e657d2015-03-10 17:30:07 -07001075 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1076
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001077 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001078 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001079 if (bounds && !imageFilter->canComputeFastBounds()) {
1080 bounds = nullptr;
1081 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001082 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001083 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001084 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001085 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001086
reed96e657d2015-03-10 17:30:07 -07001087 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088 r.roundOut(&ir);
1089 // early exit if the layer's bounds are clipped out
1090 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001091 if (BoundsAffectsClip(saveLayerFlags)) {
reed1f836ee2014-07-07 07:49:34 -07001092 fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001093 fDeviceClipBounds.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001094 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001095 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001096 }
1097 } else { // no user bounds, so just use the clip
1098 ir = clipBounds;
1099 }
reed180aec42015-03-11 10:39:04 -07001100 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001101
reed4960eee2015-12-18 07:09:18 -08001102 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001103 // Simplify the current clips since they will be applied properly during restore()
reed73603f32016-09-20 08:42:38 -07001104 fClipStack->clipDevRect(ir, kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001105 fMCRec->fRasterClip.setRect(ir);
msarettfbfa2582016-08-12 08:29:08 -07001106 fDeviceClipBounds = qr_clip_bounds(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001107 }
1108
1109 if (intersection) {
1110 *intersection = ir;
1111 }
1112 return true;
1113}
1114
reed4960eee2015-12-18 07:09:18 -08001115
reed4960eee2015-12-18 07:09:18 -08001116int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1117 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001118}
1119
reed70ee31b2015-12-10 13:44:45 -08001120int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001121 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1122}
1123
1124int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1125 SaveLayerRec rec(origRec);
1126 if (gIgnoreSaveLayerBounds) {
1127 rec.fBounds = nullptr;
1128 }
1129 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1130 fSaveCount += 1;
1131 this->internalSaveLayer(rec, strategy);
1132 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001133}
1134
reeda2217ef2016-07-20 06:04:34 -07001135void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1136 SkBaseDevice* dst, const SkMatrix& ctm,
1137 const SkClipStack* clipStack) {
1138 SkDraw draw;
1139 SkRasterClip rc;
1140 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1141 if (!dst->accessPixels(&draw.fDst)) {
1142 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001143 }
reeda2217ef2016-07-20 06:04:34 -07001144 draw.fMatrix = &SkMatrix::I();
1145 draw.fRC = &rc;
1146 draw.fClipStack = clipStack;
1147 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001148
1149 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001150 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001151
1152 int x = src->getOrigin().x() - dst->getOrigin().x();
1153 int y = src->getOrigin().y() - dst->getOrigin().y();
1154 auto special = src->snapSpecial();
1155 if (special) {
1156 dst->drawSpecial(draw, special.get(), x, y, p);
1157 }
robertphillips7354a4b2015-12-16 05:08:27 -08001158}
reed70ee31b2015-12-10 13:44:45 -08001159
reed129ed1c2016-02-22 06:42:31 -08001160static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1161 const SkPaint* paint) {
1162 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1163 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001164 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001165 const bool hasImageFilter = paint && paint->getImageFilter();
1166
1167 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1168 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1169 // force to L32
1170 return SkImageInfo::MakeN32(w, h, alphaType);
1171 } else {
1172 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001173 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001174 }
1175}
1176
reed4960eee2015-12-18 07:09:18 -08001177void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1178 const SkRect* bounds = rec.fBounds;
1179 const SkPaint* paint = rec.fPaint;
1180 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1181
reed8c30a812016-04-20 16:36:51 -07001182 SkLazyPaint lazyP;
1183 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1184 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001185 SkMatrix remainder;
1186 SkSize scale;
1187 /*
1188 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1189 * but they do handle scaling. To accommodate this, we do the following:
1190 *
1191 * 1. Stash off the current CTM
1192 * 2. Decompose the CTM into SCALE and REMAINDER
1193 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1194 * contains the REMAINDER
1195 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1196 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1197 * of the original imagefilter, and draw that (via drawSprite)
1198 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1199 *
1200 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1201 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1202 */
reed96a04f32016-04-25 09:25:15 -07001203 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001204 stashedMatrix.decomposeScale(&scale, &remainder))
1205 {
1206 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1207 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1208 SkPaint* p = lazyP.set(*paint);
1209 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1210 SkFilterQuality::kLow_SkFilterQuality,
1211 sk_ref_sp(imageFilter)));
1212 imageFilter = p->getImageFilter();
1213 paint = p;
1214 }
reed8c30a812016-04-20 16:36:51 -07001215
junov@chromium.orga907ac32012-02-24 21:54:07 +00001216 // do this before we create the layer. We don't call the public save() since
1217 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001218 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001219
1220 fDeviceCMDirty = true;
1221
1222 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001223 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001224 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001225 }
1226
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001227 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1228 // the clipRectBounds() call above?
1229 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001230 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001231 }
1232
reed4960eee2015-12-18 07:09:18 -08001233 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001234 SkPixelGeometry geo = fProps.pixelGeometry();
1235 if (paint) {
reed76033be2015-03-14 10:54:31 -07001236 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001237 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001238 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001239 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001240 }
1241 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001242
robertphillips5139e502016-07-19 05:10:40 -07001243 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001244 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001245 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001246 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001247 }
reedb2db8982014-11-13 12:41:02 -08001248
robertphillips5139e502016-07-19 05:10:40 -07001249 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001250 paint);
1251
Hal Canary704cd322016-11-07 14:13:52 -05001252 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001253 {
reed70ee31b2015-12-10 13:44:45 -08001254 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001255 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001256 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001257 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001258 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001259 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1260 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001261 return;
reed61f501f2015-04-29 08:34:00 -07001262 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001263 }
robertphillips5139e502016-07-19 05:10:40 -07001264 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001265
Hal Canary704cd322016-11-07 14:13:52 -05001266 DeviceCM* layer =
1267 new DeviceCM(newDevice.get(), paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268
1269 layer->fNext = fMCRec->fTopLayer;
1270 fMCRec->fLayer = layer;
1271 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001272
1273 if (rec.fBackdrop) {
Hal Canary704cd322016-11-07 14:13:52 -05001274 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(),
reeda2217ef2016-07-20 06:04:34 -07001275 fMCRec->fMatrix, this->getClipStack());
1276 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001277}
1278
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001279int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001280 if (0xFF == alpha) {
1281 return this->saveLayer(bounds, nullptr);
1282 } else {
1283 SkPaint tmpPaint;
1284 tmpPaint.setAlpha(alpha);
1285 return this->saveLayer(bounds, &tmpPaint);
1286 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001287}
1288
reed@android.com8a1c16f2008-12-17 15:59:43 +00001289void SkCanvas::internalRestore() {
1290 SkASSERT(fMCStack.count() != 0);
1291
1292 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001293
reed687fa1c2015-04-07 08:00:56 -07001294 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001295
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001296 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001297 DeviceCM* layer = fMCRec->fLayer; // may be null
1298 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001299 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300
1301 // now do the normal restore()
1302 fMCRec->~MCRec(); // balanced in save()
1303 fMCStack.pop_back();
1304 fMCRec = (MCRec*)fMCStack.back();
1305
1306 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1307 since if we're being recorded, we don't want to record this (the
1308 recorder will have already recorded the restore).
1309 */
bsalomon49f085d2014-09-05 13:34:00 -07001310 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001311 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001312 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001313 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001314 // restore what we smashed in internalSaveLayer
1315 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001316 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001317 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001318 delete layer;
reedb679ca82015-04-07 04:40:48 -07001319 } else {
1320 // we're at the root
reeda499f902015-05-01 09:34:31 -07001321 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001322 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001323 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001325 }
msarettfbfa2582016-08-12 08:29:08 -07001326
1327 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001328 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001329 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1330 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001331}
1332
reede8f30622016-03-23 18:59:25 -07001333sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001334 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001335 props = &fProps;
1336 }
1337 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001338}
1339
reede8f30622016-03-23 18:59:25 -07001340sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001341 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001342 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001343}
1344
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001345SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001346 return this->onImageInfo();
1347}
1348
1349SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001350 SkBaseDevice* dev = this->getDevice();
1351 if (dev) {
1352 return dev->imageInfo();
1353 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001354 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001355 }
1356}
1357
brianosman898235c2016-04-06 07:38:23 -07001358bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001359 return this->onGetProps(props);
1360}
1361
1362bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001363 SkBaseDevice* dev = this->getDevice();
1364 if (dev) {
1365 if (props) {
1366 *props = fProps;
1367 }
1368 return true;
1369 } else {
1370 return false;
1371 }
1372}
1373
reed6ceeebd2016-03-09 14:26:26 -08001374bool SkCanvas::peekPixels(SkPixmap* pmap) {
1375 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001376}
1377
reed884e97c2015-05-26 11:31:54 -07001378bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001379 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001380 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001381}
1382
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001383void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001384 SkPixmap pmap;
1385 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001386 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001387 }
1388 if (info) {
1389 *info = pmap.info();
1390 }
1391 if (rowBytes) {
1392 *rowBytes = pmap.rowBytes();
1393 }
1394 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001395 *origin = this->getTopDevice(false)->getOrigin();
1396 }
reed884e97c2015-05-26 11:31:54 -07001397 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001398}
1399
reed884e97c2015-05-26 11:31:54 -07001400bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001401 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001402 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001403}
1404
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406
reed7503d602016-07-15 14:23:29 -07001407void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001409 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 paint = &tmp;
1411 }
reed@google.com4b226022011-01-11 18:32:13 +00001412
reed@google.com8926b162012-03-23 15:36:36 +00001413 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001414
reed@android.com8a1c16f2008-12-17 15:59:43 +00001415 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001416 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001417 paint = &looper.paint();
1418 SkImageFilter* filter = paint->getImageFilter();
1419 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Hal Canary43513542016-10-14 11:19:43 -04001420 sk_sp<SkSpecialImage> specialImage;
1421 if (filter && (specialImage = srcDev->snapSpecial())) {
1422 dstDev->drawSpecial(iter, specialImage.get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001423 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001424 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001425 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426 }
reeda2217ef2016-07-20 06:04:34 -07001427
reed@google.com4e2b3d32011-04-07 14:18:59 +00001428 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429}
1430
reed32704672015-12-16 08:27:10 -08001431/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001432
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001433void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001434 if (dx || dy) {
1435 this->checkForDeferredSave();
1436 fDeviceCMDirty = true;
1437 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001438
reedfe69b502016-09-12 06:31:48 -07001439 // Translate shouldn't affect the is-scale-translateness of the matrix.
1440 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001441
reedfe69b502016-09-12 06:31:48 -07001442 this->didTranslate(dx,dy);
1443 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444}
1445
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001446void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001447 SkMatrix m;
1448 m.setScale(sx, sy);
1449 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001450}
1451
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001452void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001453 SkMatrix m;
1454 m.setRotate(degrees);
1455 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001456}
1457
bungeman7438bfc2016-07-12 15:01:19 -07001458void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1459 SkMatrix m;
1460 m.setRotate(degrees, px, py);
1461 this->concat(m);
1462}
1463
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001464void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001465 SkMatrix m;
1466 m.setSkew(sx, sy);
1467 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001468}
1469
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001470void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001471 if (matrix.isIdentity()) {
1472 return;
1473 }
1474
reed2ff1fce2014-12-11 07:07:37 -08001475 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001476 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001477 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001478 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001479 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001480}
1481
reed8c30a812016-04-20 16:36:51 -07001482void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001483 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001484 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001485 fIsScaleTranslate = matrix.isScaleTranslate();
reed8c30a812016-04-20 16:36:51 -07001486}
1487
1488void SkCanvas::setMatrix(const SkMatrix& matrix) {
1489 this->checkForDeferredSave();
1490 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001491 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001492}
1493
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001495 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496}
1497
vjiaoblack95302da2016-07-21 10:25:54 -07001498#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001499void SkCanvas::translateZ(SkScalar z) {
1500 this->checkForDeferredSave();
1501 this->fMCRec->fCurDrawDepth += z;
1502 this->didTranslateZ(z);
1503}
1504
1505SkScalar SkCanvas::getZ() const {
1506 return this->fMCRec->fCurDrawDepth;
1507}
1508
vjiaoblack95302da2016-07-21 10:25:54 -07001509void SkCanvas::setLights(sk_sp<SkLights> lights) {
1510 this->fLights = lights;
1511}
1512
1513sk_sp<SkLights> SkCanvas::getLights() const {
1514 return this->fLights;
1515}
1516#endif
1517
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518//////////////////////////////////////////////////////////////////////////////
1519
reed73603f32016-09-20 08:42:38 -07001520void SkCanvas::clipRect(const SkRect& rect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001521 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001522 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1523 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001524}
1525
reed73603f32016-09-20 08:42:38 -07001526void SkCanvas::onClipRect(const SkRect& rect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001527 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reedc64eff52015-11-21 12:39:45 -08001528 AutoValidateClip avc(this);
Brian Salomona3b45d42016-10-03 11:36:16 -04001529 fClipStack->clipRect(rect, fMCRec->fMatrix, op, isAA);
1530 fMCRec->fRasterClip.op(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1531 isAA);
reedc64eff52015-11-21 12:39:45 -08001532 fDeviceCMDirty = true;
msarettfbfa2582016-08-12 08:29:08 -07001533 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001534}
1535
reed73603f32016-09-20 08:42:38 -07001536void SkCanvas::clipRRect(const SkRRect& rrect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001537 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001538 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001539 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001540 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1541 } else {
1542 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001543 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001544}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001545
reed73603f32016-09-20 08:42:38 -07001546void SkCanvas::onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001547 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001548
Brian Salomona3b45d42016-10-03 11:36:16 -04001549 fDeviceCMDirty = true;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001550
Brian Salomona3b45d42016-10-03 11:36:16 -04001551 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1552 fClipStack->clipRRect(rrect, fMCRec->fMatrix, op, isAA);
1553 fMCRec->fRasterClip.op(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1554 isAA);
1555 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1556 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001557}
1558
reed73603f32016-09-20 08:42:38 -07001559void SkCanvas::clipPath(const SkPath& path, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001560 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001561 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001562
1563 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1564 SkRect r;
1565 if (path.isRect(&r)) {
1566 this->onClipRect(r, op, edgeStyle);
1567 return;
1568 }
1569 SkRRect rrect;
1570 if (path.isOval(&r)) {
1571 rrect.setOval(r);
1572 this->onClipRRect(rrect, op, edgeStyle);
1573 return;
1574 }
1575 if (path.isRRect(&rrect)) {
1576 this->onClipRRect(rrect, op, edgeStyle);
1577 return;
1578 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001579 }
robertphillips39f05382015-11-24 09:30:12 -08001580
1581 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001582}
1583
reed73603f32016-09-20 08:42:38 -07001584void SkCanvas::onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001585 AutoValidateClip avc(this);
1586
reed@android.com8a1c16f2008-12-17 15:59:43 +00001587 fDeviceCMDirty = true;
Brian Salomona3b45d42016-10-03 11:36:16 -04001588 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589
Brian Salomona3b45d42016-10-03 11:36:16 -04001590 fClipStack->clipPath(path, fMCRec->fMatrix, op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591
Brian Salomona3b45d42016-10-03 11:36:16 -04001592 const SkPath* rasterClipPath = &path;
1593 const SkMatrix* matrix = &fMCRec->fMatrix;
1594 SkPath tempPath;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001595 if (fAllowSimplifyClip) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001596 isAA = getClipStack()->asPath(&tempPath);
1597 rasterClipPath = &tempPath;
1598 matrix = &SkMatrix::I();
reed73603f32016-09-20 08:42:38 -07001599 op = kReplace_Op;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001600 }
Brian Salomona3b45d42016-10-03 11:36:16 -04001601 fMCRec->fRasterClip.op(*rasterClipPath, *matrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1602 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001603 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604}
1605
reed73603f32016-09-20 08:42:38 -07001606void SkCanvas::clipRegion(const SkRegion& rgn, ClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001607 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001608 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001609}
1610
reed73603f32016-09-20 08:42:38 -07001611void SkCanvas::onClipRegion(const SkRegion& rgn, ClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001612 AutoValidateClip avc(this);
1613
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001615
reed@google.com5c3d1472011-02-22 19:12:23 +00001616 // todo: signal fClipStack that we have a region, and therefore (I guess)
1617 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001618 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001619
reed73603f32016-09-20 08:42:38 -07001620 fMCRec->fRasterClip.op(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001621 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622}
1623
reed@google.com819c9212011-02-23 18:56:55 +00001624#ifdef SK_DEBUG
1625void SkCanvas::validateClip() const {
1626 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001627 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001628 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001629 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001630 return;
1631 }
1632
reed@google.com819c9212011-02-23 18:56:55 +00001633 SkIRect ir;
1634 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001635 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001636
reed687fa1c2015-04-07 08:00:56 -07001637 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001638 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001639 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001640 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001641 case SkClipStack::Element::kRect_Type:
1642 element->getRect().round(&ir);
reed73603f32016-09-20 08:42:38 -07001643 tmpClip.op(ir, (SkRegion::Op)element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001644 break;
1645 case SkClipStack::Element::kEmpty_Type:
1646 tmpClip.setEmpty();
1647 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001648 default: {
1649 SkPath path;
1650 element->asPath(&path);
Brian Salomona3b45d42016-10-03 11:36:16 -04001651 tmpClip.op(path, SkMatrix::I(), this->getTopLayerBounds(),
1652 (SkRegion::Op)element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001653 break;
1654 }
reed@google.com819c9212011-02-23 18:56:55 +00001655 }
1656 }
reed@google.com819c9212011-02-23 18:56:55 +00001657}
1658#endif
1659
reed@google.com90c07ea2012-04-13 13:50:27 +00001660void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001661 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001662 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001663
halcanary96fcdcc2015-08-27 07:41:13 -07001664 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001665 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001666 }
1667}
1668
reed@google.com5c3d1472011-02-22 19:12:23 +00001669///////////////////////////////////////////////////////////////////////////////
1670
reed@google.com754de5f2014-02-24 19:38:20 +00001671bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001672 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001673}
1674
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001675bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001676 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001677}
1678
msarettfbfa2582016-08-12 08:29:08 -07001679static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1680#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1681 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1682 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1683 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1684 return 0xF != _mm_movemask_ps(mask);
1685#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1686 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1687 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1688 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1689 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1690#else
1691 SkRect devRectAsRect;
1692 SkRect devClipAsRect;
1693 devRect.store(&devRectAsRect.fLeft);
1694 devClip.store(&devClipAsRect.fLeft);
1695 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1696#endif
1697}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001698
msarettfbfa2582016-08-12 08:29:08 -07001699// It's important for this function to not be inlined. Otherwise the compiler will share code
1700// between the fast path and the slow path, resulting in two slow paths.
1701static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1702 const SkMatrix& matrix) {
1703 SkRect deviceRect;
1704 matrix.mapRect(&deviceRect, src);
1705 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1706}
1707
1708bool SkCanvas::quickReject(const SkRect& src) const {
1709#ifdef SK_DEBUG
1710 // Verify that fDeviceClipBounds are set properly.
1711 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001712 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001713 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001714 } else {
msarettfbfa2582016-08-12 08:29:08 -07001715 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001716 }
msarettfbfa2582016-08-12 08:29:08 -07001717
msarett9637ea92016-08-18 14:03:30 -07001718 // Verify that fIsScaleTranslate is set properly.
1719 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001720#endif
1721
msarett9637ea92016-08-18 14:03:30 -07001722 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001723 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1724 }
1725
1726 // We inline the implementation of mapScaleTranslate() for the fast path.
1727 float sx = fMCRec->fMatrix.getScaleX();
1728 float sy = fMCRec->fMatrix.getScaleY();
1729 float tx = fMCRec->fMatrix.getTranslateX();
1730 float ty = fMCRec->fMatrix.getTranslateY();
1731 Sk4f scale(sx, sy, sx, sy);
1732 Sk4f trans(tx, ty, tx, ty);
1733
1734 // Apply matrix.
1735 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1736
1737 // Make sure left < right, top < bottom.
1738 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1739 Sk4f min = Sk4f::Min(ltrb, rblt);
1740 Sk4f max = Sk4f::Max(ltrb, rblt);
1741 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1742 // ARM this sequence generates the fastest (a single instruction).
1743 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1744
1745 // Check if the device rect is NaN or outside the clip.
1746 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001747}
1748
reed@google.com3b3e8952012-08-16 20:53:31 +00001749bool SkCanvas::quickReject(const SkPath& path) const {
1750 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001751}
1752
reed@google.com3b3e8952012-08-16 20:53:31 +00001753bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001754 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001755 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001756 return false;
1757 }
1758
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001759 SkMatrix inverse;
1760 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001761 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001762 if (bounds) {
1763 bounds->setEmpty();
1764 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001765 return false;
1766 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001767
bsalomon49f085d2014-09-05 13:34:00 -07001768 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001769 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001770 // adjust it outwards in case we are antialiasing
1771 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001772
reed@google.com8f4d2302013-12-17 16:44:46 +00001773 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1774 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001775 inverse.mapRect(bounds, r);
1776 }
1777 return true;
1778}
1779
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001780bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001781 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001782 if (clip.isEmpty()) {
1783 if (bounds) {
1784 bounds->setEmpty();
1785 }
1786 return false;
1787 }
1788
bsalomon49f085d2014-09-05 13:34:00 -07001789 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001790 *bounds = clip.getBounds();
1791 }
1792 return true;
1793}
1794
reed@android.com8a1c16f2008-12-17 15:59:43 +00001795const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001796 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797}
1798
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001799const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001800 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001801}
1802
Brian Osman11052242016-10-27 14:47:55 -04001803GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001804 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001805 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001806}
1807
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001808GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001809 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001810 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001811}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001812
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001813void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1814 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001815 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001816 if (outer.isEmpty()) {
1817 return;
1818 }
1819 if (inner.isEmpty()) {
1820 this->drawRRect(outer, paint);
1821 return;
1822 }
1823
1824 // We don't have this method (yet), but technically this is what we should
1825 // be able to assert...
1826 // SkASSERT(outer.contains(inner));
1827 //
1828 // For now at least check for containment of bounds
1829 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1830
1831 this->onDrawDRRect(outer, inner, paint);
1832}
1833
reed41af9662015-01-05 07:49:08 -08001834// These need to stop being virtual -- clients need to override the onDraw... versions
1835
1836void SkCanvas::drawPaint(const SkPaint& paint) {
1837 this->onDrawPaint(paint);
1838}
1839
1840void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1841 this->onDrawRect(r, paint);
1842}
1843
msarettdca352e2016-08-26 06:37:45 -07001844void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
1845 if (region.isEmpty()) {
1846 return;
1847 }
1848
1849 if (region.isRect()) {
1850 return this->drawIRect(region.getBounds(), paint);
1851 }
1852
1853 this->onDrawRegion(region, paint);
1854}
1855
reed41af9662015-01-05 07:49:08 -08001856void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1857 this->onDrawOval(r, paint);
1858}
1859
1860void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1861 this->onDrawRRect(rrect, paint);
1862}
1863
1864void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1865 this->onDrawPoints(mode, count, pts, paint);
1866}
1867
1868void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001869 const SkPoint texs[], const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08001870 const uint16_t indices[], int indexCount, const SkPaint& paint) {
Mike Reedfaba3712016-11-03 14:45:31 -04001871 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, bmode,
reed41af9662015-01-05 07:49:08 -08001872 indices, indexCount, paint);
1873}
1874
1875void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1876 this->onDrawPath(path, paint);
1877}
1878
reeda85d4d02015-05-06 12:56:48 -07001879void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001880 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001881 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001882}
1883
reede47829b2015-08-06 10:02:53 -07001884void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1885 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001886 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001887 if (dst.isEmpty() || src.isEmpty()) {
1888 return;
1889 }
1890 this->onDrawImageRect(image, &src, dst, paint, constraint);
1891}
reed41af9662015-01-05 07:49:08 -08001892
reed84984ef2015-07-17 07:09:43 -07001893void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1894 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001895 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001896 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001897}
1898
reede47829b2015-08-06 10:02:53 -07001899void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1900 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001901 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001902 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1903 constraint);
1904}
reede47829b2015-08-06 10:02:53 -07001905
reed4c21dc52015-06-25 12:32:03 -07001906void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1907 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001908 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001909 if (dst.isEmpty()) {
1910 return;
1911 }
msarett552bca92016-08-03 06:53:26 -07001912 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1913 this->onDrawImageNine(image, center, dst, paint);
1914 } else {
reede47829b2015-08-06 10:02:53 -07001915 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001916 }
reed4c21dc52015-06-25 12:32:03 -07001917}
1918
msarett16882062016-08-16 09:31:08 -07001919void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1920 const SkPaint* paint) {
1921 RETURN_ON_NULL(image);
1922 if (dst.isEmpty()) {
1923 return;
1924 }
msarett71df2d72016-09-30 12:41:42 -07001925
1926 SkIRect bounds;
1927 Lattice latticePlusBounds = lattice;
1928 if (!latticePlusBounds.fBounds) {
1929 bounds = SkIRect::MakeWH(image->width(), image->height());
1930 latticePlusBounds.fBounds = &bounds;
1931 }
1932
1933 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
1934 this->onDrawImageLattice(image, latticePlusBounds, dst, paint);
msarett16882062016-08-16 09:31:08 -07001935 } else {
1936 this->drawImageRect(image, dst, paint);
1937 }
1938}
1939
reed41af9662015-01-05 07:49:08 -08001940void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001941 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001942 return;
1943 }
reed41af9662015-01-05 07:49:08 -08001944 this->onDrawBitmap(bitmap, dx, dy, paint);
1945}
1946
reede47829b2015-08-06 10:02:53 -07001947void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001948 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001949 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001950 return;
1951 }
reede47829b2015-08-06 10:02:53 -07001952 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001953}
1954
reed84984ef2015-07-17 07:09:43 -07001955void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1956 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001957 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001958}
1959
reede47829b2015-08-06 10:02:53 -07001960void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1961 SrcRectConstraint constraint) {
1962 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1963 constraint);
1964}
reede47829b2015-08-06 10:02:53 -07001965
reed41af9662015-01-05 07:49:08 -08001966void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1967 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001968 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001969 return;
1970 }
msarett552bca92016-08-03 06:53:26 -07001971 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
1972 this->onDrawBitmapNine(bitmap, center, dst, paint);
1973 } else {
reeda5517e22015-07-14 10:54:12 -07001974 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001975 }
reed41af9662015-01-05 07:49:08 -08001976}
1977
msarettc573a402016-08-02 08:05:56 -07001978void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
1979 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07001980 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07001981 return;
1982 }
msarett71df2d72016-09-30 12:41:42 -07001983
1984 SkIRect bounds;
1985 Lattice latticePlusBounds = lattice;
1986 if (!latticePlusBounds.fBounds) {
1987 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1988 latticePlusBounds.fBounds = &bounds;
1989 }
1990
1991 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
1992 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, paint);
msarett552bca92016-08-03 06:53:26 -07001993 } else {
msarett16882062016-08-16 09:31:08 -07001994 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07001995 }
msarettc573a402016-08-02 08:05:56 -07001996}
1997
reed71c3c762015-06-24 10:29:17 -07001998void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001999 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002000 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002001 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002002 if (count <= 0) {
2003 return;
2004 }
2005 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002006 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002007 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002008}
2009
reedf70b5312016-03-04 16:36:20 -08002010void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2011 if (key) {
2012 this->onDrawAnnotation(rect, key, value);
2013 }
2014}
2015
reede47829b2015-08-06 10:02:53 -07002016void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2017 const SkPaint* paint, SrcRectConstraint constraint) {
2018 if (src) {
2019 this->drawImageRect(image, *src, dst, paint, constraint);
2020 } else {
2021 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2022 dst, paint, constraint);
2023 }
2024}
2025void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2026 const SkPaint* paint, SrcRectConstraint constraint) {
2027 if (src) {
2028 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2029 } else {
2030 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2031 dst, paint, constraint);
2032 }
2033}
2034
tomhudsoncb3bd182016-05-18 07:24:16 -07002035void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2036 SkIRect layer_bounds = this->getTopLayerBounds();
2037 if (matrix) {
2038 *matrix = this->getTotalMatrix();
2039 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2040 }
2041 if (clip_bounds) {
2042 this->getClipDeviceBounds(clip_bounds);
2043 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2044 }
2045}
2046
reed@android.com8a1c16f2008-12-17 15:59:43 +00002047//////////////////////////////////////////////////////////////////////////////
2048// These are the virtual drawing methods
2049//////////////////////////////////////////////////////////////////////////////
2050
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002051void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002052 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002053 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2054 }
2055}
2056
reed41af9662015-01-05 07:49:08 -08002057void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002058 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002059 this->internalDrawPaint(paint);
2060}
2061
2062void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002063 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064
2065 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002066 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067 }
2068
reed@google.com4e2b3d32011-04-07 14:18:59 +00002069 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002070}
2071
reed41af9662015-01-05 07:49:08 -08002072void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2073 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002074 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002075 if ((long)count <= 0) {
2076 return;
2077 }
2078
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002079 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002080 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002081 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002082 // special-case 2 points (common for drawing a single line)
2083 if (2 == count) {
2084 r.set(pts[0], pts[1]);
2085 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002086 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002087 }
senorblanco87e066e2015-10-28 11:23:36 -07002088 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2089 return;
2090 }
2091 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002092 }
reed@google.coma584aed2012-05-16 14:06:02 +00002093
halcanary96fcdcc2015-08-27 07:41:13 -07002094 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002096 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002097
reed@android.com8a1c16f2008-12-17 15:59:43 +00002098 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002099 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100 }
reed@google.com4b226022011-01-11 18:32:13 +00002101
reed@google.com4e2b3d32011-04-07 14:18:59 +00002102 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002103}
2104
reed4a167172016-08-18 17:15:25 -07002105static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
2106 return ((intptr_t)paint.getImageFilter() |
2107#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
2108 (intptr_t)canvas->getDrawFilter() |
2109#endif
2110 (intptr_t)paint.getLooper() ) != 0;
2111}
2112
reed41af9662015-01-05 07:49:08 -08002113void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002114 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002115 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002116 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002118 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2119 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2120 SkRect tmp(r);
2121 tmp.sort();
2122
senorblanco87e066e2015-10-28 11:23:36 -07002123 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2124 return;
2125 }
2126 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002127 }
reed@google.com4b226022011-01-11 18:32:13 +00002128
reed4a167172016-08-18 17:15:25 -07002129 if (needs_autodrawlooper(this, paint)) {
2130 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131
reed4a167172016-08-18 17:15:25 -07002132 while (iter.next()) {
2133 iter.fDevice->drawRect(iter, r, looper.paint());
2134 }
2135
2136 LOOPER_END
2137 } else {
2138 this->predrawNotify(bounds, &paint, false);
2139 SkDrawIter iter(this);
2140 while (iter.next()) {
2141 iter.fDevice->drawRect(iter, r, paint);
2142 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002144}
2145
msarett44df6512016-08-25 13:54:30 -07002146void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
2147 SkRect storage;
2148 SkRect regionRect = SkRect::Make(region.getBounds());
2149 const SkRect* bounds = nullptr;
2150 if (paint.canComputeFastBounds()) {
2151 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2152 return;
2153 }
2154 bounds = &regionRect;
2155 }
2156
2157 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
2158
2159 while (iter.next()) {
2160 iter.fDevice->drawRegion(iter, region, looper.paint());
2161 }
2162
2163 LOOPER_END
2164}
2165
reed41af9662015-01-05 07:49:08 -08002166void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002167 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002168 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002169 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002170 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002171 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2172 return;
2173 }
2174 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002175 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002176
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002177 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002178
2179 while (iter.next()) {
2180 iter.fDevice->drawOval(iter, oval, looper.paint());
2181 }
2182
2183 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002184}
2185
bsalomonac3aa242016-08-19 11:25:19 -07002186void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2187 SkScalar sweepAngle, bool useCenter,
2188 const SkPaint& paint) {
2189 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
2190 const SkRect* bounds = nullptr;
2191 if (paint.canComputeFastBounds()) {
2192 SkRect storage;
2193 // Note we're using the entire oval as the bounds.
2194 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2195 return;
2196 }
2197 bounds = &oval;
2198 }
2199
2200 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
2201
2202 while (iter.next()) {
2203 iter.fDevice->drawArc(iter, oval, startAngle, sweepAngle, useCenter, looper.paint());
2204 }
2205
2206 LOOPER_END
2207}
2208
reed41af9662015-01-05 07:49:08 -08002209void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002210 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002211 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002212 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002213 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002214 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2215 return;
2216 }
2217 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002218 }
2219
2220 if (rrect.isRect()) {
2221 // call the non-virtual version
2222 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002223 return;
2224 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002225 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002226 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2227 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002228 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002229
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002230 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002231
2232 while (iter.next()) {
2233 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2234 }
2235
2236 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002237}
2238
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002239void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2240 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002241 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002242 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002243 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002244 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2245 return;
2246 }
2247 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002248 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002249
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002250 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002251
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002252 while (iter.next()) {
2253 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2254 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002255
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002256 LOOPER_END
2257}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002258
reed41af9662015-01-05 07:49:08 -08002259void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002260 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002261 if (!path.isFinite()) {
2262 return;
2263 }
2264
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002265 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002266 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002267 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002268 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002269 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2270 return;
2271 }
2272 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002273 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002274
2275 const SkRect& r = path.getBounds();
2276 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002277 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002278 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002279 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002280 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002281 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002282
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002283 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002284
2285 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002286 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287 }
2288
reed@google.com4e2b3d32011-04-07 14:18:59 +00002289 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002290}
2291
reed262a71b2015-12-05 13:07:27 -08002292bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002293 if (!paint.getImageFilter()) {
2294 return false;
2295 }
2296
2297 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002298 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002299 return false;
2300 }
2301
2302 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2303 // Once we can filter and the filter will return a result larger than itself, we should be
2304 // able to remove this constraint.
2305 // skbug.com/4526
2306 //
2307 SkPoint pt;
2308 ctm.mapXY(x, y, &pt);
2309 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2310 return ir.contains(fMCRec->fRasterClip.getBounds());
2311}
2312
reeda85d4d02015-05-06 12:56:48 -07002313void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002314 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002315 SkRect bounds = SkRect::MakeXYWH(x, y,
2316 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002317 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002318 SkRect tmp = bounds;
2319 if (paint) {
2320 paint->computeFastBounds(tmp, &tmp);
2321 }
2322 if (this->quickReject(tmp)) {
2323 return;
2324 }
reeda85d4d02015-05-06 12:56:48 -07002325 }
halcanary9d524f22016-03-29 09:03:52 -07002326
reeda85d4d02015-05-06 12:56:48 -07002327 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002328 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002329 paint = lazy.init();
2330 }
reed262a71b2015-12-05 13:07:27 -08002331
reeda2217ef2016-07-20 06:04:34 -07002332 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002333 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2334 *paint);
2335 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002336 special = this->getDevice()->makeSpecial(image);
2337 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002338 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002339 }
2340 }
2341
reed262a71b2015-12-05 13:07:27 -08002342 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2343
reeda85d4d02015-05-06 12:56:48 -07002344 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002345 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002346 if (special) {
2347 SkPoint pt;
2348 iter.fMatrix->mapXY(x, y, &pt);
2349 iter.fDevice->drawSpecial(iter, special.get(),
2350 SkScalarRoundToInt(pt.fX),
2351 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002352 } else {
2353 iter.fDevice->drawImage(iter, image, x, y, pnt);
2354 }
reeda85d4d02015-05-06 12:56:48 -07002355 }
halcanary9d524f22016-03-29 09:03:52 -07002356
reeda85d4d02015-05-06 12:56:48 -07002357 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002358}
2359
reed41af9662015-01-05 07:49:08 -08002360void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002361 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002362 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002363 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002364 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002365 if (paint) {
2366 paint->computeFastBounds(dst, &storage);
2367 }
2368 if (this->quickReject(storage)) {
2369 return;
2370 }
reeda85d4d02015-05-06 12:56:48 -07002371 }
2372 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002373 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002374 paint = lazy.init();
2375 }
halcanary9d524f22016-03-29 09:03:52 -07002376
senorblancoc41e7e12015-12-07 12:51:30 -08002377 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002378 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002379
reeda85d4d02015-05-06 12:56:48 -07002380 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002381 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002382 }
halcanary9d524f22016-03-29 09:03:52 -07002383
reeda85d4d02015-05-06 12:56:48 -07002384 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002385}
2386
reed41af9662015-01-05 07:49:08 -08002387void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002388 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002389 SkDEBUGCODE(bitmap.validate();)
2390
reed33366972015-10-08 09:22:02 -07002391 if (bitmap.drawsNothing()) {
2392 return;
2393 }
2394
2395 SkLazyPaint lazy;
2396 if (nullptr == paint) {
2397 paint = lazy.init();
2398 }
2399
2400 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2401
2402 SkRect storage;
2403 const SkRect* bounds = nullptr;
2404 if (paint->canComputeFastBounds()) {
2405 bitmap.getBounds(&storage);
2406 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002407 SkRect tmp = storage;
2408 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2409 return;
2410 }
2411 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002412 }
reed@google.com4b226022011-01-11 18:32:13 +00002413
reeda2217ef2016-07-20 06:04:34 -07002414 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002415 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2416 *paint);
2417 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002418 special = this->getDevice()->makeSpecial(bitmap);
2419 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002420 drawAsSprite = false;
2421 }
2422 }
2423
reed262a71b2015-12-05 13:07:27 -08002424 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002425
2426 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002427 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002428 if (special) {
reed262a71b2015-12-05 13:07:27 -08002429 SkPoint pt;
2430 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002431 iter.fDevice->drawSpecial(iter, special.get(),
2432 SkScalarRoundToInt(pt.fX),
2433 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002434 } else {
2435 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2436 }
reed33366972015-10-08 09:22:02 -07002437 }
msarettfbfa2582016-08-12 08:29:08 -07002438
reed33366972015-10-08 09:22:02 -07002439 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002440}
2441
reed@google.com9987ec32011-09-07 11:57:52 +00002442// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002443void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002444 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002445 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002446 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002447 return;
2448 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002449
halcanary96fcdcc2015-08-27 07:41:13 -07002450 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002451 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002452 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2453 return;
2454 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002455 }
reed@google.com3d608122011-11-21 15:16:16 +00002456
reed@google.com33535f32012-09-25 15:37:50 +00002457 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002458 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002459 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002460 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002461
senorblancoc41e7e12015-12-07 12:51:30 -08002462 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002463 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002464
reed@google.com33535f32012-09-25 15:37:50 +00002465 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002466 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002467 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002468
reed@google.com33535f32012-09-25 15:37:50 +00002469 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002470}
2471
reed41af9662015-01-05 07:49:08 -08002472void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002473 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002474 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002475 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002476 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002477}
2478
reed4c21dc52015-06-25 12:32:03 -07002479void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2480 const SkPaint* paint) {
2481 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002482
halcanary96fcdcc2015-08-27 07:41:13 -07002483 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002484 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002485 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2486 return;
2487 }
reed@google.com3d608122011-11-21 15:16:16 +00002488 }
halcanary9d524f22016-03-29 09:03:52 -07002489
reed4c21dc52015-06-25 12:32:03 -07002490 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002491 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002492 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002493 }
halcanary9d524f22016-03-29 09:03:52 -07002494
senorblancoc41e7e12015-12-07 12:51:30 -08002495 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002496
reed4c21dc52015-06-25 12:32:03 -07002497 while (iter.next()) {
2498 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002499 }
halcanary9d524f22016-03-29 09:03:52 -07002500
reed4c21dc52015-06-25 12:32:03 -07002501 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002502}
2503
reed41af9662015-01-05 07:49:08 -08002504void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2505 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002506 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002507 SkDEBUGCODE(bitmap.validate();)
2508
halcanary96fcdcc2015-08-27 07:41:13 -07002509 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002510 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002511 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2512 return;
2513 }
reed4c21dc52015-06-25 12:32:03 -07002514 }
halcanary9d524f22016-03-29 09:03:52 -07002515
reed4c21dc52015-06-25 12:32:03 -07002516 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002517 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002518 paint = lazy.init();
2519 }
halcanary9d524f22016-03-29 09:03:52 -07002520
senorblancoc41e7e12015-12-07 12:51:30 -08002521 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002522
reed4c21dc52015-06-25 12:32:03 -07002523 while (iter.next()) {
2524 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2525 }
halcanary9d524f22016-03-29 09:03:52 -07002526
reed4c21dc52015-06-25 12:32:03 -07002527 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002528}
2529
msarett16882062016-08-16 09:31:08 -07002530void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2531 const SkPaint* paint) {
2532 if (nullptr == paint || paint->canComputeFastBounds()) {
2533 SkRect storage;
2534 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2535 return;
2536 }
2537 }
2538
2539 SkLazyPaint lazy;
2540 if (nullptr == paint) {
2541 paint = lazy.init();
2542 }
2543
2544 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2545
2546 while (iter.next()) {
2547 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2548 }
2549
2550 LOOPER_END
2551}
2552
2553void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2554 const SkRect& dst, const SkPaint* paint) {
2555 if (nullptr == paint || paint->canComputeFastBounds()) {
2556 SkRect storage;
2557 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2558 return;
2559 }
2560 }
2561
2562 SkLazyPaint lazy;
2563 if (nullptr == paint) {
2564 paint = lazy.init();
2565 }
2566
2567 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2568
2569 while (iter.next()) {
2570 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2571 }
2572
2573 LOOPER_END
2574}
2575
reed@google.comf67e4cf2011-03-15 20:56:58 +00002576class SkDeviceFilteredPaint {
2577public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002578 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002579 uint32_t filteredFlags = device->filterTextFlags(paint);
2580 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002581 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002582 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002583 fPaint = newPaint;
2584 } else {
2585 fPaint = &paint;
2586 }
2587 }
2588
reed@google.comf67e4cf2011-03-15 20:56:58 +00002589 const SkPaint& paint() const { return *fPaint; }
2590
2591private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002592 const SkPaint* fPaint;
2593 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002594};
2595
bungeman@google.com52c748b2011-08-22 21:30:43 +00002596void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2597 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002598 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002599 draw.fDevice->drawRect(draw, r, paint);
2600 } else {
2601 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002602 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002603 draw.fDevice->drawRect(draw, r, p);
2604 }
2605}
2606
2607void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2608 const char text[], size_t byteLength,
2609 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002610 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002611
2612 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002613 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002614 draw.fRC->isEmpty() ||
reed374772b2016-10-05 17:33:02 -07002615 (paint.getAlpha() == 0 && paint.isSrcOver())) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002616 return;
2617 }
2618
2619 SkScalar width = 0;
2620 SkPoint start;
2621
2622 start.set(0, 0); // to avoid warning
2623 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2624 SkPaint::kStrikeThruText_Flag)) {
2625 width = paint.measureText(text, byteLength);
2626
2627 SkScalar offsetX = 0;
2628 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2629 offsetX = SkScalarHalf(width);
2630 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2631 offsetX = width;
2632 }
2633 start.set(x - offsetX, y);
2634 }
2635
2636 if (0 == width) {
2637 return;
2638 }
2639
2640 uint32_t flags = paint.getFlags();
2641
2642 if (flags & (SkPaint::kUnderlineText_Flag |
2643 SkPaint::kStrikeThruText_Flag)) {
2644 SkScalar textSize = paint.getTextSize();
2645 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2646 SkRect r;
2647
2648 r.fLeft = start.fX;
2649 r.fRight = start.fX + width;
2650
2651 if (flags & SkPaint::kUnderlineText_Flag) {
2652 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2653 start.fY);
2654 r.fTop = offset;
2655 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002656 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002657 }
2658 if (flags & SkPaint::kStrikeThruText_Flag) {
2659 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2660 start.fY);
2661 r.fTop = offset;
2662 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002663 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002664 }
2665 }
2666}
2667
reed@google.come0d9ce82014-04-23 04:00:17 +00002668void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2669 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002670 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002671
2672 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002673 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002674 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002675 DrawTextDecorations(iter, dfp.paint(),
2676 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002677 }
2678
reed@google.com4e2b3d32011-04-07 14:18:59 +00002679 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002680}
2681
reed@google.come0d9ce82014-04-23 04:00:17 +00002682void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2683 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002684 SkPoint textOffset = SkPoint::Make(0, 0);
2685
halcanary96fcdcc2015-08-27 07:41:13 -07002686 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002687
reed@android.com8a1c16f2008-12-17 15:59:43 +00002688 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002689 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002690 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002691 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002692 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002693
reed@google.com4e2b3d32011-04-07 14:18:59 +00002694 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002695}
2696
reed@google.come0d9ce82014-04-23 04:00:17 +00002697void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2698 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002699
2700 SkPoint textOffset = SkPoint::Make(0, constY);
2701
halcanary96fcdcc2015-08-27 07:41:13 -07002702 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002703
reed@android.com8a1c16f2008-12-17 15:59:43 +00002704 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002705 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002706 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002707 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002708 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002709
reed@google.com4e2b3d32011-04-07 14:18:59 +00002710 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002711}
2712
reed@google.come0d9ce82014-04-23 04:00:17 +00002713void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2714 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002715 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002716
reed@android.com8a1c16f2008-12-17 15:59:43 +00002717 while (iter.next()) {
2718 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002719 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002720 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002721
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002722 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002723}
2724
reed45561a02016-07-07 12:47:17 -07002725void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2726 const SkRect* cullRect, const SkPaint& paint) {
2727 if (cullRect && this->quickReject(*cullRect)) {
2728 return;
2729 }
2730
2731 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2732
2733 while (iter.next()) {
2734 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2735 }
2736
2737 LOOPER_END
2738}
2739
fmalita00d5c2c2014-08-21 08:53:26 -07002740void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2741 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002742
fmalita85d5eb92015-03-04 11:20:12 -08002743 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002744 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002745 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002746 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002747 SkRect tmp;
2748 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2749 return;
2750 }
2751 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002752 }
2753
fmalita024f9962015-03-03 19:08:17 -08002754 // We cannot filter in the looper as we normally do, because the paint is
2755 // incomplete at this point (text-related attributes are embedded within blob run paints).
2756 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002757 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002758
fmalita85d5eb92015-03-04 11:20:12 -08002759 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002760
fmalitaaa1b9122014-08-28 14:32:24 -07002761 while (iter.next()) {
2762 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002763 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002764 }
2765
fmalitaaa1b9122014-08-28 14:32:24 -07002766 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002767
2768 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002769}
2770
reed@google.come0d9ce82014-04-23 04:00:17 +00002771// These will become non-virtual, so they always call the (virtual) onDraw... method
2772void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2773 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002774 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002775 if (byteLength) {
2776 this->onDrawText(text, byteLength, x, y, paint);
2777 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002778}
2779void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2780 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002781 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002782 if (byteLength) {
2783 this->onDrawPosText(text, byteLength, pos, paint);
2784 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002785}
2786void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2787 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002788 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002789 if (byteLength) {
2790 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2791 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002792}
2793void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2794 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002795 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002796 if (byteLength) {
2797 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2798 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002799}
reed45561a02016-07-07 12:47:17 -07002800void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2801 const SkRect* cullRect, const SkPaint& paint) {
2802 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2803 if (byteLength) {
2804 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2805 }
2806}
fmalita00d5c2c2014-08-21 08:53:26 -07002807void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2808 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002809 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002810 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002811 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002812}
reed@google.come0d9ce82014-04-23 04:00:17 +00002813
reed41af9662015-01-05 07:49:08 -08002814void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2815 const SkPoint verts[], const SkPoint texs[],
Mike Reedfaba3712016-11-03 14:45:31 -04002816 const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08002817 const uint16_t indices[], int indexCount,
2818 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002819 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002820 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002821
reed@android.com8a1c16f2008-12-17 15:59:43 +00002822 while (iter.next()) {
2823 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
Mike Reedfaba3712016-11-03 14:45:31 -04002824 colors, bmode, indices, indexCount,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002825 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002826 }
reed@google.com4b226022011-01-11 18:32:13 +00002827
reed@google.com4e2b3d32011-04-07 14:18:59 +00002828 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002829}
2830
dandovb3c9d1c2014-08-12 08:34:29 -07002831void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002832 const SkPoint texCoords[4], SkBlendMode bmode,
2833 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002834 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002835 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002836 return;
2837 }
mtklein6cfa73a2014-08-13 13:33:49 -07002838
Mike Reedfaba3712016-11-03 14:45:31 -04002839 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002840}
2841
2842void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002843 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002844 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002845 // Since a patch is always within the convex hull of the control points, we discard it when its
2846 // bounding rectangle is completely outside the current clip.
2847 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002848 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002849 if (this->quickReject(bounds)) {
2850 return;
2851 }
mtklein6cfa73a2014-08-13 13:33:49 -07002852
halcanary96fcdcc2015-08-27 07:41:13 -07002853 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002854
dandovecfff212014-08-04 10:02:00 -07002855 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002856 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002857 }
mtklein6cfa73a2014-08-13 13:33:49 -07002858
dandovecfff212014-08-04 10:02:00 -07002859 LOOPER_END
2860}
2861
reeda8db7282015-07-07 10:22:31 -07002862void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002863 RETURN_ON_NULL(dr);
2864 if (x || y) {
2865 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2866 this->onDrawDrawable(dr, &matrix);
2867 } else {
2868 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002869 }
2870}
2871
reeda8db7282015-07-07 10:22:31 -07002872void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002873 RETURN_ON_NULL(dr);
2874 if (matrix && matrix->isIdentity()) {
2875 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002876 }
reede3b38ce2016-01-08 09:18:44 -08002877 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002878}
2879
2880void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002881 // drawable bounds are no longer reliable (e.g. android displaylist)
2882 // so don't use them for quick-reject
reeda8db7282015-07-07 10:22:31 -07002883 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002884}
2885
reed71c3c762015-06-24 10:29:17 -07002886void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002887 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002888 const SkRect* cull, const SkPaint* paint) {
2889 if (cull && this->quickReject(*cull)) {
2890 return;
2891 }
2892
2893 SkPaint pnt;
2894 if (paint) {
2895 pnt = *paint;
2896 }
halcanary9d524f22016-03-29 09:03:52 -07002897
halcanary96fcdcc2015-08-27 07:41:13 -07002898 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002899 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002900 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002901 }
2902 LOOPER_END
2903}
2904
reedf70b5312016-03-04 16:36:20 -08002905void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2906 SkASSERT(key);
2907
2908 SkPaint paint;
2909 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2910 while (iter.next()) {
2911 iter.fDevice->drawAnnotation(iter, rect, key, value);
2912 }
2913 LOOPER_END
2914}
2915
reed@android.com8a1c16f2008-12-17 15:59:43 +00002916//////////////////////////////////////////////////////////////////////////////
2917// These methods are NOT virtual, and therefore must call back into virtual
2918// methods, rather than actually drawing themselves.
2919//////////////////////////////////////////////////////////////////////////////
2920
reed374772b2016-10-05 17:33:02 -07002921void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002922 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002923 SkPaint paint;
2924
2925 paint.setARGB(a, r, g, b);
reed374772b2016-10-05 17:33:02 -07002926 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002927 this->drawPaint(paint);
2928}
2929
reed374772b2016-10-05 17:33:02 -07002930void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002931 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002932 SkPaint paint;
2933
2934 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002935 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002936 this->drawPaint(paint);
2937}
2938
2939void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002940 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002941 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002942
reed@android.com8a1c16f2008-12-17 15:59:43 +00002943 pt.set(x, y);
2944 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2945}
2946
2947void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002948 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949 SkPoint pt;
2950 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002951
reed@android.com8a1c16f2008-12-17 15:59:43 +00002952 pt.set(x, y);
2953 paint.setColor(color);
2954 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2955}
2956
2957void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2958 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002959 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002960 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002961
reed@android.com8a1c16f2008-12-17 15:59:43 +00002962 pts[0].set(x0, y0);
2963 pts[1].set(x1, y1);
2964 this->drawPoints(kLines_PointMode, 2, pts, paint);
2965}
2966
2967void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2968 SkScalar right, SkScalar bottom,
2969 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002970 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002971 SkRect r;
2972
2973 r.set(left, top, right, bottom);
2974 this->drawRect(r, paint);
2975}
2976
2977void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2978 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002979 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002980 if (radius < 0) {
2981 radius = 0;
2982 }
2983
2984 SkRect r;
2985 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002986 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002987}
2988
2989void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2990 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002991 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002992 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002993 SkRRect rrect;
2994 rrect.setRectXY(r, rx, ry);
2995 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002996 } else {
2997 this->drawRect(r, paint);
2998 }
2999}
3000
reed@android.com8a1c16f2008-12-17 15:59:43 +00003001void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3002 SkScalar sweepAngle, bool useCenter,
3003 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003004 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
bsalomon21af9ca2016-08-25 12:29:23 -07003005 if (oval.isEmpty() || !sweepAngle) {
3006 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003007 }
bsalomon21af9ca2016-08-25 12:29:23 -07003008 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003009}
3010
3011void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3012 const SkPath& path, SkScalar hOffset,
3013 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003014 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003015 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003016
reed@android.com8a1c16f2008-12-17 15:59:43 +00003017 matrix.setTranslate(hOffset, vOffset);
3018 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3019}
3020
reed@android.comf76bacf2009-05-13 14:00:33 +00003021///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003022
3023/**
3024 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3025 * against the playback cost of recursing into the subpicture to get at its actual ops.
3026 *
3027 * For now we pick a conservatively small value, though measurement (and other heuristics like
3028 * the type of ops contained) may justify changing this value.
3029 */
3030#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003031
reedd5fa1a42014-08-09 11:08:05 -07003032void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003033 RETURN_ON_NULL(picture);
3034
reed1c2c4412015-04-30 13:09:24 -07003035 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003036 if (matrix && matrix->isIdentity()) {
3037 matrix = nullptr;
3038 }
3039 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3040 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3041 picture->playback(this);
3042 } else {
3043 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003044 }
3045}
robertphillips9b14f262014-06-04 05:40:44 -07003046
reedd5fa1a42014-08-09 11:08:05 -07003047void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3048 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003049 if (!paint || paint->canComputeFastBounds()) {
3050 SkRect bounds = picture->cullRect();
3051 if (paint) {
3052 paint->computeFastBounds(bounds, &bounds);
3053 }
3054 if (matrix) {
3055 matrix->mapRect(&bounds);
3056 }
3057 if (this->quickReject(bounds)) {
3058 return;
3059 }
3060 }
3061
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003062 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003063 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003064}
3065
vjiaoblack95302da2016-07-21 10:25:54 -07003066#ifdef SK_EXPERIMENTAL_SHADOWING
3067void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3068 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003069 const SkPaint* paint,
3070 const SkShadowParams& params) {
vjiaoblack95302da2016-07-21 10:25:54 -07003071 RETURN_ON_NULL(picture);
3072
3073 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3074
vjiaoblacke6f5d562016-08-25 06:30:23 -07003075 this->onDrawShadowedPicture(picture, matrix, paint, params);
vjiaoblack95302da2016-07-21 10:25:54 -07003076}
3077
3078void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3079 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003080 const SkPaint* paint,
3081 const SkShadowParams& params) {
vjiaoblack904527d2016-08-09 09:32:09 -07003082 if (!paint || paint->canComputeFastBounds()) {
3083 SkRect bounds = picture->cullRect();
3084 if (paint) {
3085 paint->computeFastBounds(bounds, &bounds);
3086 }
3087 if (matrix) {
3088 matrix->mapRect(&bounds);
3089 }
3090 if (this->quickReject(bounds)) {
3091 return;
3092 }
3093 }
3094
3095 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3096
vjiaoblacke6f5d562016-08-25 06:30:23 -07003097 sk_sp<SkImage> povDepthMap;
3098 sk_sp<SkImage> diffuseMap;
3099
vjiaoblack904527d2016-08-09 09:32:09 -07003100 // povDepthMap
3101 {
3102 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003103 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3104 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003105 sk_sp<SkLights> povLight = builder.finish();
3106
3107 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3108 picture->cullRect().height(),
3109 kBGRA_8888_SkColorType,
3110 kOpaque_SkAlphaType);
3111
3112 // Create a new surface (that matches the backend of canvas)
3113 // to create the povDepthMap
3114 sk_sp<SkSurface> surf(this->makeSurface(info));
3115
3116 // Wrap another SPFCanvas around the surface
3117 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3118 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3119
3120 // set the depth map canvas to have the light as the user's POV
3121 depthMapCanvas->setLights(std::move(povLight));
3122
3123 depthMapCanvas->drawPicture(picture);
vjiaoblack904527d2016-08-09 09:32:09 -07003124 povDepthMap = surf->makeImageSnapshot();
3125 }
3126
3127 // diffuseMap
3128 {
3129 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3130 picture->cullRect().height(),
3131 kBGRA_8888_SkColorType,
3132 kOpaque_SkAlphaType);
3133
3134 sk_sp<SkSurface> surf(this->makeSurface(info));
3135 surf->getCanvas()->drawPicture(picture);
3136
3137 diffuseMap = surf->makeImageSnapshot();
3138 }
vjiaoblack904527d2016-08-09 09:32:09 -07003139
3140 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3141 SkShader::kClamp_TileMode);
vjiaoblack904527d2016-08-09 09:32:09 -07003142 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3143 SkShader::kClamp_TileMode);
vjiaoblackb2796fd2016-09-09 09:22:39 -07003144
3145 // TODO: pass the depth to the shader in vertices, or uniforms
3146 // so we don't have to render depth and color separately
3147 for (int i = 0; i < fLights->numLights(); ++i) {
3148 // skip over ambient lights; they don't cast shadows
3149 // lights that have shadow maps do not need updating (because lights are immutable)
3150 sk_sp<SkImage> depthMap;
3151 SkISize shMapSize;
3152
3153 if (fLights->light(i).getShadowMap() != nullptr) {
3154 continue;
3155 }
3156
3157 if (fLights->light(i).isRadial()) {
3158 shMapSize.fHeight = 1;
3159 shMapSize.fWidth = (int) picture->cullRect().width();
3160
3161 SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1,
3162 kBGRA_8888_SkColorType,
3163 kOpaque_SkAlphaType);
3164
3165 // Create new surface (that matches the backend of canvas)
3166 // for each shadow map
3167 sk_sp<SkSurface> surf(this->makeSurface(info));
3168
3169 // Wrap another SPFCanvas around the surface
3170 SkCanvas* depthMapCanvas = surf->getCanvas();
3171
3172 SkLights::Builder builder;
3173 builder.add(fLights->light(i));
3174 sk_sp<SkLights> curLight = builder.finish();
3175
3176 sk_sp<SkShader> shadowMapShader;
3177 shadowMapShader = SkRadialShadowMapShader::Make(
3178 povDepthShader, curLight,
3179 (int) picture->cullRect().width(),
3180 (int) picture->cullRect().height());
3181
3182 SkPaint shadowMapPaint;
3183 shadowMapPaint.setShader(std::move(shadowMapShader));
3184
3185 depthMapCanvas->setLights(curLight);
3186
3187 depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(),
3188 diffuseMap->height()),
3189 shadowMapPaint);
3190
3191 depthMap = surf->makeImageSnapshot();
3192
3193 } else {
3194 // TODO: compute the correct size of the depth map from the light properties
3195 // TODO: maybe add a kDepth_8_SkColorType
3196 // TODO: find actual max depth of picture
3197 shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3198 fLights->light(i), 255,
3199 (int) picture->cullRect().width(),
3200 (int) picture->cullRect().height());
3201
3202 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3203 kBGRA_8888_SkColorType,
3204 kOpaque_SkAlphaType);
3205
3206 // Create a new surface (that matches the backend of canvas)
3207 // for each shadow map
3208 sk_sp<SkSurface> surf(this->makeSurface(info));
3209
3210 // Wrap another SPFCanvas around the surface
3211 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3212 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3213 depthMapCanvas->setShadowParams(params);
3214
3215 // set the depth map canvas to have the light we're drawing.
3216 SkLights::Builder builder;
3217 builder.add(fLights->light(i));
3218 sk_sp<SkLights> curLight = builder.finish();
3219 depthMapCanvas->setLights(std::move(curLight));
3220
3221 depthMapCanvas->drawPicture(picture);
3222 depthMap = surf->makeImageSnapshot();
3223 }
3224
3225 if (params.fType == SkShadowParams::kNoBlur_ShadowType) {
3226 fLights->light(i).setShadowMap(std::move(depthMap));
3227 } else if (params.fType == SkShadowParams::kVariance_ShadowType) {
3228 // we blur the variance map
3229 SkPaint blurPaint;
3230 blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius,
3231 params.fShadowRadius, nullptr));
3232
3233 SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3234 kBGRA_8888_SkColorType,
3235 kOpaque_SkAlphaType);
3236
3237 sk_sp<SkSurface> blurSurf(this->makeSurface(blurInfo));
3238
3239 blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
3240
3241 fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
3242 }
3243 }
3244
3245 SkPaint shadowPaint;
vjiaoblack904527d2016-08-09 09:32:09 -07003246 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3247 std::move(diffuseShader),
vjiaoblackb2796fd2016-09-09 09:22:39 -07003248 fLights,
vjiaoblack904527d2016-08-09 09:32:09 -07003249 diffuseMap->width(),
vjiaoblacke6f5d562016-08-25 06:30:23 -07003250 diffuseMap->height(),
3251 params);
vjiaoblack904527d2016-08-09 09:32:09 -07003252
3253 shadowPaint.setShader(shadowShader);
3254
3255 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003256}
3257#endif
3258
reed@android.com8a1c16f2008-12-17 15:59:43 +00003259///////////////////////////////////////////////////////////////////////////////
3260///////////////////////////////////////////////////////////////////////////////
3261
reed3aafe112016-08-18 12:45:34 -07003262SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003263 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003264
3265 SkASSERT(canvas);
3266
reed3aafe112016-08-18 12:45:34 -07003267 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003268 fDone = !fImpl->next();
3269}
3270
3271SkCanvas::LayerIter::~LayerIter() {
3272 fImpl->~SkDrawIter();
3273}
3274
3275void SkCanvas::LayerIter::next() {
3276 fDone = !fImpl->next();
3277}
3278
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003279SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003280 return fImpl->getDevice();
3281}
3282
3283const SkMatrix& SkCanvas::LayerIter::matrix() const {
3284 return fImpl->getMatrix();
3285}
3286
3287const SkPaint& SkCanvas::LayerIter::paint() const {
3288 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003289 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003290 paint = &fDefaultPaint;
3291 }
3292 return *paint;
3293}
3294
reed1e7f5e72016-04-27 07:49:17 -07003295const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003296int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3297int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003298
3299///////////////////////////////////////////////////////////////////////////////
3300
fmalitac3b589a2014-06-05 12:40:07 -07003301SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003302
3303///////////////////////////////////////////////////////////////////////////////
3304
3305static bool supported_for_raster_canvas(const SkImageInfo& info) {
3306 switch (info.alphaType()) {
3307 case kPremul_SkAlphaType:
3308 case kOpaque_SkAlphaType:
3309 break;
3310 default:
3311 return false;
3312 }
3313
3314 switch (info.colorType()) {
3315 case kAlpha_8_SkColorType:
3316 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003317 case kN32_SkColorType:
junov9e3dbdf2016-10-13 13:14:27 -07003318 case kRGBA_F16_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003319 break;
3320 default:
3321 return false;
3322 }
3323
3324 return true;
3325}
3326
Mike Reed5df49342016-11-12 08:06:55 -06003327std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
3328 size_t rowBytes) {
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003329 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003330 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003331 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003332
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003333 SkBitmap bitmap;
3334 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003335 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003336 }
Mike Reed5df49342016-11-12 08:06:55 -06003337 return skstd::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003338}
reedd5fa1a42014-08-09 11:08:05 -07003339
3340///////////////////////////////////////////////////////////////////////////////
3341
3342SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003343 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003344 : fCanvas(canvas)
3345 , fSaveCount(canvas->getSaveCount())
3346{
bsalomon49f085d2014-09-05 13:34:00 -07003347 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003348 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003349 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003350 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003351 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003352 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003353 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003354 canvas->save();
3355 }
mtklein6cfa73a2014-08-13 13:33:49 -07003356
bsalomon49f085d2014-09-05 13:34:00 -07003357 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003358 canvas->concat(*matrix);
3359 }
3360}
3361
3362SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3363 fCanvas->restoreToCount(fSaveCount);
3364}
reede8f30622016-03-23 18:59:25 -07003365
reed73603f32016-09-20 08:42:38 -07003366/////////////////////////////////
3367
3368const SkCanvas::ClipOp SkCanvas::kDifference_Op;
3369const SkCanvas::ClipOp SkCanvas::kIntersect_Op;
3370const SkCanvas::ClipOp SkCanvas::kUnion_Op;
3371const SkCanvas::ClipOp SkCanvas::kXOR_Op;
3372const SkCanvas::ClipOp SkCanvas::kReverseDifference_Op;
3373const SkCanvas::ClipOp SkCanvas::kReplace_Op;
3374
3375static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3376static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3377static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3378static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3379static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3380static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");