blob: eddf106ae4a405c1a18c1456109062efd9ed0d74 [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()) {
herbbf6d80a2016-11-15 06:26:56 -0800499 fLooperContext = fLooperContextAllocator.createWithIniter(
500 looper->contextSize(),
501 [&](void* buffer) {
502 return looper->createContext(canvas, buffer);
503 });
reed@google.com129ec222012-05-15 13:24:09 +0000504 fIsSimple = false;
505 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700506 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000507 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700508 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000509 }
510 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000511
reed@android.com8a1c16f2008-12-17 15:59:43 +0000512 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700513 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000514 fCanvas->internalRestore();
515 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000516 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000517 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000518
reed@google.com4e2b3d32011-04-07 14:18:59 +0000519 const SkPaint& paint() const {
520 SkASSERT(fPaint);
521 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000522 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000523
reed@google.com129ec222012-05-15 13:24:09 +0000524 bool next(SkDrawFilter::Type drawType) {
525 if (fDone) {
526 return false;
527 } else if (fIsSimple) {
528 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000529 return !fPaint->nothingToDraw();
530 } else {
531 return this->doNext(drawType);
532 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000533 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000534
reed@android.com8a1c16f2008-12-17 15:59:43 +0000535private:
reeddbc3cef2015-04-29 12:18:57 -0700536 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
537 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000538 SkCanvas* fCanvas;
539 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000540 SkDrawFilter* fFilter;
541 const SkPaint* fPaint;
542 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700543 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000544 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000545 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000546 SkDrawLooper::Context* fLooperContext;
547 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000548
549 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000550};
551
reed@google.com129ec222012-05-15 13:24:09 +0000552bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700553 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000554 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700555 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000556
reeddbc3cef2015-04-29 12:18:57 -0700557 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
558 *fLazyPaintInit.get() : fOrigPaint);
reed@google.com129ec222012-05-15 13:24:09 +0000559
reed5c476fb2015-04-20 08:04:21 -0700560 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700561 paint->setImageFilter(nullptr);
reed374772b2016-10-05 17:33:02 -0700562 paint->setBlendMode(SkBlendMode::kSrcOver);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000563 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000564
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000565 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000566 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000567 return false;
568 }
569 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000570 if (!fFilter->filter(paint, drawType)) {
571 fDone = true;
572 return false;
573 }
halcanary96fcdcc2015-08-27 07:41:13 -0700574 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000575 // no looper means we only draw once
576 fDone = true;
577 }
578 }
579 fPaint = paint;
580
581 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000582 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000583 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000584 }
585
586 // call this after any possible paint modifiers
587 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700588 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000589 return false;
590 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000591 return true;
592}
593
reed@android.com8a1c16f2008-12-17 15:59:43 +0000594////////// macros to place around the internal draw calls //////////////////
595
reed3aafe112016-08-18 12:45:34 -0700596#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
597 this->predrawNotify(); \
598 AutoDrawLooper looper(this, paint, skipLayerForFilter, bounds); \
599 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
reed262a71b2015-12-05 13:07:27 -0800600 SkDrawIter iter(this);
601
602
reed@google.com8926b162012-03-23 15:36:36 +0000603#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000604 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700605 AutoDrawLooper looper(this, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000606 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000607 SkDrawIter iter(this);
608
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000609#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000610 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700611 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000612 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000613 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000614
reedc83a2972015-07-16 07:40:45 -0700615#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
616 this->predrawNotify(bounds, &paint, auxOpaque); \
reed3aafe112016-08-18 12:45:34 -0700617 AutoDrawLooper looper(this, paint, false, bounds); \
reedc83a2972015-07-16 07:40:45 -0700618 while (looper.next(type)) { \
619 SkDrawIter iter(this);
620
reed@google.com4e2b3d32011-04-07 14:18:59 +0000621#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000622
623////////////////////////////////////////////////////////////////////////////
624
msarettfbfa2582016-08-12 08:29:08 -0700625static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
626 if (bounds.isEmpty()) {
627 return SkRect::MakeEmpty();
628 }
629
630 // Expand bounds out by 1 in case we are anti-aliasing. We store the
631 // bounds as floats to enable a faster quick reject implementation.
632 SkRect dst;
633 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
634 return dst;
635}
636
mtkleinfeaadee2015-04-08 11:25:48 -0700637void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
638 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700639 fClipStack->reset();
640 fMCRec->reset(bounds);
641
642 // We're peering through a lot of structs here. Only at this scope do we
643 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
644 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
msarettfbfa2582016-08-12 08:29:08 -0700645 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700646 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700647}
648
reedd9544982014-09-09 18:46:22 -0700649SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800650 if (device && device->forceConservativeRasterClip()) {
651 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
652 }
653 // Since init() is only called once by our constructors, it is safe to perform this
654 // const-cast.
655 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
656
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000657 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700658 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800659 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700660 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700661#ifdef SK_EXPERIMENTAL_SHADOWING
662 fLights = nullptr;
663#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664
halcanary385fe4d2015-08-26 13:07:48 -0700665 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700666
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700668 new (fMCRec) MCRec(fConservativeRasterClip);
msarett9637ea92016-08-18 14:03:30 -0700669 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000670
reeda499f902015-05-01 09:34:31 -0700671 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
672 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700673 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700674 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700675
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000677
halcanary96fcdcc2015-08-27 07:41:13 -0700678 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000679
reedf92c8662014-08-18 08:02:43 -0700680 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700681 // The root device and the canvas should always have the same pixel geometry
682 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700683 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800684 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700685 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700686 }
msarettfbfa2582016-08-12 08:29:08 -0700687
reedf92c8662014-08-18 08:02:43 -0700688 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000689}
690
reed@google.comcde92112011-07-06 20:00:52 +0000691SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000692 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700693 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800694 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000695{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000696 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000697
halcanary96fcdcc2015-08-27 07:41:13 -0700698 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000699}
700
reedd9544982014-09-09 18:46:22 -0700701static SkBitmap make_nopixels(int width, int height) {
702 SkBitmap bitmap;
703 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
704 return bitmap;
705}
706
707class SkNoPixelsBitmapDevice : public SkBitmapDevice {
708public:
robertphillipsfcf78292015-06-19 11:49:52 -0700709 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
710 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800711 {
712 this->setOrigin(bounds.x(), bounds.y());
713 }
reedd9544982014-09-09 18:46:22 -0700714
715private:
piotaixrb5fae932014-09-24 13:03:30 -0700716
reedd9544982014-09-09 18:46:22 -0700717 typedef SkBitmapDevice INHERITED;
718};
719
reed96a857e2015-01-25 10:33:58 -0800720SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000721 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800722 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800723 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000724{
725 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700726
halcanary385fe4d2015-08-26 13:07:48 -0700727 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
728 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700729}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000730
reed78e27682014-11-19 08:04:34 -0800731SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700732 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700733 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800734 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700735{
736 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700737
halcanary385fe4d2015-08-26 13:07:48 -0700738 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700739}
740
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000741SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000742 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700743 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800744 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000745{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700747
reedd9544982014-09-09 18:46:22 -0700748 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000749}
750
robertphillipsfcf78292015-06-19 11:49:52 -0700751SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
752 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700753 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800754 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700755{
756 inc_canvas();
757
758 this->init(device, flags);
759}
760
reed4a8126e2014-09-22 07:29:03 -0700761SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700762 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700763 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800764 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700765{
766 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700767
Hal Canary704cd322016-11-07 14:13:52 -0500768 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
769 this->init(device.get(), kDefault_InitFlags);
reed4a8126e2014-09-22 07:29:03 -0700770}
reed29c857d2014-09-21 10:25:07 -0700771
reed4a8126e2014-09-22 07:29:03 -0700772SkCanvas::SkCanvas(const SkBitmap& bitmap)
773 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
774 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800775 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700776{
777 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700778
Hal Canary704cd322016-11-07 14:13:52 -0500779 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
780 this->init(device.get(), kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000781}
782
783SkCanvas::~SkCanvas() {
784 // free up the contents of our deque
785 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000786
reed@android.com8a1c16f2008-12-17 15:59:43 +0000787 this->internalRestore(); // restore the last, since we're going away
788
halcanary385fe4d2015-08-26 13:07:48 -0700789 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000790
reed@android.com8a1c16f2008-12-17 15:59:43 +0000791 dec_canvas();
792}
793
fmalita53d9f1c2016-01-25 06:23:54 -0800794#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000795SkDrawFilter* SkCanvas::getDrawFilter() const {
796 return fMCRec->fFilter;
797}
798
799SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700800 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000801 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
802 return filter;
803}
fmalita77650002016-01-21 18:47:11 -0800804#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000805
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000806SkMetaData& SkCanvas::getMetaData() {
807 // metadata users are rare, so we lazily allocate it. If that changes we
808 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700809 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000810 fMetaData = new SkMetaData;
811 }
812 return *fMetaData;
813}
814
reed@android.com8a1c16f2008-12-17 15:59:43 +0000815///////////////////////////////////////////////////////////////////////////////
816
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000817void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700818 this->onFlush();
819}
820
821void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000822 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000823 if (device) {
824 device->flush();
825 }
826}
827
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000828SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000829 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000830 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
831}
832
senorblancoafc7cce2016-02-02 18:44:15 -0800833SkIRect SkCanvas::getTopLayerBounds() const {
834 SkBaseDevice* d = this->getTopDevice();
835 if (!d) {
836 return SkIRect::MakeEmpty();
837 }
838 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
839}
840
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000841SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000843 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000844 SkASSERT(rec && rec->fLayer);
845 return rec->fLayer->fDevice;
846}
847
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000848SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000849 if (updateMatrixClip) {
850 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
851 }
reed@google.com9266fed2011-03-30 00:18:03 +0000852 return fMCRec->fTopLayer->fDevice;
853}
854
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000855bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700856 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000857 return false;
858 }
859
860 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700861 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700862 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000863 return false;
864 }
865 weAllocated = true;
866 }
867
reedcf01e312015-05-23 19:14:51 -0700868 SkAutoPixmapUnlock unlocker;
869 if (bitmap->requestLock(&unlocker)) {
870 const SkPixmap& pm = unlocker.pixmap();
871 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
872 return true;
873 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000874 }
875
876 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700877 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000878 }
879 return false;
880}
reed@google.com51df9e32010-12-23 19:29:18 +0000881
bsalomon@google.comc6980972011-11-02 19:57:21 +0000882bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000883 SkIRect r = srcRect;
884 const SkISize size = this->getBaseLayerSize();
885 if (!r.intersect(0, 0, size.width(), size.height())) {
886 bitmap->reset();
887 return false;
888 }
889
reed84825042014-09-02 12:50:45 -0700890 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000891 // bitmap will already be reset.
892 return false;
893 }
894 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
895 bitmap->reset();
896 return false;
897 }
898 return true;
899}
900
reed96472de2014-12-10 09:53:42 -0800901bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000902 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000903 if (!device) {
904 return false;
905 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000906 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800907
reed96472de2014-12-10 09:53:42 -0800908 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
909 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000910 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000911 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000912
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000913 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800914 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000915}
916
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000917bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700918 SkAutoPixmapUnlock unlocker;
919 if (bitmap.requestLock(&unlocker)) {
920 const SkPixmap& pm = unlocker.pixmap();
921 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000922 }
923 return false;
924}
925
926bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
927 int x, int y) {
928 switch (origInfo.colorType()) {
929 case kUnknown_SkColorType:
930 case kIndex_8_SkColorType:
931 return false;
932 default:
933 break;
934 }
halcanary96fcdcc2015-08-27 07:41:13 -0700935 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000936 return false;
937 }
938
939 const SkISize size = this->getBaseLayerSize();
940 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
941 if (!target.intersect(0, 0, size.width(), size.height())) {
942 return false;
943 }
944
945 SkBaseDevice* device = this->getDevice();
946 if (!device) {
947 return false;
948 }
949
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000950 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700951 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000952
953 // if x or y are negative, then we have to adjust pixels
954 if (x > 0) {
955 x = 0;
956 }
957 if (y > 0) {
958 y = 0;
959 }
960 // here x,y are either 0 or negative
961 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
962
reed4af35f32014-06-27 17:47:49 -0700963 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700964 const bool completeOverwrite = info.dimensions() == size;
965 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700966
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000967 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000968 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000969}
reed@google.com51df9e32010-12-23 19:29:18 +0000970
reed@android.com8a1c16f2008-12-17 15:59:43 +0000971//////////////////////////////////////////////////////////////////////////////
972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973void SkCanvas::updateDeviceCMCache() {
974 if (fDeviceCMDirty) {
975 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700976 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000977 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000978
halcanary96fcdcc2015-08-27 07:41:13 -0700979 if (nullptr == layer->fNext) { // only one layer
reedde6c5312016-09-02 12:10:07 -0700980 layer->updateMC(totalMatrix, totalClip, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000981 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000982 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 do {
reedde6c5312016-09-02 12:10:07 -0700984 layer->updateMC(totalMatrix, clip, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700985 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000986 }
987 fDeviceCMDirty = false;
988 }
989}
990
reed@android.com8a1c16f2008-12-17 15:59:43 +0000991///////////////////////////////////////////////////////////////////////////////
992
reed2ff1fce2014-12-11 07:07:37 -0800993void SkCanvas::checkForDeferredSave() {
994 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800995 this->doSave();
996 }
997}
998
reedf0090cb2014-11-26 08:55:51 -0800999int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001000#ifdef SK_DEBUG
1001 int count = 0;
1002 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1003 for (;;) {
1004 const MCRec* rec = (const MCRec*)iter.next();
1005 if (!rec) {
1006 break;
1007 }
1008 count += 1 + rec->fDeferredSaveCount;
1009 }
1010 SkASSERT(count == fSaveCount);
1011#endif
1012 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001013}
1014
1015int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001016 fSaveCount += 1;
1017 fMCRec->fDeferredSaveCount += 1;
1018 return this->getSaveCount() - 1; // return our prev value
1019}
1020
1021void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001022 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001023
1024 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1025 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001026 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001027}
1028
1029void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001030 if (fMCRec->fDeferredSaveCount > 0) {
1031 SkASSERT(fSaveCount > 1);
1032 fSaveCount -= 1;
1033 fMCRec->fDeferredSaveCount -= 1;
1034 } else {
1035 // check for underflow
1036 if (fMCStack.count() > 1) {
1037 this->willRestore();
1038 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001039 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001040 this->internalRestore();
1041 this->didRestore();
1042 }
reedf0090cb2014-11-26 08:55:51 -08001043 }
1044}
1045
1046void SkCanvas::restoreToCount(int count) {
1047 // sanity check
1048 if (count < 1) {
1049 count = 1;
1050 }
mtkleinf0f14112014-12-12 08:46:25 -08001051
reedf0090cb2014-11-26 08:55:51 -08001052 int n = this->getSaveCount() - count;
1053 for (int i = 0; i < n; ++i) {
1054 this->restore();
1055 }
1056}
1057
reed2ff1fce2014-12-11 07:07:37 -08001058void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001059 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001060 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001062
reed687fa1c2015-04-07 08:00:56 -07001063 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001064}
1065
reed4960eee2015-12-18 07:09:18 -08001066bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed4960eee2015-12-18 07:09:18 -08001067 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001068}
1069
reed4960eee2015-12-18 07:09:18 -08001070bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001071 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001072 SkIRect clipBounds;
1073 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001074 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001075 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001076
reed96e657d2015-03-10 17:30:07 -07001077 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1078
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001079 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001080 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001081 if (bounds && !imageFilter->canComputeFastBounds()) {
1082 bounds = nullptr;
1083 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001084 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001085 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001086 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001088
reed96e657d2015-03-10 17:30:07 -07001089 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001090 r.roundOut(&ir);
1091 // early exit if the layer's bounds are clipped out
1092 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001093 if (BoundsAffectsClip(saveLayerFlags)) {
reed1f836ee2014-07-07 07:49:34 -07001094 fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001095 fDeviceClipBounds.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001096 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001097 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001098 }
1099 } else { // no user bounds, so just use the clip
1100 ir = clipBounds;
1101 }
reed180aec42015-03-11 10:39:04 -07001102 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001103
reed4960eee2015-12-18 07:09:18 -08001104 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001105 // Simplify the current clips since they will be applied properly during restore()
reed73603f32016-09-20 08:42:38 -07001106 fClipStack->clipDevRect(ir, kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001107 fMCRec->fRasterClip.setRect(ir);
msarettfbfa2582016-08-12 08:29:08 -07001108 fDeviceClipBounds = qr_clip_bounds(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001109 }
1110
1111 if (intersection) {
1112 *intersection = ir;
1113 }
1114 return true;
1115}
1116
reed4960eee2015-12-18 07:09:18 -08001117
reed4960eee2015-12-18 07:09:18 -08001118int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1119 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001120}
1121
reed70ee31b2015-12-10 13:44:45 -08001122int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001123 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1124}
1125
1126int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1127 SaveLayerRec rec(origRec);
1128 if (gIgnoreSaveLayerBounds) {
1129 rec.fBounds = nullptr;
1130 }
1131 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1132 fSaveCount += 1;
1133 this->internalSaveLayer(rec, strategy);
1134 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001135}
1136
reeda2217ef2016-07-20 06:04:34 -07001137void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1138 SkBaseDevice* dst, const SkMatrix& ctm,
1139 const SkClipStack* clipStack) {
1140 SkDraw draw;
1141 SkRasterClip rc;
1142 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1143 if (!dst->accessPixels(&draw.fDst)) {
1144 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001145 }
reeda2217ef2016-07-20 06:04:34 -07001146 draw.fMatrix = &SkMatrix::I();
1147 draw.fRC = &rc;
1148 draw.fClipStack = clipStack;
1149 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001150
1151 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001152 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001153
1154 int x = src->getOrigin().x() - dst->getOrigin().x();
1155 int y = src->getOrigin().y() - dst->getOrigin().y();
1156 auto special = src->snapSpecial();
1157 if (special) {
1158 dst->drawSpecial(draw, special.get(), x, y, p);
1159 }
robertphillips7354a4b2015-12-16 05:08:27 -08001160}
reed70ee31b2015-12-10 13:44:45 -08001161
reed129ed1c2016-02-22 06:42:31 -08001162static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1163 const SkPaint* paint) {
1164 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1165 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001166 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001167 const bool hasImageFilter = paint && paint->getImageFilter();
1168
1169 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1170 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1171 // force to L32
1172 return SkImageInfo::MakeN32(w, h, alphaType);
1173 } else {
1174 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001175 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001176 }
1177}
1178
reed4960eee2015-12-18 07:09:18 -08001179void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1180 const SkRect* bounds = rec.fBounds;
1181 const SkPaint* paint = rec.fPaint;
1182 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1183
reed8c30a812016-04-20 16:36:51 -07001184 SkLazyPaint lazyP;
1185 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1186 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001187 SkMatrix remainder;
1188 SkSize scale;
1189 /*
1190 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1191 * but they do handle scaling. To accommodate this, we do the following:
1192 *
1193 * 1. Stash off the current CTM
1194 * 2. Decompose the CTM into SCALE and REMAINDER
1195 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1196 * contains the REMAINDER
1197 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1198 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1199 * of the original imagefilter, and draw that (via drawSprite)
1200 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1201 *
1202 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1203 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1204 */
reed96a04f32016-04-25 09:25:15 -07001205 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001206 stashedMatrix.decomposeScale(&scale, &remainder))
1207 {
1208 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1209 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1210 SkPaint* p = lazyP.set(*paint);
1211 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1212 SkFilterQuality::kLow_SkFilterQuality,
1213 sk_ref_sp(imageFilter)));
1214 imageFilter = p->getImageFilter();
1215 paint = p;
1216 }
reed8c30a812016-04-20 16:36:51 -07001217
junov@chromium.orga907ac32012-02-24 21:54:07 +00001218 // do this before we create the layer. We don't call the public save() since
1219 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001220 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001221
1222 fDeviceCMDirty = true;
1223
1224 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001225 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001226 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001227 }
1228
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001229 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1230 // the clipRectBounds() call above?
1231 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001232 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001233 }
1234
reed4960eee2015-12-18 07:09:18 -08001235 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001236 SkPixelGeometry geo = fProps.pixelGeometry();
1237 if (paint) {
reed76033be2015-03-14 10:54:31 -07001238 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001239 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001240 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001241 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001242 }
1243 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001244
robertphillips5139e502016-07-19 05:10:40 -07001245 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001246 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001247 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001248 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001249 }
reedb2db8982014-11-13 12:41:02 -08001250
robertphillips5139e502016-07-19 05:10:40 -07001251 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001252 paint);
1253
Hal Canary704cd322016-11-07 14:13:52 -05001254 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001255 {
reed70ee31b2015-12-10 13:44:45 -08001256 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001257 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001258 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001259 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001260 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001261 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1262 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001263 return;
reed61f501f2015-04-29 08:34:00 -07001264 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001265 }
robertphillips5139e502016-07-19 05:10:40 -07001266 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001267
Hal Canary704cd322016-11-07 14:13:52 -05001268 DeviceCM* layer =
1269 new DeviceCM(newDevice.get(), paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001270
1271 layer->fNext = fMCRec->fTopLayer;
1272 fMCRec->fLayer = layer;
1273 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001274
1275 if (rec.fBackdrop) {
Hal Canary704cd322016-11-07 14:13:52 -05001276 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(),
reeda2217ef2016-07-20 06:04:34 -07001277 fMCRec->fMatrix, this->getClipStack());
1278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001279}
1280
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001281int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001282 if (0xFF == alpha) {
1283 return this->saveLayer(bounds, nullptr);
1284 } else {
1285 SkPaint tmpPaint;
1286 tmpPaint.setAlpha(alpha);
1287 return this->saveLayer(bounds, &tmpPaint);
1288 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001289}
1290
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291void SkCanvas::internalRestore() {
1292 SkASSERT(fMCStack.count() != 0);
1293
1294 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295
reed687fa1c2015-04-07 08:00:56 -07001296 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001297
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001298 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299 DeviceCM* layer = fMCRec->fLayer; // may be null
1300 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001301 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001302
1303 // now do the normal restore()
1304 fMCRec->~MCRec(); // balanced in save()
1305 fMCStack.pop_back();
1306 fMCRec = (MCRec*)fMCStack.back();
1307
1308 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1309 since if we're being recorded, we don't want to record this (the
1310 recorder will have already recorded the restore).
1311 */
bsalomon49f085d2014-09-05 13:34:00 -07001312 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001313 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001314 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001315 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001316 // restore what we smashed in internalSaveLayer
1317 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001318 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001319 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001320 delete layer;
reedb679ca82015-04-07 04:40:48 -07001321 } else {
1322 // we're at the root
reeda499f902015-05-01 09:34:31 -07001323 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001324 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001325 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001326 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001327 }
msarettfbfa2582016-08-12 08:29:08 -07001328
1329 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001330 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001331 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1332 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001333}
1334
reede8f30622016-03-23 18:59:25 -07001335sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001336 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001337 props = &fProps;
1338 }
1339 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001340}
1341
reede8f30622016-03-23 18:59:25 -07001342sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001343 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001344 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001345}
1346
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001347SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001348 return this->onImageInfo();
1349}
1350
1351SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001352 SkBaseDevice* dev = this->getDevice();
1353 if (dev) {
1354 return dev->imageInfo();
1355 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001356 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001357 }
1358}
1359
brianosman898235c2016-04-06 07:38:23 -07001360bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001361 return this->onGetProps(props);
1362}
1363
1364bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001365 SkBaseDevice* dev = this->getDevice();
1366 if (dev) {
1367 if (props) {
1368 *props = fProps;
1369 }
1370 return true;
1371 } else {
1372 return false;
1373 }
1374}
1375
reed6ceeebd2016-03-09 14:26:26 -08001376bool SkCanvas::peekPixels(SkPixmap* pmap) {
1377 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001378}
1379
reed884e97c2015-05-26 11:31:54 -07001380bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001381 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001382 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001383}
1384
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001385void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001386 SkPixmap pmap;
1387 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001388 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001389 }
1390 if (info) {
1391 *info = pmap.info();
1392 }
1393 if (rowBytes) {
1394 *rowBytes = pmap.rowBytes();
1395 }
1396 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001397 *origin = this->getTopDevice(false)->getOrigin();
1398 }
reed884e97c2015-05-26 11:31:54 -07001399 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001400}
1401
reed884e97c2015-05-26 11:31:54 -07001402bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001403 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001404 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001405}
1406
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408
reed7503d602016-07-15 14:23:29 -07001409void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001410 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001411 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001412 paint = &tmp;
1413 }
reed@google.com4b226022011-01-11 18:32:13 +00001414
reed@google.com8926b162012-03-23 15:36:36 +00001415 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001416
reed@android.com8a1c16f2008-12-17 15:59:43 +00001417 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001418 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001419 paint = &looper.paint();
1420 SkImageFilter* filter = paint->getImageFilter();
1421 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Hal Canary43513542016-10-14 11:19:43 -04001422 sk_sp<SkSpecialImage> specialImage;
1423 if (filter && (specialImage = srcDev->snapSpecial())) {
1424 dstDev->drawSpecial(iter, specialImage.get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001425 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001426 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001427 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428 }
reeda2217ef2016-07-20 06:04:34 -07001429
reed@google.com4e2b3d32011-04-07 14:18:59 +00001430 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001431}
1432
reed32704672015-12-16 08:27:10 -08001433/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001434
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001435void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001436 if (dx || dy) {
1437 this->checkForDeferredSave();
1438 fDeviceCMDirty = true;
1439 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001440
reedfe69b502016-09-12 06:31:48 -07001441 // Translate shouldn't affect the is-scale-translateness of the matrix.
1442 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001443
reedfe69b502016-09-12 06:31:48 -07001444 this->didTranslate(dx,dy);
1445 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001446}
1447
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001448void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001449 SkMatrix m;
1450 m.setScale(sx, sy);
1451 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001452}
1453
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001454void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001455 SkMatrix m;
1456 m.setRotate(degrees);
1457 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458}
1459
bungeman7438bfc2016-07-12 15:01:19 -07001460void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1461 SkMatrix m;
1462 m.setRotate(degrees, px, py);
1463 this->concat(m);
1464}
1465
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001466void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001467 SkMatrix m;
1468 m.setSkew(sx, sy);
1469 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001470}
1471
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001472void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001473 if (matrix.isIdentity()) {
1474 return;
1475 }
1476
reed2ff1fce2014-12-11 07:07:37 -08001477 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001478 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001479 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001480 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001481 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001482}
1483
reed8c30a812016-04-20 16:36:51 -07001484void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001486 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001487 fIsScaleTranslate = matrix.isScaleTranslate();
reed8c30a812016-04-20 16:36:51 -07001488}
1489
1490void SkCanvas::setMatrix(const SkMatrix& matrix) {
1491 this->checkForDeferredSave();
1492 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001493 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494}
1495
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001497 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001498}
1499
vjiaoblack95302da2016-07-21 10:25:54 -07001500#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001501void SkCanvas::translateZ(SkScalar z) {
1502 this->checkForDeferredSave();
1503 this->fMCRec->fCurDrawDepth += z;
1504 this->didTranslateZ(z);
1505}
1506
1507SkScalar SkCanvas::getZ() const {
1508 return this->fMCRec->fCurDrawDepth;
1509}
1510
vjiaoblack95302da2016-07-21 10:25:54 -07001511void SkCanvas::setLights(sk_sp<SkLights> lights) {
1512 this->fLights = lights;
1513}
1514
1515sk_sp<SkLights> SkCanvas::getLights() const {
1516 return this->fLights;
1517}
1518#endif
1519
reed@android.com8a1c16f2008-12-17 15:59:43 +00001520//////////////////////////////////////////////////////////////////////////////
1521
reed73603f32016-09-20 08:42:38 -07001522void SkCanvas::clipRect(const SkRect& rect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001523 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001524 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1525 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001526}
1527
reed73603f32016-09-20 08:42:38 -07001528void SkCanvas::onClipRect(const SkRect& rect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001529 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reedc64eff52015-11-21 12:39:45 -08001530 AutoValidateClip avc(this);
Brian Salomona3b45d42016-10-03 11:36:16 -04001531 fClipStack->clipRect(rect, fMCRec->fMatrix, op, isAA);
1532 fMCRec->fRasterClip.op(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1533 isAA);
reedc64eff52015-11-21 12:39:45 -08001534 fDeviceCMDirty = true;
msarettfbfa2582016-08-12 08:29:08 -07001535 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536}
1537
reed73603f32016-09-20 08:42:38 -07001538void SkCanvas::clipRRect(const SkRRect& rrect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001539 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001540 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001541 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001542 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1543 } else {
1544 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001545 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001546}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001547
reed73603f32016-09-20 08:42:38 -07001548void SkCanvas::onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001549 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001550
Brian Salomona3b45d42016-10-03 11:36:16 -04001551 fDeviceCMDirty = true;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001552
Brian Salomona3b45d42016-10-03 11:36:16 -04001553 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1554 fClipStack->clipRRect(rrect, fMCRec->fMatrix, op, isAA);
1555 fMCRec->fRasterClip.op(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1556 isAA);
1557 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1558 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001559}
1560
reed73603f32016-09-20 08:42:38 -07001561void SkCanvas::clipPath(const SkPath& path, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001562 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001563 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001564
1565 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1566 SkRect r;
1567 if (path.isRect(&r)) {
1568 this->onClipRect(r, op, edgeStyle);
1569 return;
1570 }
1571 SkRRect rrect;
1572 if (path.isOval(&r)) {
1573 rrect.setOval(r);
1574 this->onClipRRect(rrect, op, edgeStyle);
1575 return;
1576 }
1577 if (path.isRRect(&rrect)) {
1578 this->onClipRRect(rrect, op, edgeStyle);
1579 return;
1580 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001581 }
robertphillips39f05382015-11-24 09:30:12 -08001582
1583 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001584}
1585
reed73603f32016-09-20 08:42:38 -07001586void SkCanvas::onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001587 AutoValidateClip avc(this);
1588
reed@android.com8a1c16f2008-12-17 15:59:43 +00001589 fDeviceCMDirty = true;
Brian Salomona3b45d42016-10-03 11:36:16 -04001590 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001591
Brian Salomona3b45d42016-10-03 11:36:16 -04001592 fClipStack->clipPath(path, fMCRec->fMatrix, op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001593
Brian Salomona3b45d42016-10-03 11:36:16 -04001594 const SkPath* rasterClipPath = &path;
1595 const SkMatrix* matrix = &fMCRec->fMatrix;
1596 SkPath tempPath;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001597 if (fAllowSimplifyClip) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001598 isAA = getClipStack()->asPath(&tempPath);
1599 rasterClipPath = &tempPath;
1600 matrix = &SkMatrix::I();
reed73603f32016-09-20 08:42:38 -07001601 op = kReplace_Op;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001602 }
Brian Salomona3b45d42016-10-03 11:36:16 -04001603 fMCRec->fRasterClip.op(*rasterClipPath, *matrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1604 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001605 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001606}
1607
reed73603f32016-09-20 08:42:38 -07001608void SkCanvas::clipRegion(const SkRegion& rgn, ClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001609 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001610 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001611}
1612
reed73603f32016-09-20 08:42:38 -07001613void SkCanvas::onClipRegion(const SkRegion& rgn, ClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001614 AutoValidateClip avc(this);
1615
reed@android.com8a1c16f2008-12-17 15:59:43 +00001616 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617
reed@google.com5c3d1472011-02-22 19:12:23 +00001618 // todo: signal fClipStack that we have a region, and therefore (I guess)
1619 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001620 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001621
reed73603f32016-09-20 08:42:38 -07001622 fMCRec->fRasterClip.op(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001623 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001624}
1625
reed@google.com819c9212011-02-23 18:56:55 +00001626#ifdef SK_DEBUG
1627void SkCanvas::validateClip() const {
1628 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001629 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001630 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001631 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001632 return;
1633 }
1634
reed@google.com819c9212011-02-23 18:56:55 +00001635 SkIRect ir;
1636 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001637 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001638
reed687fa1c2015-04-07 08:00:56 -07001639 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001640 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001641 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001642 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001643 case SkClipStack::Element::kRect_Type:
1644 element->getRect().round(&ir);
reed73603f32016-09-20 08:42:38 -07001645 tmpClip.op(ir, (SkRegion::Op)element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001646 break;
1647 case SkClipStack::Element::kEmpty_Type:
1648 tmpClip.setEmpty();
1649 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001650 default: {
1651 SkPath path;
1652 element->asPath(&path);
Brian Salomona3b45d42016-10-03 11:36:16 -04001653 tmpClip.op(path, SkMatrix::I(), this->getTopLayerBounds(),
1654 (SkRegion::Op)element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001655 break;
1656 }
reed@google.com819c9212011-02-23 18:56:55 +00001657 }
1658 }
reed@google.com819c9212011-02-23 18:56:55 +00001659}
1660#endif
1661
reed@google.com90c07ea2012-04-13 13:50:27 +00001662void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001663 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001664 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001665
halcanary96fcdcc2015-08-27 07:41:13 -07001666 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001667 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001668 }
1669}
1670
reed@google.com5c3d1472011-02-22 19:12:23 +00001671///////////////////////////////////////////////////////////////////////////////
1672
reed@google.com754de5f2014-02-24 19:38:20 +00001673bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001674 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001675}
1676
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001677bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001678 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001679}
1680
msarettfbfa2582016-08-12 08:29:08 -07001681static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1682#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1683 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1684 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1685 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1686 return 0xF != _mm_movemask_ps(mask);
1687#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1688 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1689 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1690 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1691 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1692#else
1693 SkRect devRectAsRect;
1694 SkRect devClipAsRect;
1695 devRect.store(&devRectAsRect.fLeft);
1696 devClip.store(&devClipAsRect.fLeft);
1697 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1698#endif
1699}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001700
msarettfbfa2582016-08-12 08:29:08 -07001701// It's important for this function to not be inlined. Otherwise the compiler will share code
1702// between the fast path and the slow path, resulting in two slow paths.
1703static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1704 const SkMatrix& matrix) {
1705 SkRect deviceRect;
1706 matrix.mapRect(&deviceRect, src);
1707 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1708}
1709
1710bool SkCanvas::quickReject(const SkRect& src) const {
1711#ifdef SK_DEBUG
1712 // Verify that fDeviceClipBounds are set properly.
1713 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001714 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001715 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001716 } else {
msarettfbfa2582016-08-12 08:29:08 -07001717 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001718 }
msarettfbfa2582016-08-12 08:29:08 -07001719
msarett9637ea92016-08-18 14:03:30 -07001720 // Verify that fIsScaleTranslate is set properly.
1721 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001722#endif
1723
msarett9637ea92016-08-18 14:03:30 -07001724 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001725 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1726 }
1727
1728 // We inline the implementation of mapScaleTranslate() for the fast path.
1729 float sx = fMCRec->fMatrix.getScaleX();
1730 float sy = fMCRec->fMatrix.getScaleY();
1731 float tx = fMCRec->fMatrix.getTranslateX();
1732 float ty = fMCRec->fMatrix.getTranslateY();
1733 Sk4f scale(sx, sy, sx, sy);
1734 Sk4f trans(tx, ty, tx, ty);
1735
1736 // Apply matrix.
1737 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1738
1739 // Make sure left < right, top < bottom.
1740 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1741 Sk4f min = Sk4f::Min(ltrb, rblt);
1742 Sk4f max = Sk4f::Max(ltrb, rblt);
1743 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1744 // ARM this sequence generates the fastest (a single instruction).
1745 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1746
1747 // Check if the device rect is NaN or outside the clip.
1748 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001749}
1750
reed@google.com3b3e8952012-08-16 20:53:31 +00001751bool SkCanvas::quickReject(const SkPath& path) const {
1752 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753}
1754
reed@google.com3b3e8952012-08-16 20:53:31 +00001755bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001756 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001757 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758 return false;
1759 }
1760
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001761 SkMatrix inverse;
1762 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001763 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001764 if (bounds) {
1765 bounds->setEmpty();
1766 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001767 return false;
1768 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769
bsalomon49f085d2014-09-05 13:34:00 -07001770 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001771 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001772 // adjust it outwards in case we are antialiasing
1773 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001774
reed@google.com8f4d2302013-12-17 16:44:46 +00001775 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1776 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001777 inverse.mapRect(bounds, r);
1778 }
1779 return true;
1780}
1781
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001782bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001783 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001784 if (clip.isEmpty()) {
1785 if (bounds) {
1786 bounds->setEmpty();
1787 }
1788 return false;
1789 }
1790
bsalomon49f085d2014-09-05 13:34:00 -07001791 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001792 *bounds = clip.getBounds();
1793 }
1794 return true;
1795}
1796
reed@android.com8a1c16f2008-12-17 15:59:43 +00001797const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001798 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001799}
1800
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001801const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001802 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001803}
1804
Brian Osman11052242016-10-27 14:47:55 -04001805GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001806 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001807 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001808}
1809
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001810GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001811 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001812 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001813}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001814
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001815void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1816 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001817 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001818 if (outer.isEmpty()) {
1819 return;
1820 }
1821 if (inner.isEmpty()) {
1822 this->drawRRect(outer, paint);
1823 return;
1824 }
1825
1826 // We don't have this method (yet), but technically this is what we should
1827 // be able to assert...
1828 // SkASSERT(outer.contains(inner));
1829 //
1830 // For now at least check for containment of bounds
1831 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1832
1833 this->onDrawDRRect(outer, inner, paint);
1834}
1835
reed41af9662015-01-05 07:49:08 -08001836// These need to stop being virtual -- clients need to override the onDraw... versions
1837
1838void SkCanvas::drawPaint(const SkPaint& paint) {
1839 this->onDrawPaint(paint);
1840}
1841
1842void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1843 this->onDrawRect(r, paint);
1844}
1845
msarettdca352e2016-08-26 06:37:45 -07001846void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
1847 if (region.isEmpty()) {
1848 return;
1849 }
1850
1851 if (region.isRect()) {
1852 return this->drawIRect(region.getBounds(), paint);
1853 }
1854
1855 this->onDrawRegion(region, paint);
1856}
1857
reed41af9662015-01-05 07:49:08 -08001858void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1859 this->onDrawOval(r, paint);
1860}
1861
1862void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1863 this->onDrawRRect(rrect, paint);
1864}
1865
1866void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1867 this->onDrawPoints(mode, count, pts, paint);
1868}
1869
1870void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001871 const SkPoint texs[], const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08001872 const uint16_t indices[], int indexCount, const SkPaint& paint) {
Mike Reedfaba3712016-11-03 14:45:31 -04001873 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, bmode,
reed41af9662015-01-05 07:49:08 -08001874 indices, indexCount, paint);
1875}
1876
1877void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1878 this->onDrawPath(path, paint);
1879}
1880
reeda85d4d02015-05-06 12:56:48 -07001881void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001882 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001883 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001884}
1885
reede47829b2015-08-06 10:02:53 -07001886void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1887 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001888 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001889 if (dst.isEmpty() || src.isEmpty()) {
1890 return;
1891 }
1892 this->onDrawImageRect(image, &src, dst, paint, constraint);
1893}
reed41af9662015-01-05 07:49:08 -08001894
reed84984ef2015-07-17 07:09:43 -07001895void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1896 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001897 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001898 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001899}
1900
reede47829b2015-08-06 10:02:53 -07001901void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1902 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001903 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001904 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1905 constraint);
1906}
reede47829b2015-08-06 10:02:53 -07001907
reed4c21dc52015-06-25 12:32:03 -07001908void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1909 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001910 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001911 if (dst.isEmpty()) {
1912 return;
1913 }
msarett552bca92016-08-03 06:53:26 -07001914 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1915 this->onDrawImageNine(image, center, dst, paint);
1916 } else {
reede47829b2015-08-06 10:02:53 -07001917 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001918 }
reed4c21dc52015-06-25 12:32:03 -07001919}
1920
msarett16882062016-08-16 09:31:08 -07001921void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1922 const SkPaint* paint) {
1923 RETURN_ON_NULL(image);
1924 if (dst.isEmpty()) {
1925 return;
1926 }
msarett71df2d72016-09-30 12:41:42 -07001927
1928 SkIRect bounds;
1929 Lattice latticePlusBounds = lattice;
1930 if (!latticePlusBounds.fBounds) {
1931 bounds = SkIRect::MakeWH(image->width(), image->height());
1932 latticePlusBounds.fBounds = &bounds;
1933 }
1934
1935 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
1936 this->onDrawImageLattice(image, latticePlusBounds, dst, paint);
msarett16882062016-08-16 09:31:08 -07001937 } else {
1938 this->drawImageRect(image, dst, paint);
1939 }
1940}
1941
reed41af9662015-01-05 07:49:08 -08001942void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001943 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001944 return;
1945 }
reed41af9662015-01-05 07:49:08 -08001946 this->onDrawBitmap(bitmap, dx, dy, paint);
1947}
1948
reede47829b2015-08-06 10:02:53 -07001949void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001950 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001951 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001952 return;
1953 }
reede47829b2015-08-06 10:02:53 -07001954 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001955}
1956
reed84984ef2015-07-17 07:09:43 -07001957void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1958 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001959 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001960}
1961
reede47829b2015-08-06 10:02:53 -07001962void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1963 SrcRectConstraint constraint) {
1964 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1965 constraint);
1966}
reede47829b2015-08-06 10:02:53 -07001967
reed41af9662015-01-05 07:49:08 -08001968void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1969 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001970 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001971 return;
1972 }
msarett552bca92016-08-03 06:53:26 -07001973 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
1974 this->onDrawBitmapNine(bitmap, center, dst, paint);
1975 } else {
reeda5517e22015-07-14 10:54:12 -07001976 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001977 }
reed41af9662015-01-05 07:49:08 -08001978}
1979
msarettc573a402016-08-02 08:05:56 -07001980void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
1981 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07001982 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07001983 return;
1984 }
msarett71df2d72016-09-30 12:41:42 -07001985
1986 SkIRect bounds;
1987 Lattice latticePlusBounds = lattice;
1988 if (!latticePlusBounds.fBounds) {
1989 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1990 latticePlusBounds.fBounds = &bounds;
1991 }
1992
1993 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
1994 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, paint);
msarett552bca92016-08-03 06:53:26 -07001995 } else {
msarett16882062016-08-16 09:31:08 -07001996 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07001997 }
msarettc573a402016-08-02 08:05:56 -07001998}
1999
reed71c3c762015-06-24 10:29:17 -07002000void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04002001 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07002002 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002003 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002004 if (count <= 0) {
2005 return;
2006 }
2007 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002008 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002009 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002010}
2011
reedf70b5312016-03-04 16:36:20 -08002012void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2013 if (key) {
2014 this->onDrawAnnotation(rect, key, value);
2015 }
2016}
2017
reede47829b2015-08-06 10:02:53 -07002018void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2019 const SkPaint* paint, SrcRectConstraint constraint) {
2020 if (src) {
2021 this->drawImageRect(image, *src, dst, paint, constraint);
2022 } else {
2023 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2024 dst, paint, constraint);
2025 }
2026}
2027void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2028 const SkPaint* paint, SrcRectConstraint constraint) {
2029 if (src) {
2030 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2031 } else {
2032 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2033 dst, paint, constraint);
2034 }
2035}
2036
tomhudsoncb3bd182016-05-18 07:24:16 -07002037void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2038 SkIRect layer_bounds = this->getTopLayerBounds();
2039 if (matrix) {
2040 *matrix = this->getTotalMatrix();
2041 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2042 }
2043 if (clip_bounds) {
2044 this->getClipDeviceBounds(clip_bounds);
2045 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2046 }
2047}
2048
reed@android.com8a1c16f2008-12-17 15:59:43 +00002049//////////////////////////////////////////////////////////////////////////////
2050// These are the virtual drawing methods
2051//////////////////////////////////////////////////////////////////////////////
2052
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002053void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002054 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002055 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2056 }
2057}
2058
reed41af9662015-01-05 07:49:08 -08002059void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002060 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002061 this->internalDrawPaint(paint);
2062}
2063
2064void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002065 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066
2067 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002068 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069 }
2070
reed@google.com4e2b3d32011-04-07 14:18:59 +00002071 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072}
2073
reed41af9662015-01-05 07:49:08 -08002074void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2075 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002076 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002077 if ((long)count <= 0) {
2078 return;
2079 }
2080
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002081 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002082 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002083 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002084 // special-case 2 points (common for drawing a single line)
2085 if (2 == count) {
2086 r.set(pts[0], pts[1]);
2087 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002088 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002089 }
senorblanco87e066e2015-10-28 11:23:36 -07002090 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2091 return;
2092 }
2093 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002094 }
reed@google.coma584aed2012-05-16 14:06:02 +00002095
halcanary96fcdcc2015-08-27 07:41:13 -07002096 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002098 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002099
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002101 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102 }
reed@google.com4b226022011-01-11 18:32:13 +00002103
reed@google.com4e2b3d32011-04-07 14:18:59 +00002104 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002105}
2106
reed4a167172016-08-18 17:15:25 -07002107static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
2108 return ((intptr_t)paint.getImageFilter() |
2109#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
2110 (intptr_t)canvas->getDrawFilter() |
2111#endif
2112 (intptr_t)paint.getLooper() ) != 0;
2113}
2114
reed41af9662015-01-05 07:49:08 -08002115void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002116 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002117 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002118 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002120 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2121 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2122 SkRect tmp(r);
2123 tmp.sort();
2124
senorblanco87e066e2015-10-28 11:23:36 -07002125 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2126 return;
2127 }
2128 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002129 }
reed@google.com4b226022011-01-11 18:32:13 +00002130
reed4a167172016-08-18 17:15:25 -07002131 if (needs_autodrawlooper(this, paint)) {
2132 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002133
reed4a167172016-08-18 17:15:25 -07002134 while (iter.next()) {
2135 iter.fDevice->drawRect(iter, r, looper.paint());
2136 }
2137
2138 LOOPER_END
2139 } else {
2140 this->predrawNotify(bounds, &paint, false);
2141 SkDrawIter iter(this);
2142 while (iter.next()) {
2143 iter.fDevice->drawRect(iter, r, paint);
2144 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002146}
2147
msarett44df6512016-08-25 13:54:30 -07002148void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
2149 SkRect storage;
2150 SkRect regionRect = SkRect::Make(region.getBounds());
2151 const SkRect* bounds = nullptr;
2152 if (paint.canComputeFastBounds()) {
2153 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2154 return;
2155 }
2156 bounds = &regionRect;
2157 }
2158
2159 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
2160
2161 while (iter.next()) {
2162 iter.fDevice->drawRegion(iter, region, looper.paint());
2163 }
2164
2165 LOOPER_END
2166}
2167
reed41af9662015-01-05 07:49:08 -08002168void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002169 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002170 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002171 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002172 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002173 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2174 return;
2175 }
2176 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002177 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002178
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002179 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002180
2181 while (iter.next()) {
2182 iter.fDevice->drawOval(iter, oval, looper.paint());
2183 }
2184
2185 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002186}
2187
bsalomonac3aa242016-08-19 11:25:19 -07002188void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2189 SkScalar sweepAngle, bool useCenter,
2190 const SkPaint& paint) {
2191 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
2192 const SkRect* bounds = nullptr;
2193 if (paint.canComputeFastBounds()) {
2194 SkRect storage;
2195 // Note we're using the entire oval as the bounds.
2196 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2197 return;
2198 }
2199 bounds = &oval;
2200 }
2201
2202 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
2203
2204 while (iter.next()) {
2205 iter.fDevice->drawArc(iter, oval, startAngle, sweepAngle, useCenter, looper.paint());
2206 }
2207
2208 LOOPER_END
2209}
2210
reed41af9662015-01-05 07:49:08 -08002211void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002212 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002213 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002214 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002215 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002216 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2217 return;
2218 }
2219 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002220 }
2221
2222 if (rrect.isRect()) {
2223 // call the non-virtual version
2224 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002225 return;
2226 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002227 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002228 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2229 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002230 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002231
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002232 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002233
2234 while (iter.next()) {
2235 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2236 }
2237
2238 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002239}
2240
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002241void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2242 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002243 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002244 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002245 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002246 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2247 return;
2248 }
2249 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002250 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002251
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002252 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002253
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002254 while (iter.next()) {
2255 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2256 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002257
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002258 LOOPER_END
2259}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002260
reed41af9662015-01-05 07:49:08 -08002261void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002262 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002263 if (!path.isFinite()) {
2264 return;
2265 }
2266
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002267 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002268 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002269 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002270 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002271 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2272 return;
2273 }
2274 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002275 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002276
2277 const SkRect& r = path.getBounds();
2278 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002279 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002280 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002281 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002282 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002283 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002284
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002285 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286
2287 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002288 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002289 }
2290
reed@google.com4e2b3d32011-04-07 14:18:59 +00002291 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292}
2293
reed262a71b2015-12-05 13:07:27 -08002294bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002295 if (!paint.getImageFilter()) {
2296 return false;
2297 }
2298
2299 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002300 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002301 return false;
2302 }
2303
2304 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2305 // Once we can filter and the filter will return a result larger than itself, we should be
2306 // able to remove this constraint.
2307 // skbug.com/4526
2308 //
2309 SkPoint pt;
2310 ctm.mapXY(x, y, &pt);
2311 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2312 return ir.contains(fMCRec->fRasterClip.getBounds());
2313}
2314
reeda85d4d02015-05-06 12:56:48 -07002315void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002316 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002317 SkRect bounds = SkRect::MakeXYWH(x, y,
2318 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002319 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002320 SkRect tmp = bounds;
2321 if (paint) {
2322 paint->computeFastBounds(tmp, &tmp);
2323 }
2324 if (this->quickReject(tmp)) {
2325 return;
2326 }
reeda85d4d02015-05-06 12:56:48 -07002327 }
halcanary9d524f22016-03-29 09:03:52 -07002328
reeda85d4d02015-05-06 12:56:48 -07002329 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002330 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002331 paint = lazy.init();
2332 }
reed262a71b2015-12-05 13:07:27 -08002333
reeda2217ef2016-07-20 06:04:34 -07002334 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002335 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2336 *paint);
2337 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002338 special = this->getDevice()->makeSpecial(image);
2339 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002340 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002341 }
2342 }
2343
reed262a71b2015-12-05 13:07:27 -08002344 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2345
reeda85d4d02015-05-06 12:56:48 -07002346 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002347 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002348 if (special) {
2349 SkPoint pt;
2350 iter.fMatrix->mapXY(x, y, &pt);
2351 iter.fDevice->drawSpecial(iter, special.get(),
2352 SkScalarRoundToInt(pt.fX),
2353 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002354 } else {
2355 iter.fDevice->drawImage(iter, image, x, y, pnt);
2356 }
reeda85d4d02015-05-06 12:56:48 -07002357 }
halcanary9d524f22016-03-29 09:03:52 -07002358
reeda85d4d02015-05-06 12:56:48 -07002359 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002360}
2361
reed41af9662015-01-05 07:49:08 -08002362void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002363 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002364 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002365 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002366 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002367 if (paint) {
2368 paint->computeFastBounds(dst, &storage);
2369 }
2370 if (this->quickReject(storage)) {
2371 return;
2372 }
reeda85d4d02015-05-06 12:56:48 -07002373 }
2374 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002375 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002376 paint = lazy.init();
2377 }
halcanary9d524f22016-03-29 09:03:52 -07002378
senorblancoc41e7e12015-12-07 12:51:30 -08002379 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002380 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002381
reeda85d4d02015-05-06 12:56:48 -07002382 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002383 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002384 }
halcanary9d524f22016-03-29 09:03:52 -07002385
reeda85d4d02015-05-06 12:56:48 -07002386 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002387}
2388
reed41af9662015-01-05 07:49:08 -08002389void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002390 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002391 SkDEBUGCODE(bitmap.validate();)
2392
reed33366972015-10-08 09:22:02 -07002393 if (bitmap.drawsNothing()) {
2394 return;
2395 }
2396
2397 SkLazyPaint lazy;
2398 if (nullptr == paint) {
2399 paint = lazy.init();
2400 }
2401
2402 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2403
2404 SkRect storage;
2405 const SkRect* bounds = nullptr;
2406 if (paint->canComputeFastBounds()) {
2407 bitmap.getBounds(&storage);
2408 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002409 SkRect tmp = storage;
2410 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2411 return;
2412 }
2413 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002414 }
reed@google.com4b226022011-01-11 18:32:13 +00002415
reeda2217ef2016-07-20 06:04:34 -07002416 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002417 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2418 *paint);
2419 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002420 special = this->getDevice()->makeSpecial(bitmap);
2421 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002422 drawAsSprite = false;
2423 }
2424 }
2425
reed262a71b2015-12-05 13:07:27 -08002426 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002427
2428 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002429 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002430 if (special) {
reed262a71b2015-12-05 13:07:27 -08002431 SkPoint pt;
2432 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002433 iter.fDevice->drawSpecial(iter, special.get(),
2434 SkScalarRoundToInt(pt.fX),
2435 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002436 } else {
2437 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2438 }
reed33366972015-10-08 09:22:02 -07002439 }
msarettfbfa2582016-08-12 08:29:08 -07002440
reed33366972015-10-08 09:22:02 -07002441 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002442}
2443
reed@google.com9987ec32011-09-07 11:57:52 +00002444// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002445void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002446 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002447 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002448 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002449 return;
2450 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002451
halcanary96fcdcc2015-08-27 07:41:13 -07002452 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002453 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002454 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2455 return;
2456 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002457 }
reed@google.com3d608122011-11-21 15:16:16 +00002458
reed@google.com33535f32012-09-25 15:37:50 +00002459 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002460 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002461 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002462 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002463
senorblancoc41e7e12015-12-07 12:51:30 -08002464 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002465 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002466
reed@google.com33535f32012-09-25 15:37:50 +00002467 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002468 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002469 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002470
reed@google.com33535f32012-09-25 15:37:50 +00002471 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002472}
2473
reed41af9662015-01-05 07:49:08 -08002474void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002475 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002476 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002477 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002478 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002479}
2480
reed4c21dc52015-06-25 12:32:03 -07002481void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2482 const SkPaint* paint) {
2483 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002484
halcanary96fcdcc2015-08-27 07:41:13 -07002485 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002486 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002487 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2488 return;
2489 }
reed@google.com3d608122011-11-21 15:16:16 +00002490 }
halcanary9d524f22016-03-29 09:03:52 -07002491
reed4c21dc52015-06-25 12:32:03 -07002492 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002493 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002494 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002495 }
halcanary9d524f22016-03-29 09:03:52 -07002496
senorblancoc41e7e12015-12-07 12:51:30 -08002497 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002498
reed4c21dc52015-06-25 12:32:03 -07002499 while (iter.next()) {
2500 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002501 }
halcanary9d524f22016-03-29 09:03:52 -07002502
reed4c21dc52015-06-25 12:32:03 -07002503 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002504}
2505
reed41af9662015-01-05 07:49:08 -08002506void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2507 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002508 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002509 SkDEBUGCODE(bitmap.validate();)
2510
halcanary96fcdcc2015-08-27 07:41:13 -07002511 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002512 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002513 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2514 return;
2515 }
reed4c21dc52015-06-25 12:32:03 -07002516 }
halcanary9d524f22016-03-29 09:03:52 -07002517
reed4c21dc52015-06-25 12:32:03 -07002518 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002519 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002520 paint = lazy.init();
2521 }
halcanary9d524f22016-03-29 09:03:52 -07002522
senorblancoc41e7e12015-12-07 12:51:30 -08002523 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002524
reed4c21dc52015-06-25 12:32:03 -07002525 while (iter.next()) {
2526 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2527 }
halcanary9d524f22016-03-29 09:03:52 -07002528
reed4c21dc52015-06-25 12:32:03 -07002529 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002530}
2531
msarett16882062016-08-16 09:31:08 -07002532void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2533 const SkPaint* paint) {
2534 if (nullptr == paint || paint->canComputeFastBounds()) {
2535 SkRect storage;
2536 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2537 return;
2538 }
2539 }
2540
2541 SkLazyPaint lazy;
2542 if (nullptr == paint) {
2543 paint = lazy.init();
2544 }
2545
2546 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2547
2548 while (iter.next()) {
2549 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2550 }
2551
2552 LOOPER_END
2553}
2554
2555void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2556 const SkRect& dst, const SkPaint* paint) {
2557 if (nullptr == paint || paint->canComputeFastBounds()) {
2558 SkRect storage;
2559 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2560 return;
2561 }
2562 }
2563
2564 SkLazyPaint lazy;
2565 if (nullptr == paint) {
2566 paint = lazy.init();
2567 }
2568
2569 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2570
2571 while (iter.next()) {
2572 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2573 }
2574
2575 LOOPER_END
2576}
2577
reed@google.comf67e4cf2011-03-15 20:56:58 +00002578class SkDeviceFilteredPaint {
2579public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002580 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002581 uint32_t filteredFlags = device->filterTextFlags(paint);
2582 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002583 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002584 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002585 fPaint = newPaint;
2586 } else {
2587 fPaint = &paint;
2588 }
2589 }
2590
reed@google.comf67e4cf2011-03-15 20:56:58 +00002591 const SkPaint& paint() const { return *fPaint; }
2592
2593private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002594 const SkPaint* fPaint;
2595 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002596};
2597
bungeman@google.com52c748b2011-08-22 21:30:43 +00002598void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2599 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002600 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002601 draw.fDevice->drawRect(draw, r, paint);
2602 } else {
2603 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002604 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002605 draw.fDevice->drawRect(draw, r, p);
2606 }
2607}
2608
2609void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2610 const char text[], size_t byteLength,
2611 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002612 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002613
2614 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002615 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002616 draw.fRC->isEmpty() ||
reed374772b2016-10-05 17:33:02 -07002617 (paint.getAlpha() == 0 && paint.isSrcOver())) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002618 return;
2619 }
2620
2621 SkScalar width = 0;
2622 SkPoint start;
2623
2624 start.set(0, 0); // to avoid warning
2625 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2626 SkPaint::kStrikeThruText_Flag)) {
2627 width = paint.measureText(text, byteLength);
2628
2629 SkScalar offsetX = 0;
2630 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2631 offsetX = SkScalarHalf(width);
2632 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2633 offsetX = width;
2634 }
2635 start.set(x - offsetX, y);
2636 }
2637
2638 if (0 == width) {
2639 return;
2640 }
2641
2642 uint32_t flags = paint.getFlags();
2643
2644 if (flags & (SkPaint::kUnderlineText_Flag |
2645 SkPaint::kStrikeThruText_Flag)) {
2646 SkScalar textSize = paint.getTextSize();
2647 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2648 SkRect r;
2649
2650 r.fLeft = start.fX;
2651 r.fRight = start.fX + width;
2652
2653 if (flags & SkPaint::kUnderlineText_Flag) {
2654 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2655 start.fY);
2656 r.fTop = offset;
2657 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002658 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002659 }
2660 if (flags & SkPaint::kStrikeThruText_Flag) {
2661 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2662 start.fY);
2663 r.fTop = offset;
2664 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002665 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002666 }
2667 }
2668}
2669
reed@google.come0d9ce82014-04-23 04:00:17 +00002670void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2671 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002672 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002673
2674 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002675 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002676 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002677 DrawTextDecorations(iter, dfp.paint(),
2678 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002679 }
2680
reed@google.com4e2b3d32011-04-07 14:18:59 +00002681 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002682}
2683
reed@google.come0d9ce82014-04-23 04:00:17 +00002684void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2685 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002686 SkPoint textOffset = SkPoint::Make(0, 0);
2687
halcanary96fcdcc2015-08-27 07:41:13 -07002688 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002689
reed@android.com8a1c16f2008-12-17 15:59:43 +00002690 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002691 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002692 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002693 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002694 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002695
reed@google.com4e2b3d32011-04-07 14:18:59 +00002696 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002697}
2698
reed@google.come0d9ce82014-04-23 04:00:17 +00002699void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2700 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002701
2702 SkPoint textOffset = SkPoint::Make(0, constY);
2703
halcanary96fcdcc2015-08-27 07:41:13 -07002704 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002705
reed@android.com8a1c16f2008-12-17 15:59:43 +00002706 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002707 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002708 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002709 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002710 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002711
reed@google.com4e2b3d32011-04-07 14:18:59 +00002712 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002713}
2714
reed@google.come0d9ce82014-04-23 04:00:17 +00002715void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2716 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002717 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002718
reed@android.com8a1c16f2008-12-17 15:59:43 +00002719 while (iter.next()) {
2720 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002721 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002722 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002723
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002724 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002725}
2726
reed45561a02016-07-07 12:47:17 -07002727void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2728 const SkRect* cullRect, const SkPaint& paint) {
2729 if (cullRect && this->quickReject(*cullRect)) {
2730 return;
2731 }
2732
2733 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2734
2735 while (iter.next()) {
2736 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2737 }
2738
2739 LOOPER_END
2740}
2741
fmalita00d5c2c2014-08-21 08:53:26 -07002742void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2743 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002744
fmalita85d5eb92015-03-04 11:20:12 -08002745 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002746 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002747 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002748 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002749 SkRect tmp;
2750 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2751 return;
2752 }
2753 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002754 }
2755
fmalita024f9962015-03-03 19:08:17 -08002756 // We cannot filter in the looper as we normally do, because the paint is
2757 // incomplete at this point (text-related attributes are embedded within blob run paints).
2758 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002759 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002760
fmalita85d5eb92015-03-04 11:20:12 -08002761 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002762
fmalitaaa1b9122014-08-28 14:32:24 -07002763 while (iter.next()) {
2764 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002765 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002766 }
2767
fmalitaaa1b9122014-08-28 14:32:24 -07002768 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002769
2770 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002771}
2772
reed@google.come0d9ce82014-04-23 04:00:17 +00002773// These will become non-virtual, so they always call the (virtual) onDraw... method
2774void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2775 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002776 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002777 if (byteLength) {
2778 this->onDrawText(text, byteLength, x, y, paint);
2779 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002780}
2781void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2782 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002783 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002784 if (byteLength) {
2785 this->onDrawPosText(text, byteLength, pos, paint);
2786 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002787}
2788void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2789 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002790 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002791 if (byteLength) {
2792 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2793 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002794}
2795void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2796 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002797 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002798 if (byteLength) {
2799 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2800 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002801}
reed45561a02016-07-07 12:47:17 -07002802void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2803 const SkRect* cullRect, const SkPaint& paint) {
2804 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2805 if (byteLength) {
2806 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2807 }
2808}
fmalita00d5c2c2014-08-21 08:53:26 -07002809void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2810 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002811 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002812 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002813 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002814}
reed@google.come0d9ce82014-04-23 04:00:17 +00002815
reed41af9662015-01-05 07:49:08 -08002816void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2817 const SkPoint verts[], const SkPoint texs[],
Mike Reedfaba3712016-11-03 14:45:31 -04002818 const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08002819 const uint16_t indices[], int indexCount,
2820 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002821 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002822 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002823
reed@android.com8a1c16f2008-12-17 15:59:43 +00002824 while (iter.next()) {
2825 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
Mike Reedfaba3712016-11-03 14:45:31 -04002826 colors, bmode, indices, indexCount,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002827 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002828 }
reed@google.com4b226022011-01-11 18:32:13 +00002829
reed@google.com4e2b3d32011-04-07 14:18:59 +00002830 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002831}
2832
dandovb3c9d1c2014-08-12 08:34:29 -07002833void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002834 const SkPoint texCoords[4], SkBlendMode bmode,
2835 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002836 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002837 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002838 return;
2839 }
mtklein6cfa73a2014-08-13 13:33:49 -07002840
Mike Reedfaba3712016-11-03 14:45:31 -04002841 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002842}
2843
2844void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002845 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002846 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002847 // Since a patch is always within the convex hull of the control points, we discard it when its
2848 // bounding rectangle is completely outside the current clip.
2849 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002850 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002851 if (this->quickReject(bounds)) {
2852 return;
2853 }
mtklein6cfa73a2014-08-13 13:33:49 -07002854
halcanary96fcdcc2015-08-27 07:41:13 -07002855 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002856
dandovecfff212014-08-04 10:02:00 -07002857 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002858 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002859 }
mtklein6cfa73a2014-08-13 13:33:49 -07002860
dandovecfff212014-08-04 10:02:00 -07002861 LOOPER_END
2862}
2863
reeda8db7282015-07-07 10:22:31 -07002864void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002865 RETURN_ON_NULL(dr);
2866 if (x || y) {
2867 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2868 this->onDrawDrawable(dr, &matrix);
2869 } else {
2870 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002871 }
2872}
2873
reeda8db7282015-07-07 10:22:31 -07002874void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002875 RETURN_ON_NULL(dr);
2876 if (matrix && matrix->isIdentity()) {
2877 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002878 }
reede3b38ce2016-01-08 09:18:44 -08002879 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002880}
2881
2882void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002883 // drawable bounds are no longer reliable (e.g. android displaylist)
2884 // so don't use them for quick-reject
reeda8db7282015-07-07 10:22:31 -07002885 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002886}
2887
reed71c3c762015-06-24 10:29:17 -07002888void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002889 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002890 const SkRect* cull, const SkPaint* paint) {
2891 if (cull && this->quickReject(*cull)) {
2892 return;
2893 }
2894
2895 SkPaint pnt;
2896 if (paint) {
2897 pnt = *paint;
2898 }
halcanary9d524f22016-03-29 09:03:52 -07002899
halcanary96fcdcc2015-08-27 07:41:13 -07002900 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002901 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002902 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002903 }
2904 LOOPER_END
2905}
2906
reedf70b5312016-03-04 16:36:20 -08002907void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2908 SkASSERT(key);
2909
2910 SkPaint paint;
2911 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2912 while (iter.next()) {
2913 iter.fDevice->drawAnnotation(iter, rect, key, value);
2914 }
2915 LOOPER_END
2916}
2917
reed@android.com8a1c16f2008-12-17 15:59:43 +00002918//////////////////////////////////////////////////////////////////////////////
2919// These methods are NOT virtual, and therefore must call back into virtual
2920// methods, rather than actually drawing themselves.
2921//////////////////////////////////////////////////////////////////////////////
2922
reed374772b2016-10-05 17:33:02 -07002923void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002924 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002925 SkPaint paint;
2926
2927 paint.setARGB(a, r, g, b);
reed374772b2016-10-05 17:33:02 -07002928 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002929 this->drawPaint(paint);
2930}
2931
reed374772b2016-10-05 17:33:02 -07002932void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002933 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002934 SkPaint paint;
2935
2936 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002937 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002938 this->drawPaint(paint);
2939}
2940
2941void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002942 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002943 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002944
reed@android.com8a1c16f2008-12-17 15:59:43 +00002945 pt.set(x, y);
2946 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2947}
2948
2949void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002950 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002951 SkPoint pt;
2952 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002953
reed@android.com8a1c16f2008-12-17 15:59:43 +00002954 pt.set(x, y);
2955 paint.setColor(color);
2956 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2957}
2958
2959void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2960 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002961 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002962 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002963
reed@android.com8a1c16f2008-12-17 15:59:43 +00002964 pts[0].set(x0, y0);
2965 pts[1].set(x1, y1);
2966 this->drawPoints(kLines_PointMode, 2, pts, paint);
2967}
2968
2969void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2970 SkScalar right, SkScalar bottom,
2971 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002972 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002973 SkRect r;
2974
2975 r.set(left, top, right, bottom);
2976 this->drawRect(r, paint);
2977}
2978
2979void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2980 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002981 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002982 if (radius < 0) {
2983 radius = 0;
2984 }
2985
2986 SkRect r;
2987 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002988 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002989}
2990
2991void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2992 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002993 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002994 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002995 SkRRect rrect;
2996 rrect.setRectXY(r, rx, ry);
2997 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002998 } else {
2999 this->drawRect(r, paint);
3000 }
3001}
3002
reed@android.com8a1c16f2008-12-17 15:59:43 +00003003void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3004 SkScalar sweepAngle, bool useCenter,
3005 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003006 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
bsalomon21af9ca2016-08-25 12:29:23 -07003007 if (oval.isEmpty() || !sweepAngle) {
3008 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003009 }
bsalomon21af9ca2016-08-25 12:29:23 -07003010 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003011}
3012
3013void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3014 const SkPath& path, SkScalar hOffset,
3015 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003016 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003017 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003018
reed@android.com8a1c16f2008-12-17 15:59:43 +00003019 matrix.setTranslate(hOffset, vOffset);
3020 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3021}
3022
reed@android.comf76bacf2009-05-13 14:00:33 +00003023///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003024
3025/**
3026 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3027 * against the playback cost of recursing into the subpicture to get at its actual ops.
3028 *
3029 * For now we pick a conservatively small value, though measurement (and other heuristics like
3030 * the type of ops contained) may justify changing this value.
3031 */
3032#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003033
reedd5fa1a42014-08-09 11:08:05 -07003034void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003035 RETURN_ON_NULL(picture);
3036
reed1c2c4412015-04-30 13:09:24 -07003037 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003038 if (matrix && matrix->isIdentity()) {
3039 matrix = nullptr;
3040 }
3041 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3042 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3043 picture->playback(this);
3044 } else {
3045 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003046 }
3047}
robertphillips9b14f262014-06-04 05:40:44 -07003048
reedd5fa1a42014-08-09 11:08:05 -07003049void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3050 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003051 if (!paint || paint->canComputeFastBounds()) {
3052 SkRect bounds = picture->cullRect();
3053 if (paint) {
3054 paint->computeFastBounds(bounds, &bounds);
3055 }
3056 if (matrix) {
3057 matrix->mapRect(&bounds);
3058 }
3059 if (this->quickReject(bounds)) {
3060 return;
3061 }
3062 }
3063
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003064 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003065 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003066}
3067
vjiaoblack95302da2016-07-21 10:25:54 -07003068#ifdef SK_EXPERIMENTAL_SHADOWING
3069void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3070 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003071 const SkPaint* paint,
3072 const SkShadowParams& params) {
vjiaoblack95302da2016-07-21 10:25:54 -07003073 RETURN_ON_NULL(picture);
3074
3075 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3076
vjiaoblacke6f5d562016-08-25 06:30:23 -07003077 this->onDrawShadowedPicture(picture, matrix, paint, params);
vjiaoblack95302da2016-07-21 10:25:54 -07003078}
3079
3080void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3081 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003082 const SkPaint* paint,
3083 const SkShadowParams& params) {
vjiaoblack904527d2016-08-09 09:32:09 -07003084 if (!paint || paint->canComputeFastBounds()) {
3085 SkRect bounds = picture->cullRect();
3086 if (paint) {
3087 paint->computeFastBounds(bounds, &bounds);
3088 }
3089 if (matrix) {
3090 matrix->mapRect(&bounds);
3091 }
3092 if (this->quickReject(bounds)) {
3093 return;
3094 }
3095 }
3096
3097 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3098
vjiaoblacke6f5d562016-08-25 06:30:23 -07003099 sk_sp<SkImage> povDepthMap;
3100 sk_sp<SkImage> diffuseMap;
3101
vjiaoblack904527d2016-08-09 09:32:09 -07003102 // povDepthMap
3103 {
3104 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003105 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3106 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003107 sk_sp<SkLights> povLight = builder.finish();
3108
3109 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3110 picture->cullRect().height(),
3111 kBGRA_8888_SkColorType,
3112 kOpaque_SkAlphaType);
3113
3114 // Create a new surface (that matches the backend of canvas)
3115 // to create the povDepthMap
3116 sk_sp<SkSurface> surf(this->makeSurface(info));
3117
3118 // Wrap another SPFCanvas around the surface
3119 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3120 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3121
3122 // set the depth map canvas to have the light as the user's POV
3123 depthMapCanvas->setLights(std::move(povLight));
3124
3125 depthMapCanvas->drawPicture(picture);
vjiaoblack904527d2016-08-09 09:32:09 -07003126 povDepthMap = surf->makeImageSnapshot();
3127 }
3128
3129 // diffuseMap
3130 {
3131 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3132 picture->cullRect().height(),
3133 kBGRA_8888_SkColorType,
3134 kOpaque_SkAlphaType);
3135
3136 sk_sp<SkSurface> surf(this->makeSurface(info));
3137 surf->getCanvas()->drawPicture(picture);
3138
3139 diffuseMap = surf->makeImageSnapshot();
3140 }
vjiaoblack904527d2016-08-09 09:32:09 -07003141
3142 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3143 SkShader::kClamp_TileMode);
vjiaoblack904527d2016-08-09 09:32:09 -07003144 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3145 SkShader::kClamp_TileMode);
vjiaoblackb2796fd2016-09-09 09:22:39 -07003146
3147 // TODO: pass the depth to the shader in vertices, or uniforms
3148 // so we don't have to render depth and color separately
3149 for (int i = 0; i < fLights->numLights(); ++i) {
3150 // skip over ambient lights; they don't cast shadows
3151 // lights that have shadow maps do not need updating (because lights are immutable)
3152 sk_sp<SkImage> depthMap;
3153 SkISize shMapSize;
3154
3155 if (fLights->light(i).getShadowMap() != nullptr) {
3156 continue;
3157 }
3158
3159 if (fLights->light(i).isRadial()) {
3160 shMapSize.fHeight = 1;
3161 shMapSize.fWidth = (int) picture->cullRect().width();
3162
3163 SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1,
3164 kBGRA_8888_SkColorType,
3165 kOpaque_SkAlphaType);
3166
3167 // Create new surface (that matches the backend of canvas)
3168 // for each shadow map
3169 sk_sp<SkSurface> surf(this->makeSurface(info));
3170
3171 // Wrap another SPFCanvas around the surface
3172 SkCanvas* depthMapCanvas = surf->getCanvas();
3173
3174 SkLights::Builder builder;
3175 builder.add(fLights->light(i));
3176 sk_sp<SkLights> curLight = builder.finish();
3177
3178 sk_sp<SkShader> shadowMapShader;
3179 shadowMapShader = SkRadialShadowMapShader::Make(
3180 povDepthShader, curLight,
3181 (int) picture->cullRect().width(),
3182 (int) picture->cullRect().height());
3183
3184 SkPaint shadowMapPaint;
3185 shadowMapPaint.setShader(std::move(shadowMapShader));
3186
3187 depthMapCanvas->setLights(curLight);
3188
3189 depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(),
3190 diffuseMap->height()),
3191 shadowMapPaint);
3192
3193 depthMap = surf->makeImageSnapshot();
3194
3195 } else {
3196 // TODO: compute the correct size of the depth map from the light properties
3197 // TODO: maybe add a kDepth_8_SkColorType
3198 // TODO: find actual max depth of picture
3199 shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3200 fLights->light(i), 255,
3201 (int) picture->cullRect().width(),
3202 (int) picture->cullRect().height());
3203
3204 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3205 kBGRA_8888_SkColorType,
3206 kOpaque_SkAlphaType);
3207
3208 // Create a new surface (that matches the backend of canvas)
3209 // for each shadow map
3210 sk_sp<SkSurface> surf(this->makeSurface(info));
3211
3212 // Wrap another SPFCanvas around the surface
3213 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3214 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3215 depthMapCanvas->setShadowParams(params);
3216
3217 // set the depth map canvas to have the light we're drawing.
3218 SkLights::Builder builder;
3219 builder.add(fLights->light(i));
3220 sk_sp<SkLights> curLight = builder.finish();
3221 depthMapCanvas->setLights(std::move(curLight));
3222
3223 depthMapCanvas->drawPicture(picture);
3224 depthMap = surf->makeImageSnapshot();
3225 }
3226
3227 if (params.fType == SkShadowParams::kNoBlur_ShadowType) {
3228 fLights->light(i).setShadowMap(std::move(depthMap));
3229 } else if (params.fType == SkShadowParams::kVariance_ShadowType) {
3230 // we blur the variance map
3231 SkPaint blurPaint;
3232 blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius,
3233 params.fShadowRadius, nullptr));
3234
3235 SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3236 kBGRA_8888_SkColorType,
3237 kOpaque_SkAlphaType);
3238
3239 sk_sp<SkSurface> blurSurf(this->makeSurface(blurInfo));
3240
3241 blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
3242
3243 fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
3244 }
3245 }
3246
3247 SkPaint shadowPaint;
vjiaoblack904527d2016-08-09 09:32:09 -07003248 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3249 std::move(diffuseShader),
vjiaoblackb2796fd2016-09-09 09:22:39 -07003250 fLights,
vjiaoblack904527d2016-08-09 09:32:09 -07003251 diffuseMap->width(),
vjiaoblacke6f5d562016-08-25 06:30:23 -07003252 diffuseMap->height(),
3253 params);
vjiaoblack904527d2016-08-09 09:32:09 -07003254
3255 shadowPaint.setShader(shadowShader);
3256
3257 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003258}
3259#endif
3260
reed@android.com8a1c16f2008-12-17 15:59:43 +00003261///////////////////////////////////////////////////////////////////////////////
3262///////////////////////////////////////////////////////////////////////////////
3263
reed3aafe112016-08-18 12:45:34 -07003264SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003265 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003266
3267 SkASSERT(canvas);
3268
reed3aafe112016-08-18 12:45:34 -07003269 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003270 fDone = !fImpl->next();
3271}
3272
3273SkCanvas::LayerIter::~LayerIter() {
3274 fImpl->~SkDrawIter();
3275}
3276
3277void SkCanvas::LayerIter::next() {
3278 fDone = !fImpl->next();
3279}
3280
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003281SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003282 return fImpl->getDevice();
3283}
3284
3285const SkMatrix& SkCanvas::LayerIter::matrix() const {
3286 return fImpl->getMatrix();
3287}
3288
3289const SkPaint& SkCanvas::LayerIter::paint() const {
3290 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003291 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003292 paint = &fDefaultPaint;
3293 }
3294 return *paint;
3295}
3296
reed1e7f5e72016-04-27 07:49:17 -07003297const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003298int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3299int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003300
3301///////////////////////////////////////////////////////////////////////////////
3302
fmalitac3b589a2014-06-05 12:40:07 -07003303SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003304
3305///////////////////////////////////////////////////////////////////////////////
3306
3307static bool supported_for_raster_canvas(const SkImageInfo& info) {
3308 switch (info.alphaType()) {
3309 case kPremul_SkAlphaType:
3310 case kOpaque_SkAlphaType:
3311 break;
3312 default:
3313 return false;
3314 }
3315
3316 switch (info.colorType()) {
3317 case kAlpha_8_SkColorType:
3318 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003319 case kN32_SkColorType:
junov9e3dbdf2016-10-13 13:14:27 -07003320 case kRGBA_F16_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003321 break;
3322 default:
3323 return false;
3324 }
3325
3326 return true;
3327}
3328
Mike Reed5df49342016-11-12 08:06:55 -06003329std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels,
3330 size_t rowBytes) {
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003331 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003332 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003333 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003334
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003335 SkBitmap bitmap;
3336 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003337 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003338 }
Mike Reed5df49342016-11-12 08:06:55 -06003339 return skstd::make_unique<SkCanvas>(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003340}
reedd5fa1a42014-08-09 11:08:05 -07003341
3342///////////////////////////////////////////////////////////////////////////////
3343
3344SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003345 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003346 : fCanvas(canvas)
3347 , fSaveCount(canvas->getSaveCount())
3348{
bsalomon49f085d2014-09-05 13:34:00 -07003349 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003350 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003351 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003352 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003353 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003354 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003355 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003356 canvas->save();
3357 }
mtklein6cfa73a2014-08-13 13:33:49 -07003358
bsalomon49f085d2014-09-05 13:34:00 -07003359 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003360 canvas->concat(*matrix);
3361 }
3362}
3363
3364SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3365 fCanvas->restoreToCount(fSaveCount);
3366}
reede8f30622016-03-23 18:59:25 -07003367
reed73603f32016-09-20 08:42:38 -07003368/////////////////////////////////
3369
3370const SkCanvas::ClipOp SkCanvas::kDifference_Op;
3371const SkCanvas::ClipOp SkCanvas::kIntersect_Op;
3372const SkCanvas::ClipOp SkCanvas::kUnion_Op;
3373const SkCanvas::ClipOp SkCanvas::kXOR_Op;
3374const SkCanvas::ClipOp SkCanvas::kReverseDifference_Op;
3375const SkCanvas::ClipOp SkCanvas::kReplace_Op;
3376
3377static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3378static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3379static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3380static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3381static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3382static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");