blob: 80ff34d417e3a08f08fcda4e41e8c1d52e2617d5 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
joshualitt5f5a8d72015-02-25 14:09:45 -080017#include "SkErrorInternals.h"
piotaixrb5fae932014-09-24 13:03:30 -070018#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080019#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070020#include "SkImageFilter.h"
21#include "SkImageFilterCache.h"
msarettc573a402016-08-02 08:05:56 -070022#include "SkLatticeIter.h"
reed262a71b2015-12-05 13:07:27 -080023#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000024#include "SkMetaData.h"
msarettfbfa2582016-08-12 08:29:08 -070025#include "SkNx.h"
reedc83a2972015-07-16 07:40:45 -070026#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070027#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000028#include "SkPicture.h"
reed@google.com00177082011-10-12 14:34:30 +000029#include "SkRasterClip.h"
reed96472de2014-12-10 09:53:42 -080030#include "SkReadPixelsRec.h"
reed@google.com4ed0fb72012-12-12 20:48:18 +000031#include "SkRRect.h"
vjiaoblack904527d2016-08-09 09:32:09 -070032#include "SkShadowPaintFilterCanvas.h"
33#include "SkShadowShader.h"
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +000034#include "SkSmallAllocator.h"
robertphillips4418dba2016-03-07 12:45:14 -080035#include "SkSpecialImage.h"
reed@google.com97af1a62012-08-28 12:19:02 +000036#include "SkSurface_Base.h"
fmalita7ba7aa72014-08-29 09:46:36 -070037#include "SkTextBlob.h"
bungeman@google.com52c748b2011-08-22 21:30:43 +000038#include "SkTextFormatParams.h"
reed@google.coma076e9b2011-04-06 20:17:29 +000039#include "SkTLazy.h"
danakj8f757f52014-11-04 11:48:43 -080040#include "SkTraceEvent.h"
bungemand3ebb482015-08-05 13:57:49 -070041
42#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"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000048#endif
49
reede3b38ce2016-01-08 09:18:44 -080050#define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0)
51
reed2d1afab2016-06-29 14:33:11 -070052//#define SK_SUPPORT_PRECHECK_CLIPRECT
53
reedc83a2972015-07-16 07:40:45 -070054/*
55 * Return true if the drawing this rect would hit every pixels in the canvas.
56 *
57 * Returns false if
58 * - rect does not contain the canvas' bounds
59 * - paint is not fill
60 * - paint would blur or otherwise change the coverage of the rect
61 */
62bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
63 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070064 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
65 (int)kNone_ShaderOverrideOpacity,
66 "need_matching_enums0");
67 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
68 (int)kOpaque_ShaderOverrideOpacity,
69 "need_matching_enums1");
70 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
71 (int)kNotOpaque_ShaderOverrideOpacity,
72 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070073
74 const SkISize size = this->getBaseLayerSize();
75 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
76 if (!this->getClipStack()->quickContains(bounds)) {
77 return false;
78 }
79
80 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070081 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070082 return false; // conservative
83 }
halcanaryc5769b22016-08-10 07:13:21 -070084
85 SkRect devRect;
86 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
87 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070088 return false;
89 }
90 }
91
92 if (paint) {
93 SkPaint::Style paintStyle = paint->getStyle();
94 if (!(paintStyle == SkPaint::kFill_Style ||
95 paintStyle == SkPaint::kStrokeAndFill_Style)) {
96 return false;
97 }
98 if (paint->getMaskFilter() || paint->getLooper()
99 || paint->getPathEffect() || paint->getImageFilter()) {
100 return false; // conservative
101 }
102 }
103 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
104}
105
106///////////////////////////////////////////////////////////////////////////////////////////////////
107
reedd990e2f2014-12-22 11:58:30 -0800108static bool gIgnoreSaveLayerBounds;
109void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
110 gIgnoreSaveLayerBounds = ignore;
111}
112bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
113 return gIgnoreSaveLayerBounds;
114}
115
reed0acf1b42014-12-22 16:12:38 -0800116static bool gTreatSpriteAsBitmap;
117void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
118 gTreatSpriteAsBitmap = spriteAsBitmap;
119}
120bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
121 return gTreatSpriteAsBitmap;
122}
123
reed@google.comda17f752012-08-16 18:27:05 +0000124// experimental for faster tiled drawing...
125//#define SK_ENABLE_CLIP_QUICKREJECT
reed@android.com8a1c16f2008-12-17 15:59:43 +0000126//#define SK_TRACE_SAVERESTORE
127
128#ifdef SK_TRACE_SAVERESTORE
129 static int gLayerCounter;
130 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
131 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
132
133 static int gRecCounter;
134 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
135 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
136
137 static int gCanvasCounter;
138 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
139 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
140#else
141 #define inc_layer()
142 #define dec_layer()
143 #define inc_rec()
144 #define dec_rec()
145 #define inc_canvas()
146 #define dec_canvas()
147#endif
148
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000149typedef SkTLazy<SkPaint> SkLazyPaint;
150
reedc83a2972015-07-16 07:40:45 -0700151void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000152 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700153 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
154 ? SkSurface::kDiscard_ContentChangeMode
155 : SkSurface::kRetain_ContentChangeMode);
156 }
157}
158
159void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
160 ShaderOverrideOpacity overrideOpacity) {
161 if (fSurfaceBase) {
162 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
163 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
164 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
165 // and therefore we don't care which mode we're in.
166 //
167 if (fSurfaceBase->outstandingImageSnapshot()) {
168 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
169 mode = SkSurface::kDiscard_ContentChangeMode;
170 }
171 }
172 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000173 }
174}
175
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177
reed4a8126e2014-09-22 07:29:03 -0700178static uint32_t filter_paint_flags(const SkSurfaceProps& props, uint32_t flags) {
179 const uint32_t propFlags = props.flags();
180 if (propFlags & SkSurfaceProps::kDisallowDither_Flag) {
181 flags &= ~SkPaint::kDither_Flag;
182 }
183 if (propFlags & SkSurfaceProps::kDisallowAntiAlias_Flag) {
184 flags &= ~SkPaint::kAntiAlias_Flag;
185 }
186 return flags;
187}
188
189///////////////////////////////////////////////////////////////////////////////
190
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000191/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192 The clip/matrix/proc are fields that reflect the top of the save/restore
193 stack. Whenever the canvas changes, it marks a dirty flag, and then before
194 these are used (assuming we're not on a layer) we rebuild these cache
195 values: they reflect the top of the save stack, but translated and clipped
196 by the device's XY offset and bitmap-bounds.
197*/
198struct DeviceCM {
199 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000200 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000201 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000202 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700203 const SkMatrix* fMatrix;
204 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700205 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000206
reed96e657d2015-03-10 17:30:07 -0700207 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed7503d602016-07-15 14:23:29 -0700208 bool conservativeRasterClip, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700209 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700210 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700211 , fStashedMatrix(stashed)
reedd9544982014-09-09 18:46:22 -0700212 {
reed2c9e2002016-07-25 08:05:22 -0700213 SkSafeRef(device);
reed@google.com4b226022011-01-11 18:32:13 +0000214 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700215 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000216 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000217
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000218 ~DeviceCM() {
reed2c9e2002016-07-25 08:05:22 -0700219 SkSafeUnref(fDevice);
halcanary385fe4d2015-08-26 13:07:48 -0700220 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000221 }
reed@google.com4b226022011-01-11 18:32:13 +0000222
mtkleinfeaadee2015-04-08 11:25:48 -0700223 void reset(const SkIRect& bounds) {
224 SkASSERT(!fPaint);
225 SkASSERT(!fNext);
226 SkASSERT(fDevice);
227 fClip.setRect(bounds);
228 }
229
reed@google.com045e62d2011-10-24 12:19:46 +0000230 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
231 const SkClipStack& clipStack, SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000232 int x = fDevice->getOrigin().x();
233 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 int width = fDevice->width();
235 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000236
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 if ((x | y) == 0) {
238 fMatrix = &totalMatrix;
239 fClip = totalClip;
240 } else {
241 fMatrixStorage = totalMatrix;
242 fMatrixStorage.postTranslate(SkIntToScalar(-x),
243 SkIntToScalar(-y));
244 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000245
reed@android.com8a1c16f2008-12-17 15:59:43 +0000246 totalClip.translate(-x, -y, &fClip);
247 }
248
reed@google.com045e62d2011-10-24 12:19:46 +0000249 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250
251 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000252
reed@android.com8a1c16f2008-12-17 15:59:43 +0000253 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000254 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 SkRegion::kDifference_Op);
256 }
reed@google.com4b226022011-01-11 18:32:13 +0000257
robertphillips@google.com3fffb2e2012-10-09 22:30:18 +0000258 fDevice->setMatrixClip(*fMatrix, fClip.forceGetBW(), clipStack);
259
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260#ifdef SK_DEBUG
261 if (!fClip.isEmpty()) {
262 SkIRect deviceR;
263 deviceR.set(0, 0, width, height);
264 SkASSERT(deviceR.contains(fClip.getBounds()));
265 }
266#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268};
269
270/* This is the record we keep for each save/restore level in the stack.
271 Since a level optionally copies the matrix and/or stack, we have pointers
272 for these fields. If the value is copied for this level, the copy is
273 stored in the ...Storage field, and the pointer points to that. If the
274 value is not copied for this level, we ignore ...Storage, and just point
275 at the corresponding value in the previous level in the stack.
276*/
277class SkCanvas::MCRec {
278public:
reed1f836ee2014-07-07 07:49:34 -0700279 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700280 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281 /* If there are any layers in the stack, this points to the top-most
282 one that is at or below this level in the stack (so we know what
283 bitmap/device to draw into from this level. This value is NOT
284 reference counted, since the real owner is either our fLayer field,
285 or a previous one in a lower level.)
286 */
reed2ff1fce2014-12-11 07:07:37 -0800287 DeviceCM* fTopLayer;
288 SkRasterClip fRasterClip;
289 SkMatrix fMatrix;
290 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291
vjiaoblacke5de1302016-07-13 14:05:28 -0700292 // This is the current cumulative depth (aggregate of all done translateZ calls)
293 SkScalar fCurDrawDepth;
294
reedd9544982014-09-09 18:46:22 -0700295 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700296 fFilter = nullptr;
297 fLayer = nullptr;
298 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800299 fMatrix.reset();
300 fDeferredSaveCount = 0;
vjiaoblacke5de1302016-07-13 14:05:28 -0700301 fCurDrawDepth = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700302
reedd9544982014-09-09 18:46:22 -0700303 // don't bother initializing fNext
304 inc_rec();
305 }
vjiaoblacke5de1302016-07-13 14:05:28 -0700306 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
307 fCurDrawDepth(prev.fCurDrawDepth) {
reedd9544982014-09-09 18:46:22 -0700308 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700309 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700310 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800311 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700312
reed@android.com8a1c16f2008-12-17 15:59:43 +0000313 // don't bother initializing fNext
314 inc_rec();
315 }
316 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000317 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700318 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319 dec_rec();
320 }
mtkleinfeaadee2015-04-08 11:25:48 -0700321
322 void reset(const SkIRect& bounds) {
323 SkASSERT(fLayer);
324 SkASSERT(fDeferredSaveCount == 0);
325
326 fMatrix.reset();
327 fRasterClip.setRect(bounds);
328 fLayer->reset(bounds);
329 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000330};
331
332class SkDrawIter : public SkDraw {
333public:
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000334 SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
junov@google.com4370aed2012-01-18 16:21:08 +0000335 canvas = canvas->canvasForDrawIter();
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000336 fCanvas = canvas;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000337 canvas->updateDeviceCMCache();
338
reed687fa1c2015-04-07 08:00:56 -0700339 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 fCurrLayer = canvas->fMCRec->fTopLayer;
tomhudson@google.com8a0b0292011-09-13 14:41:06 +0000341 fSkipEmptyClips = skipEmptyClips;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342 }
reed@google.com4b226022011-01-11 18:32:13 +0000343
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 bool next() {
345 // skip over recs with empty clips
346 if (fSkipEmptyClips) {
347 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
348 fCurrLayer = fCurrLayer->fNext;
349 }
350 }
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:
380 SkCanvas* fCanvas;
381 const DeviceCM* fCurrLayer;
382 const SkPaint* fPaint; // May be null.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383 SkBool8 fSkipEmptyClips;
384
385 typedef SkDraw INHERITED;
386};
387
388/////////////////////////////////////////////////////////////////////////////
389
reeddbc3cef2015-04-29 12:18:57 -0700390static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
391 return lazy->isValid() ? lazy->get() : lazy->set(orig);
392}
393
394/**
395 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700396 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700397 */
reedd053ce92016-03-22 10:17:23 -0700398static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700399 SkImageFilter* imgf = paint.getImageFilter();
400 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700401 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700402 }
403
reedd053ce92016-03-22 10:17:23 -0700404 SkColorFilter* imgCFPtr;
405 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700406 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700407 }
reedd053ce92016-03-22 10:17:23 -0700408 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700409
410 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700411 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700412 // there is no existing paint colorfilter, so we can just return the imagefilter's
413 return imgCF;
414 }
415
416 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
417 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700418 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700419}
420
senorblanco87e066e2015-10-28 11:23:36 -0700421/**
422 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
423 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
424 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
425 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
426 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
427 * conservative "effective" bounds based on the settings in the paint... with one exception. This
428 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
429 * deliberately ignored.
430 */
431static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
432 const SkRect& rawBounds,
433 SkRect* storage) {
434 SkPaint tmpUnfiltered(paint);
435 tmpUnfiltered.setImageFilter(nullptr);
436 if (tmpUnfiltered.canComputeFastBounds()) {
437 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
438 } else {
439 return rawBounds;
440 }
441}
442
reed@android.com8a1c16f2008-12-17 15:59:43 +0000443class AutoDrawLooper {
444public:
senorblanco87e066e2015-10-28 11:23:36 -0700445 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
446 // paint. It's used to determine the size of the offscreen layer for filters.
447 // If null, the clip will be used instead.
reed4a8126e2014-09-22 07:29:03 -0700448 AutoDrawLooper(SkCanvas* canvas, const SkSurfaceProps& props, const SkPaint& paint,
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000449 bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700450 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000451 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800452#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000453 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800454#else
455 fFilter = nullptr;
456#endif
reed4a8126e2014-09-22 07:29:03 -0700457 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000458 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700459 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000460 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461
reedd053ce92016-03-22 10:17:23 -0700462 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700463 if (simplifiedCF) {
464 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700465 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700466 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700467 fPaint = paint;
468 }
469
470 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700471 /**
472 * We implement ImageFilters for a given draw by creating a layer, then applying the
473 * imagefilter to the pixels of that layer (its backing surface/image), and then
474 * we call restore() to xfer that layer to the main canvas.
475 *
476 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
477 * 2. Generate the src pixels:
478 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
479 * return (fPaint). We then draw the primitive (using srcover) into a cleared
480 * buffer/surface.
481 * 3. Restore the layer created in #1
482 * The imagefilter is passed the buffer/surface from the layer (now filled with the
483 * src pixels of the primitive). It returns a new "filtered" buffer, which we
484 * draw onto the previous layer using the xfermode from the original paint.
485 */
reed@google.com8926b162012-03-23 15:36:36 +0000486 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700487 tmp.setImageFilter(fPaint->getImageFilter());
reedcfb6bdf2016-03-29 11:32:50 -0700488 tmp.setXfermode(sk_ref_sp(fPaint->getXfermode()));
senorblanco87e066e2015-10-28 11:23:36 -0700489 SkRect storage;
490 if (rawBounds) {
491 // Make rawBounds include all paint outsets except for those due to image filters.
492 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
493 }
reedbfd5f172016-01-07 11:28:08 -0800494 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700495 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700496 fTempLayerForImageFilter = true;
497 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000498 }
499
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000500 if (SkDrawLooper* looper = paint.getLooper()) {
501 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
502 looper->contextSize());
503 fLooperContext = looper->createContext(canvas, buffer);
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 }
piotaixrb5fae932014-09-24 13:03:30 -0700510
reed4a8126e2014-09-22 07:29:03 -0700511 uint32_t oldFlags = paint.getFlags();
512 fNewPaintFlags = filter_paint_flags(props, oldFlags);
513 if (fIsSimple && (fNewPaintFlags != oldFlags)) {
reeddbc3cef2015-04-29 12:18:57 -0700514 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700515 paint->setFlags(fNewPaintFlags);
516 fPaint = paint;
517 // if we're not simple, doNext() will take care of calling setFlags()
518 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000519 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000520
reed@android.com8a1c16f2008-12-17 15:59:43 +0000521 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700522 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000523 fCanvas->internalRestore();
524 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000525 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000526 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000527
reed@google.com4e2b3d32011-04-07 14:18:59 +0000528 const SkPaint& paint() const {
529 SkASSERT(fPaint);
530 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000531 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000532
reed@google.com129ec222012-05-15 13:24:09 +0000533 bool next(SkDrawFilter::Type drawType) {
534 if (fDone) {
535 return false;
536 } else if (fIsSimple) {
537 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000538 return !fPaint->nothingToDraw();
539 } else {
540 return this->doNext(drawType);
541 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000542 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000543
reed@android.com8a1c16f2008-12-17 15:59:43 +0000544private:
reeddbc3cef2015-04-29 12:18:57 -0700545 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
546 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000547 SkCanvas* fCanvas;
548 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000549 SkDrawFilter* fFilter;
550 const SkPaint* fPaint;
551 int fSaveCount;
reed4a8126e2014-09-22 07:29:03 -0700552 uint32_t fNewPaintFlags;
reed5c476fb2015-04-20 08:04:21 -0700553 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000554 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000555 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000556 SkDrawLooper::Context* fLooperContext;
557 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000558
559 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000560};
561
reed@google.com129ec222012-05-15 13:24:09 +0000562bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700563 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000564 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700565 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000566
reeddbc3cef2015-04-29 12:18:57 -0700567 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
568 *fLazyPaintInit.get() : fOrigPaint);
reed4a8126e2014-09-22 07:29:03 -0700569 paint->setFlags(fNewPaintFlags);
reed@google.com129ec222012-05-15 13:24:09 +0000570
reed5c476fb2015-04-20 08:04:21 -0700571 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700572 paint->setImageFilter(nullptr);
573 paint->setXfermode(nullptr);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000574 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000575
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000576 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000577 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000578 return false;
579 }
580 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000581 if (!fFilter->filter(paint, drawType)) {
582 fDone = true;
583 return false;
584 }
halcanary96fcdcc2015-08-27 07:41:13 -0700585 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000586 // no looper means we only draw once
587 fDone = true;
588 }
589 }
590 fPaint = paint;
591
592 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000593 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000594 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000595 }
596
597 // call this after any possible paint modifiers
598 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700599 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000600 return false;
601 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000602 return true;
603}
604
reed@android.com8a1c16f2008-12-17 15:59:43 +0000605////////// macros to place around the internal draw calls //////////////////
606
reed262a71b2015-12-05 13:07:27 -0800607#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
608 this->predrawNotify(); \
609 AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
610 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
611 SkDrawIter iter(this);
612
613
reed@google.com8926b162012-03-23 15:36:36 +0000614#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000615 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700616 AutoDrawLooper looper(this, fProps, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000617 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000618 SkDrawIter iter(this);
619
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000620#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000621 this->predrawNotify(); \
reed4a8126e2014-09-22 07:29:03 -0700622 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000623 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000624 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000625
reedc83a2972015-07-16 07:40:45 -0700626#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
627 this->predrawNotify(bounds, &paint, auxOpaque); \
628 AutoDrawLooper looper(this, fProps, paint, false, bounds); \
629 while (looper.next(type)) { \
630 SkDrawIter iter(this);
631
reed@google.com4e2b3d32011-04-07 14:18:59 +0000632#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000633
634////////////////////////////////////////////////////////////////////////////
635
msarettfbfa2582016-08-12 08:29:08 -0700636static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
637 if (bounds.isEmpty()) {
638 return SkRect::MakeEmpty();
639 }
640
641 // Expand bounds out by 1 in case we are anti-aliasing. We store the
642 // bounds as floats to enable a faster quick reject implementation.
643 SkRect dst;
644 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
645 return dst;
646}
647
mtkleinfeaadee2015-04-08 11:25:48 -0700648void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
649 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700650 fClipStack->reset();
651 fMCRec->reset(bounds);
652
653 // We're peering through a lot of structs here. Only at this scope do we
654 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
655 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
msarettfbfa2582016-08-12 08:29:08 -0700656 fDeviceClipBounds = qr_clip_bounds(bounds);
657 fConservativeIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700658}
659
reedd9544982014-09-09 18:46:22 -0700660SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800661 if (device && device->forceConservativeRasterClip()) {
662 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
663 }
664 // Since init() is only called once by our constructors, it is safe to perform this
665 // const-cast.
666 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
667
caryclark@google.com8f0a7b82012-11-07 14:54:49 +0000668 fAllowSoftClip = true;
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000669 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700670 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800671 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700672 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700673#ifdef SK_EXPERIMENTAL_SHADOWING
674 fLights = nullptr;
675#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000676
halcanary385fe4d2015-08-26 13:07:48 -0700677 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700678
reed@android.com8a1c16f2008-12-17 15:59:43 +0000679 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700680 new (fMCRec) MCRec(fConservativeRasterClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681
reeda499f902015-05-01 09:34:31 -0700682 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
683 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700684 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700685 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700686
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000688
halcanary96fcdcc2015-08-27 07:41:13 -0700689 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000690
reedf92c8662014-08-18 08:02:43 -0700691 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700692 // The root device and the canvas should always have the same pixel geometry
693 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700694 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800695 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700696 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
697 fConservativeIsScaleTranslate = true;
reedf92c8662014-08-18 08:02:43 -0700698 }
msarettfbfa2582016-08-12 08:29:08 -0700699
reedf92c8662014-08-18 08:02:43 -0700700 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000701}
702
reed@google.comcde92112011-07-06 20:00:52 +0000703SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000704 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700705 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800706 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000707{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000708 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000709
halcanary96fcdcc2015-08-27 07:41:13 -0700710 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000711}
712
reedd9544982014-09-09 18:46:22 -0700713static SkBitmap make_nopixels(int width, int height) {
714 SkBitmap bitmap;
715 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
716 return bitmap;
717}
718
719class SkNoPixelsBitmapDevice : public SkBitmapDevice {
720public:
robertphillipsfcf78292015-06-19 11:49:52 -0700721 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
722 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800723 {
724 this->setOrigin(bounds.x(), bounds.y());
725 }
reedd9544982014-09-09 18:46:22 -0700726
727private:
piotaixrb5fae932014-09-24 13:03:30 -0700728
reedd9544982014-09-09 18:46:22 -0700729 typedef SkBitmapDevice INHERITED;
730};
731
reed96a857e2015-01-25 10:33:58 -0800732SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000733 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800734 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800735 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000736{
737 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700738
halcanary385fe4d2015-08-26 13:07:48 -0700739 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
740 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700741}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000742
reed78e27682014-11-19 08:04:34 -0800743SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700744 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700745 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800746 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700747{
748 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700749
halcanary385fe4d2015-08-26 13:07:48 -0700750 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700751}
752
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000753SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000754 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700755 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800756 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000757{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000758 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700759
reedd9544982014-09-09 18:46:22 -0700760 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000761}
762
robertphillipsfcf78292015-06-19 11:49:52 -0700763SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
764 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700765 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800766 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700767{
768 inc_canvas();
769
770 this->init(device, flags);
771}
772
reed4a8126e2014-09-22 07:29:03 -0700773SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700774 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700775 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800776 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700777{
778 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700779
halcanary385fe4d2015-08-26 13:07:48 -0700780 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700781 this->init(device, kDefault_InitFlags);
782}
reed29c857d2014-09-21 10:25:07 -0700783
reed4a8126e2014-09-22 07:29:03 -0700784SkCanvas::SkCanvas(const SkBitmap& bitmap)
785 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
786 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800787 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700788{
789 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700790
halcanary385fe4d2015-08-26 13:07:48 -0700791 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700792 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793}
794
795SkCanvas::~SkCanvas() {
796 // free up the contents of our deque
797 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000798
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 this->internalRestore(); // restore the last, since we're going away
800
halcanary385fe4d2015-08-26 13:07:48 -0700801 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000802
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803 dec_canvas();
804}
805
fmalita53d9f1c2016-01-25 06:23:54 -0800806#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000807SkDrawFilter* SkCanvas::getDrawFilter() const {
808 return fMCRec->fFilter;
809}
810
811SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700812 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
814 return filter;
815}
fmalita77650002016-01-21 18:47:11 -0800816#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000817
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000818SkMetaData& SkCanvas::getMetaData() {
819 // metadata users are rare, so we lazily allocate it. If that changes we
820 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700821 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000822 fMetaData = new SkMetaData;
823 }
824 return *fMetaData;
825}
826
reed@android.com8a1c16f2008-12-17 15:59:43 +0000827///////////////////////////////////////////////////////////////////////////////
828
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000829void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700830 this->onFlush();
831}
832
833void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000834 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000835 if (device) {
836 device->flush();
837 }
838}
839
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000840SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000841 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000842 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
843}
844
senorblancoafc7cce2016-02-02 18:44:15 -0800845SkIRect SkCanvas::getTopLayerBounds() const {
846 SkBaseDevice* d = this->getTopDevice();
847 if (!d) {
848 return SkIRect::MakeEmpty();
849 }
850 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
851}
852
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000853SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000854 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000855 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000856 SkASSERT(rec && rec->fLayer);
857 return rec->fLayer->fDevice;
858}
859
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000860SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000861 if (updateMatrixClip) {
862 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
863 }
reed@google.com9266fed2011-03-30 00:18:03 +0000864 return fMCRec->fTopLayer->fDevice;
865}
866
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000867bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700868 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000869 return false;
870 }
871
872 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700873 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700874 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000875 return false;
876 }
877 weAllocated = true;
878 }
879
reedcf01e312015-05-23 19:14:51 -0700880 SkAutoPixmapUnlock unlocker;
881 if (bitmap->requestLock(&unlocker)) {
882 const SkPixmap& pm = unlocker.pixmap();
883 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
884 return true;
885 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000886 }
887
888 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700889 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000890 }
891 return false;
892}
reed@google.com51df9e32010-12-23 19:29:18 +0000893
bsalomon@google.comc6980972011-11-02 19:57:21 +0000894bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000895 SkIRect r = srcRect;
896 const SkISize size = this->getBaseLayerSize();
897 if (!r.intersect(0, 0, size.width(), size.height())) {
898 bitmap->reset();
899 return false;
900 }
901
reed84825042014-09-02 12:50:45 -0700902 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000903 // bitmap will already be reset.
904 return false;
905 }
906 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
907 bitmap->reset();
908 return false;
909 }
910 return true;
911}
912
reed96472de2014-12-10 09:53:42 -0800913bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000914 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000915 if (!device) {
916 return false;
917 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000918 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800919
reed96472de2014-12-10 09:53:42 -0800920 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
921 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000922 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000923 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000924
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000925 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800926 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000927}
928
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000929bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700930 SkAutoPixmapUnlock unlocker;
931 if (bitmap.requestLock(&unlocker)) {
932 const SkPixmap& pm = unlocker.pixmap();
933 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000934 }
935 return false;
936}
937
938bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
939 int x, int y) {
940 switch (origInfo.colorType()) {
941 case kUnknown_SkColorType:
942 case kIndex_8_SkColorType:
943 return false;
944 default:
945 break;
946 }
halcanary96fcdcc2015-08-27 07:41:13 -0700947 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000948 return false;
949 }
950
951 const SkISize size = this->getBaseLayerSize();
952 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
953 if (!target.intersect(0, 0, size.width(), size.height())) {
954 return false;
955 }
956
957 SkBaseDevice* device = this->getDevice();
958 if (!device) {
959 return false;
960 }
961
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000962 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700963 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000964
965 // if x or y are negative, then we have to adjust pixels
966 if (x > 0) {
967 x = 0;
968 }
969 if (y > 0) {
970 y = 0;
971 }
972 // here x,y are either 0 or negative
973 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
974
reed4af35f32014-06-27 17:47:49 -0700975 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700976 const bool completeOverwrite = info.dimensions() == size;
977 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700978
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000979 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000980 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000981}
reed@google.com51df9e32010-12-23 19:29:18 +0000982
junov@google.com4370aed2012-01-18 16:21:08 +0000983SkCanvas* SkCanvas::canvasForDrawIter() {
984 return this;
985}
986
reed@android.com8a1c16f2008-12-17 15:59:43 +0000987//////////////////////////////////////////////////////////////////////////////
988
reed@android.com8a1c16f2008-12-17 15:59:43 +0000989void SkCanvas::updateDeviceCMCache() {
990 if (fDeviceCMDirty) {
991 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700992 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000994
halcanary96fcdcc2015-08-27 07:41:13 -0700995 if (nullptr == layer->fNext) { // only one layer
996 layer->updateMC(totalMatrix, totalClip, *fClipStack, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000997 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000998 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000999 do {
reed687fa1c2015-04-07 08:00:56 -07001000 layer->updateMC(totalMatrix, clip, *fClipStack, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -07001001 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001002 }
1003 fDeviceCMDirty = false;
1004 }
1005}
1006
reed@android.com8a1c16f2008-12-17 15:59:43 +00001007///////////////////////////////////////////////////////////////////////////////
1008
reed2ff1fce2014-12-11 07:07:37 -08001009void SkCanvas::checkForDeferredSave() {
1010 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -08001011 this->doSave();
1012 }
1013}
1014
reedf0090cb2014-11-26 08:55:51 -08001015int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001016#ifdef SK_DEBUG
1017 int count = 0;
1018 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1019 for (;;) {
1020 const MCRec* rec = (const MCRec*)iter.next();
1021 if (!rec) {
1022 break;
1023 }
1024 count += 1 + rec->fDeferredSaveCount;
1025 }
1026 SkASSERT(count == fSaveCount);
1027#endif
1028 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001029}
1030
1031int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001032 fSaveCount += 1;
1033 fMCRec->fDeferredSaveCount += 1;
1034 return this->getSaveCount() - 1; // return our prev value
1035}
1036
1037void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001038 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001039
1040 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1041 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001042 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001043}
1044
1045void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001046 if (fMCRec->fDeferredSaveCount > 0) {
1047 SkASSERT(fSaveCount > 1);
1048 fSaveCount -= 1;
1049 fMCRec->fDeferredSaveCount -= 1;
1050 } else {
1051 // check for underflow
1052 if (fMCStack.count() > 1) {
1053 this->willRestore();
1054 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001055 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001056 this->internalRestore();
1057 this->didRestore();
1058 }
reedf0090cb2014-11-26 08:55:51 -08001059 }
1060}
1061
1062void SkCanvas::restoreToCount(int count) {
1063 // sanity check
1064 if (count < 1) {
1065 count = 1;
1066 }
mtkleinf0f14112014-12-12 08:46:25 -08001067
reedf0090cb2014-11-26 08:55:51 -08001068 int n = this->getSaveCount() - count;
1069 for (int i = 0; i < n; ++i) {
1070 this->restore();
1071 }
1072}
1073
reed2ff1fce2014-12-11 07:07:37 -08001074void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001075 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001076 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001077 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001078
reed687fa1c2015-04-07 08:00:56 -07001079 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001080}
1081
reed4960eee2015-12-18 07:09:18 -08001082bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed@google.comb93ba452014-03-10 19:47:58 +00001083#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001084 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@google.comb93ba452014-03-10 19:47:58 +00001085#else
1086 return true;
1087#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +00001088}
1089
reed4960eee2015-12-18 07:09:18 -08001090bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001091 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001092 SkIRect clipBounds;
1093 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001094 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001095 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001096
reed96e657d2015-03-10 17:30:07 -07001097 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1098
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001099 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001100 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001101 if (bounds && !imageFilter->canComputeFastBounds()) {
1102 bounds = nullptr;
1103 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001104 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001105 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001106 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001107 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001108
reed96e657d2015-03-10 17:30:07 -07001109 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001110 r.roundOut(&ir);
1111 // early exit if the layer's bounds are clipped out
1112 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001113 if (BoundsAffectsClip(saveLayerFlags)) {
reed1f836ee2014-07-07 07:49:34 -07001114 fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001115 fDeviceClipBounds.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001116 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001117 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001118 }
1119 } else { // no user bounds, so just use the clip
1120 ir = clipBounds;
1121 }
reed180aec42015-03-11 10:39:04 -07001122 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001123
reed4960eee2015-12-18 07:09:18 -08001124 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001125 // Simplify the current clips since they will be applied properly during restore()
reed687fa1c2015-04-07 08:00:56 -07001126 fClipStack->clipDevRect(ir, SkRegion::kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001127 fMCRec->fRasterClip.setRect(ir);
msarettfbfa2582016-08-12 08:29:08 -07001128 fDeviceClipBounds = qr_clip_bounds(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001129 }
1130
1131 if (intersection) {
1132 *intersection = ir;
1133 }
1134 return true;
1135}
1136
reed4960eee2015-12-18 07:09:18 -08001137
reed4960eee2015-12-18 07:09:18 -08001138int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1139 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001140}
1141
reed70ee31b2015-12-10 13:44:45 -08001142int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001143 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1144}
1145
1146int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1147 SaveLayerRec rec(origRec);
1148 if (gIgnoreSaveLayerBounds) {
1149 rec.fBounds = nullptr;
1150 }
1151 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1152 fSaveCount += 1;
1153 this->internalSaveLayer(rec, strategy);
1154 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001155}
1156
reeda2217ef2016-07-20 06:04:34 -07001157void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1158 SkBaseDevice* dst, const SkMatrix& ctm,
1159 const SkClipStack* clipStack) {
1160 SkDraw draw;
1161 SkRasterClip rc;
1162 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1163 if (!dst->accessPixels(&draw.fDst)) {
1164 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001165 }
reeda2217ef2016-07-20 06:04:34 -07001166 draw.fMatrix = &SkMatrix::I();
1167 draw.fRC = &rc;
1168 draw.fClipStack = clipStack;
1169 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001170
1171 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001172 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001173
1174 int x = src->getOrigin().x() - dst->getOrigin().x();
1175 int y = src->getOrigin().y() - dst->getOrigin().y();
1176 auto special = src->snapSpecial();
1177 if (special) {
1178 dst->drawSpecial(draw, special.get(), x, y, p);
1179 }
robertphillips7354a4b2015-12-16 05:08:27 -08001180}
reed70ee31b2015-12-10 13:44:45 -08001181
reed129ed1c2016-02-22 06:42:31 -08001182static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1183 const SkPaint* paint) {
1184 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1185 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001186 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001187 const bool hasImageFilter = paint && paint->getImageFilter();
1188
1189 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1190 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1191 // force to L32
1192 return SkImageInfo::MakeN32(w, h, alphaType);
1193 } else {
1194 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001195 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001196 }
1197}
1198
reed4960eee2015-12-18 07:09:18 -08001199void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1200 const SkRect* bounds = rec.fBounds;
1201 const SkPaint* paint = rec.fPaint;
1202 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1203
reed@google.comb93ba452014-03-10 19:47:58 +00001204#ifndef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG
reed4960eee2015-12-18 07:09:18 -08001205 saveLayerFlags &= ~kDontClipToLayer_PrivateSaveLayerFlag;
reed@google.comb93ba452014-03-10 19:47:58 +00001206#endif
1207
reed8c30a812016-04-20 16:36:51 -07001208 SkLazyPaint lazyP;
1209 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1210 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001211 SkMatrix remainder;
1212 SkSize scale;
1213 /*
1214 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1215 * but they do handle scaling. To accommodate this, we do the following:
1216 *
1217 * 1. Stash off the current CTM
1218 * 2. Decompose the CTM into SCALE and REMAINDER
1219 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1220 * contains the REMAINDER
1221 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1222 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1223 * of the original imagefilter, and draw that (via drawSprite)
1224 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1225 *
1226 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1227 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1228 */
reed96a04f32016-04-25 09:25:15 -07001229 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001230 stashedMatrix.decomposeScale(&scale, &remainder))
1231 {
1232 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1233 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1234 SkPaint* p = lazyP.set(*paint);
1235 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1236 SkFilterQuality::kLow_SkFilterQuality,
1237 sk_ref_sp(imageFilter)));
1238 imageFilter = p->getImageFilter();
1239 paint = p;
1240 }
reed8c30a812016-04-20 16:36:51 -07001241
junov@chromium.orga907ac32012-02-24 21:54:07 +00001242 // do this before we create the layer. We don't call the public save() since
1243 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001244 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001245
1246 fDeviceCMDirty = true;
1247
1248 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001249 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001250 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001251 }
1252
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001253 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1254 // the clipRectBounds() call above?
1255 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001256 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001257 }
1258
reed4960eee2015-12-18 07:09:18 -08001259 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001260 SkPixelGeometry geo = fProps.pixelGeometry();
1261 if (paint) {
reed76033be2015-03-14 10:54:31 -07001262 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001263 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001264 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001265 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001266 }
1267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001268
robertphillips5139e502016-07-19 05:10:40 -07001269 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001270 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001271 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001272 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001273 }
reedb2db8982014-11-13 12:41:02 -08001274
robertphillips5139e502016-07-19 05:10:40 -07001275 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001276 paint);
1277
robertphillips5139e502016-07-19 05:10:40 -07001278 SkAutoTUnref<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001279 {
reed70ee31b2015-12-10 13:44:45 -08001280 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001281 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001282 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001283 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001284 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001285 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1286 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001287 SkErrorInternals::SetError(kInternalError_SkError,
1288 "Unable to create device for layer.");
1289 return;
reed61f501f2015-04-29 08:34:00 -07001290 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001291 }
robertphillips5139e502016-07-19 05:10:40 -07001292 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001293
robertphillips5139e502016-07-19 05:10:40 -07001294 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295
1296 layer->fNext = fMCRec->fTopLayer;
1297 fMCRec->fLayer = layer;
1298 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001299
1300 if (rec.fBackdrop) {
1301 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice,
1302 fMCRec->fMatrix, this->getClipStack());
1303 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001304}
1305
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001306int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001307 if (0xFF == alpha) {
1308 return this->saveLayer(bounds, nullptr);
1309 } else {
1310 SkPaint tmpPaint;
1311 tmpPaint.setAlpha(alpha);
1312 return this->saveLayer(bounds, &tmpPaint);
1313 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001314}
1315
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316void SkCanvas::internalRestore() {
1317 SkASSERT(fMCStack.count() != 0);
1318
1319 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320
reed687fa1c2015-04-07 08:00:56 -07001321 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001322
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001323 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001324 DeviceCM* layer = fMCRec->fLayer; // may be null
1325 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001326 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327
1328 // now do the normal restore()
1329 fMCRec->~MCRec(); // balanced in save()
1330 fMCStack.pop_back();
1331 fMCRec = (MCRec*)fMCStack.back();
1332
1333 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1334 since if we're being recorded, we don't want to record this (the
1335 recorder will have already recorded the restore).
1336 */
bsalomon49f085d2014-09-05 13:34:00 -07001337 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001338 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001339 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001340 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001341 // restore what we smashed in internalSaveLayer
1342 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001343 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001344 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001345 delete layer;
reedb679ca82015-04-07 04:40:48 -07001346 } else {
1347 // we're at the root
reeda499f902015-05-01 09:34:31 -07001348 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001349 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001350 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001351 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001352 }
msarettfbfa2582016-08-12 08:29:08 -07001353
1354 if (fMCRec) {
1355 fConservativeIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
1356 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1357 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001358}
1359
reede8f30622016-03-23 18:59:25 -07001360sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001361 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001362 props = &fProps;
1363 }
1364 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001365}
1366
reede8f30622016-03-23 18:59:25 -07001367sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001368 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001369 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001370}
1371
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001372SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001373 return this->onImageInfo();
1374}
1375
1376SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001377 SkBaseDevice* dev = this->getDevice();
1378 if (dev) {
1379 return dev->imageInfo();
1380 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001381 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001382 }
1383}
1384
brianosman898235c2016-04-06 07:38:23 -07001385bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001386 return this->onGetProps(props);
1387}
1388
1389bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001390 SkBaseDevice* dev = this->getDevice();
1391 if (dev) {
1392 if (props) {
1393 *props = fProps;
1394 }
1395 return true;
1396 } else {
1397 return false;
1398 }
1399}
1400
reed6ceeebd2016-03-09 14:26:26 -08001401#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001402const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001403 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001404 if (this->peekPixels(&pmap)) {
1405 if (info) {
1406 *info = pmap.info();
1407 }
1408 if (rowBytes) {
1409 *rowBytes = pmap.rowBytes();
1410 }
1411 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001412 }
reed6ceeebd2016-03-09 14:26:26 -08001413 return nullptr;
1414}
1415#endif
1416
1417bool SkCanvas::peekPixels(SkPixmap* pmap) {
1418 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001419}
1420
reed884e97c2015-05-26 11:31:54 -07001421bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001422 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001423 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001424}
1425
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001426void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001427 SkPixmap pmap;
1428 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001429 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001430 }
1431 if (info) {
1432 *info = pmap.info();
1433 }
1434 if (rowBytes) {
1435 *rowBytes = pmap.rowBytes();
1436 }
1437 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001438 *origin = this->getTopDevice(false)->getOrigin();
1439 }
reed884e97c2015-05-26 11:31:54 -07001440 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001441}
1442
reed884e97c2015-05-26 11:31:54 -07001443bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001444 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001445 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001446}
1447
reed@android.com8a1c16f2008-12-17 15:59:43 +00001448/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449
reed7503d602016-07-15 14:23:29 -07001450void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001451 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001452 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453 paint = &tmp;
1454 }
reed@google.com4b226022011-01-11 18:32:13 +00001455
reed@google.com8926b162012-03-23 15:36:36 +00001456 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001457
reed@android.com8a1c16f2008-12-17 15:59:43 +00001458 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001459 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001460 paint = &looper.paint();
1461 SkImageFilter* filter = paint->getImageFilter();
1462 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001463 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001464 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001465 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001466 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001467 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468 }
reeda2217ef2016-07-20 06:04:34 -07001469
reed@google.com4e2b3d32011-04-07 14:18:59 +00001470 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001471}
1472
reed32704672015-12-16 08:27:10 -08001473/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001474
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001475void SkCanvas::translate(SkScalar dx, SkScalar dy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001476 SkMatrix m;
1477 m.setTranslate(dx, dy);
1478 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001479}
1480
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001481void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001482 SkMatrix m;
1483 m.setScale(sx, sy);
1484 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001485}
1486
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001487void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001488 SkMatrix m;
1489 m.setRotate(degrees);
1490 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491}
1492
bungeman7438bfc2016-07-12 15:01:19 -07001493void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1494 SkMatrix m;
1495 m.setRotate(degrees, px, py);
1496 this->concat(m);
1497}
1498
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001499void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001500 SkMatrix m;
1501 m.setSkew(sx, sy);
1502 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001503}
1504
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001505void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001506 if (matrix.isIdentity()) {
1507 return;
1508 }
1509
reed2ff1fce2014-12-11 07:07:37 -08001510 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001511 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001512 fMCRec->fMatrix.preConcat(matrix);
msarettfbfa2582016-08-12 08:29:08 -07001513 fConservativeIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001514 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001515}
1516
reed8c30a812016-04-20 16:36:51 -07001517void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001518 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001519 fMCRec->fMatrix = matrix;
reed8c30a812016-04-20 16:36:51 -07001520}
1521
1522void SkCanvas::setMatrix(const SkMatrix& matrix) {
1523 this->checkForDeferredSave();
1524 this->internalSetMatrix(matrix);
msarettfbfa2582016-08-12 08:29:08 -07001525 fConservativeIsScaleTranslate = matrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001526 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001527}
1528
reed@android.com8a1c16f2008-12-17 15:59:43 +00001529void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001530 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531}
1532
vjiaoblack95302da2016-07-21 10:25:54 -07001533#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001534void SkCanvas::translateZ(SkScalar z) {
1535 this->checkForDeferredSave();
1536 this->fMCRec->fCurDrawDepth += z;
1537 this->didTranslateZ(z);
1538}
1539
1540SkScalar SkCanvas::getZ() const {
1541 return this->fMCRec->fCurDrawDepth;
1542}
1543
vjiaoblack95302da2016-07-21 10:25:54 -07001544void SkCanvas::setLights(sk_sp<SkLights> lights) {
1545 this->fLights = lights;
1546}
1547
1548sk_sp<SkLights> SkCanvas::getLights() const {
1549 return this->fLights;
1550}
1551#endif
1552
reed@android.com8a1c16f2008-12-17 15:59:43 +00001553//////////////////////////////////////////////////////////////////////////////
1554
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001555void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed2d1afab2016-06-29 14:33:11 -07001556 if (!fAllowSoftClip) {
1557 doAA = false;
1558 }
1559
1560#ifdef SK_SUPPORT_PRECHECK_CLIPRECT
1561 // Check if we can quick-accept the clip call (and do nothing)
1562 //
reed74467162016-06-30 08:15:35 -07001563 if (SkRegion::kIntersect_Op == op && !doAA && fMCRec->fMatrix.isScaleTranslate()) {
halcanaryc5769b22016-08-10 07:13:21 -07001564 SkRect devR;
1565 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reed2d1afab2016-06-29 14:33:11 -07001566 // NOTE: this check is CTM specific, since we might round differently with a different
1567 // CTM. Thus this is only 100% reliable if there is not global CTM scale to be
1568 // applied later (i.e. if this is going into a picture).
1569 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1570#if 0
1571 SkDebugf("ignored clipRect [%g %g %g %g]\n",
1572 rect.left(), rect.top(), rect.right(), rect.bottom());
1573#endif
1574 return;
1575 }
1576 }
1577#endif
1578
reed2ff1fce2014-12-11 07:07:37 -08001579 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001580 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1581 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001582}
1583
1584void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001585#ifdef SK_ENABLE_CLIP_QUICKREJECT
1586 if (SkRegion::kIntersect_Op == op) {
reed1f836ee2014-07-07 07:49:34 -07001587 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001588 return;
reed@google.comda17f752012-08-16 18:27:05 +00001589 }
1590
reed@google.com3b3e8952012-08-16 20:53:31 +00001591 if (this->quickReject(rect)) {
reed@google.comda17f752012-08-16 18:27:05 +00001592 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001593 fCachedLocalClipBoundsDirty = true;
reed@google.comda17f752012-08-16 18:27:05 +00001594
reed687fa1c2015-04-07 08:00:56 -07001595 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001596 (void)fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001597 fDeviceClipBounds.setEmpty();
reed2d1afab2016-06-29 14:33:11 -07001598 return;
reed@google.comda17f752012-08-16 18:27:05 +00001599 }
1600 }
1601#endif
1602
reed74467162016-06-30 08:15:35 -07001603 const bool isScaleTrans = fMCRec->fMatrix.isScaleTranslate();
reedc64eff52015-11-21 12:39:45 -08001604 SkRect devR;
reed74467162016-06-30 08:15:35 -07001605 if (isScaleTrans) {
halcanaryc5769b22016-08-10 07:13:21 -07001606 fMCRec->fMatrix.mapRectScaleTranslate(&devR, rect);
reedc64eff52015-11-21 12:39:45 -08001607 }
bsalomonac8cabd2015-11-20 18:53:07 -08001608
reed2d1afab2016-06-29 14:33:11 -07001609#ifndef SK_SUPPORT_PRECHECK_CLIPRECT
reedc64eff52015-11-21 12:39:45 -08001610 if (SkRegion::kIntersect_Op == op &&
1611 kHard_ClipEdgeStyle == edgeStyle
reed74467162016-06-30 08:15:35 -07001612 && isScaleTrans)
reedc64eff52015-11-21 12:39:45 -08001613 {
1614 if (devR.round().contains(fMCRec->fRasterClip.getBounds())) {
1615#if 0
1616 SkDebugf("------- ignored clipRect [%g %g %g %g]\n",
1617 rect.left(), rect.top(), rect.right(), rect.bottom());
1618#endif
1619 return;
1620 }
1621 }
reed2d1afab2016-06-29 14:33:11 -07001622#endif
reedc64eff52015-11-21 12:39:45 -08001623
1624 AutoValidateClip avc(this);
1625
1626 fDeviceCMDirty = true;
reedc64eff52015-11-21 12:39:45 -08001627
reed74467162016-06-30 08:15:35 -07001628 if (isScaleTrans) {
reedc64eff52015-11-21 12:39:45 -08001629 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1630 fClipStack->clipDevRect(devR, op, isAA);
senorblancoafc7cce2016-02-02 18:44:15 -08001631 fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 } else {
robertphillips@google.com12367192013-10-20 13:11:16 +00001633 // since we're rotated or some such thing, we convert the rect to a path
reed@android.com98de2bd2009-03-02 19:41:36 +00001634 // and clip against that, since it can handle any matrix. However, to
1635 // avoid recursion in the case where we are subclassed (e.g. Pictures)
1636 // we explicitly call "our" version of clipPath.
reed@android.com8a1c16f2008-12-17 15:59:43 +00001637 SkPath path;
1638
1639 path.addRect(rect);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001640 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001641 }
msarettfbfa2582016-08-12 08:29:08 -07001642
1643 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001644}
1645
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001646void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001647 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001648 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001649 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001650 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1651 } else {
1652 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001653 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001654}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001655
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001656void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001657 SkRRect transformedRRect;
reed1f836ee2014-07-07 07:49:34 -07001658 if (rrect.transform(fMCRec->fMatrix, &transformedRRect)) {
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001659 AutoValidateClip avc(this);
1660
1661 fDeviceCMDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001662 if (!fAllowSoftClip) {
1663 edgeStyle = kHard_ClipEdgeStyle;
1664 }
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001665
reed687fa1c2015-04-07 08:00:56 -07001666 fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001667
senorblancoafc7cce2016-02-02 18:44:15 -08001668 fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op,
robertphillips125f19a2015-11-23 09:00:05 -08001669 kSoft_ClipEdgeStyle == edgeStyle);
msarettfbfa2582016-08-12 08:29:08 -07001670 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001671 return;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001672 }
1673
1674 SkPath path;
1675 path.addRRect(rrect);
1676 // call the non-virtual version
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001677 this->SkCanvas::onClipPath(path, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001678}
1679
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001680void SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001681 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001682 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001683
1684 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1685 SkRect r;
1686 if (path.isRect(&r)) {
1687 this->onClipRect(r, op, edgeStyle);
1688 return;
1689 }
1690 SkRRect rrect;
1691 if (path.isOval(&r)) {
1692 rrect.setOval(r);
1693 this->onClipRRect(rrect, op, edgeStyle);
1694 return;
1695 }
1696 if (path.isRRect(&rrect)) {
1697 this->onClipRRect(rrect, op, edgeStyle);
1698 return;
1699 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001700 }
robertphillips39f05382015-11-24 09:30:12 -08001701
1702 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001703}
1704
1705void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
reed@google.comda17f752012-08-16 18:27:05 +00001706#ifdef SK_ENABLE_CLIP_QUICKREJECT
1707 if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
reed1f836ee2014-07-07 07:49:34 -07001708 if (fMCRec->fRasterClip.isEmpty()) {
reed2d1afab2016-06-29 14:33:11 -07001709 return;
reed@google.comda17f752012-08-16 18:27:05 +00001710 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001711
reed@google.com3b3e8952012-08-16 20:53:31 +00001712 if (this->quickReject(path.getBounds())) {
reed@google.comda17f752012-08-16 18:27:05 +00001713 fDeviceCMDirty = true;
reed@google.comc0784db2013-12-13 21:16:12 +00001714 fCachedLocalClipBoundsDirty = true;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00001715
reed687fa1c2015-04-07 08:00:56 -07001716 fClipStack->clipEmpty();
reed2d1afab2016-06-29 14:33:11 -07001717 (void)fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001718 fDeviceClipBounds.setEmpty();
reed2d1afab2016-06-29 14:33:11 -07001719 return;
reed@google.comda17f752012-08-16 18:27:05 +00001720 }
1721 }
1722#endif
1723
reed@google.com5c3d1472011-02-22 19:12:23 +00001724 AutoValidateClip avc(this);
1725
reed@android.com8a1c16f2008-12-17 15:59:43 +00001726 fDeviceCMDirty = true;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001727 if (!fAllowSoftClip) {
1728 edgeStyle = kHard_ClipEdgeStyle;
1729 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001730
1731 SkPath devPath;
reed1f836ee2014-07-07 07:49:34 -07001732 path.transform(fMCRec->fMatrix, &devPath);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001733
reed@google.comfe701122011-11-08 19:41:23 +00001734 // Check if the transfomation, or the original path itself
1735 // made us empty. Note this can also happen if we contained NaN
1736 // values. computing the bounds detects this, and will set our
1737 // bounds to empty if that is the case. (see SkRect::set(pts, count))
1738 if (devPath.getBounds().isEmpty()) {
1739 // resetting the path will remove any NaN or other wanky values
1740 // that might upset our scan converter.
1741 devPath.reset();
1742 }
1743
reed@google.com5c3d1472011-02-22 19:12:23 +00001744 // if we called path.swap() we could avoid a deep copy of this path
reed687fa1c2015-04-07 08:00:56 -07001745 fClipStack->clipDevPath(devPath, op, kSoft_ClipEdgeStyle == edgeStyle);
reed@google.com5c3d1472011-02-22 19:12:23 +00001746
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001747 if (fAllowSimplifyClip) {
fmalita1a481fe2015-02-04 07:39:34 -08001748 bool clipIsAA = getClipStack()->asPath(&devPath);
1749 if (clipIsAA) {
1750 edgeStyle = kSoft_ClipEdgeStyle;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001751 }
fmalita1a481fe2015-02-04 07:39:34 -08001752
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001753 op = SkRegion::kReplace_Op;
1754 }
1755
senorblancoafc7cce2016-02-02 18:44:15 -08001756 fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle);
msarettfbfa2582016-08-12 08:29:08 -07001757 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001758}
1759
commit-bot@chromium.org759cf482014-03-06 13:18:07 +00001760void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed2ff1fce2014-12-11 07:07:37 -08001761 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001762 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001763}
1764
1765void SkCanvas::onClipRegion(const SkRegion& rgn, SkRegion::Op op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001766 AutoValidateClip avc(this);
1767
reed@android.com8a1c16f2008-12-17 15:59:43 +00001768 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769
reed@google.com5c3d1472011-02-22 19:12:23 +00001770 // todo: signal fClipStack that we have a region, and therefore (I guess)
1771 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001772 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001773
reed1f836ee2014-07-07 07:49:34 -07001774 fMCRec->fRasterClip.op(rgn, op);
msarettfbfa2582016-08-12 08:29:08 -07001775 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001776}
1777
reed@google.com819c9212011-02-23 18:56:55 +00001778#ifdef SK_DEBUG
1779void SkCanvas::validateClip() const {
1780 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001781 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001782 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001783 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001784 return;
1785 }
1786
reed@google.com819c9212011-02-23 18:56:55 +00001787 SkIRect ir;
1788 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001789 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001790
reed687fa1c2015-04-07 08:00:56 -07001791 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001792 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001793 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001794 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001795 case SkClipStack::Element::kRect_Type:
1796 element->getRect().round(&ir);
1797 tmpClip.op(ir, element->getOp());
1798 break;
1799 case SkClipStack::Element::kEmpty_Type:
1800 tmpClip.setEmpty();
1801 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001802 default: {
1803 SkPath path;
1804 element->asPath(&path);
senorblancoafc7cce2016-02-02 18:44:15 -08001805 tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001806 break;
1807 }
reed@google.com819c9212011-02-23 18:56:55 +00001808 }
1809 }
reed@google.com819c9212011-02-23 18:56:55 +00001810}
1811#endif
1812
reed@google.com90c07ea2012-04-13 13:50:27 +00001813void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001814 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001815 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001816
halcanary96fcdcc2015-08-27 07:41:13 -07001817 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001818 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001819 }
1820}
1821
reed@google.com5c3d1472011-02-22 19:12:23 +00001822///////////////////////////////////////////////////////////////////////////////
1823
reed@google.com754de5f2014-02-24 19:38:20 +00001824bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001825 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001826}
1827
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001828bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001829 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001830}
1831
msarettfbfa2582016-08-12 08:29:08 -07001832static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1833#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1834 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1835 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1836 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1837 return 0xF != _mm_movemask_ps(mask);
1838#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1839 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1840 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1841 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1842 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1843#else
1844 SkRect devRectAsRect;
1845 SkRect devClipAsRect;
1846 devRect.store(&devRectAsRect.fLeft);
1847 devClip.store(&devClipAsRect.fLeft);
1848 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1849#endif
1850}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001851
msarettfbfa2582016-08-12 08:29:08 -07001852// It's important for this function to not be inlined. Otherwise the compiler will share code
1853// between the fast path and the slow path, resulting in two slow paths.
1854static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1855 const SkMatrix& matrix) {
1856 SkRect deviceRect;
1857 matrix.mapRect(&deviceRect, src);
1858 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1859}
1860
1861bool SkCanvas::quickReject(const SkRect& src) const {
1862#ifdef SK_DEBUG
1863 // Verify that fDeviceClipBounds are set properly.
1864 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001865 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001866 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001867 } else {
msarettfbfa2582016-08-12 08:29:08 -07001868 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001869 }
msarettfbfa2582016-08-12 08:29:08 -07001870
1871 // Verify that fConservativeIsScaleTranslate is set properly.
1872 SkASSERT(!fConservativeIsScaleTranslate || fMCRec->fMatrix.isScaleTranslate());
1873#endif
1874
1875 if (!fConservativeIsScaleTranslate) {
1876 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1877 }
1878
1879 // We inline the implementation of mapScaleTranslate() for the fast path.
1880 float sx = fMCRec->fMatrix.getScaleX();
1881 float sy = fMCRec->fMatrix.getScaleY();
1882 float tx = fMCRec->fMatrix.getTranslateX();
1883 float ty = fMCRec->fMatrix.getTranslateY();
1884 Sk4f scale(sx, sy, sx, sy);
1885 Sk4f trans(tx, ty, tx, ty);
1886
1887 // Apply matrix.
1888 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1889
1890 // Make sure left < right, top < bottom.
1891 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1892 Sk4f min = Sk4f::Min(ltrb, rblt);
1893 Sk4f max = Sk4f::Max(ltrb, rblt);
1894 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1895 // ARM this sequence generates the fastest (a single instruction).
1896 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1897
1898 // Check if the device rect is NaN or outside the clip.
1899 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001900}
1901
reed@google.com3b3e8952012-08-16 20:53:31 +00001902bool SkCanvas::quickReject(const SkPath& path) const {
1903 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001904}
1905
reed@google.com3b3e8952012-08-16 20:53:31 +00001906bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001907 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001908 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001909 return false;
1910 }
1911
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001912 SkMatrix inverse;
1913 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001914 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001915 if (bounds) {
1916 bounds->setEmpty();
1917 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001918 return false;
1919 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001920
bsalomon49f085d2014-09-05 13:34:00 -07001921 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001922 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001923 // adjust it outwards in case we are antialiasing
1924 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001925
reed@google.com8f4d2302013-12-17 16:44:46 +00001926 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1927 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001928 inverse.mapRect(bounds, r);
1929 }
1930 return true;
1931}
1932
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001933bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001934 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001935 if (clip.isEmpty()) {
1936 if (bounds) {
1937 bounds->setEmpty();
1938 }
1939 return false;
1940 }
1941
bsalomon49f085d2014-09-05 13:34:00 -07001942 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001943 *bounds = clip.getBounds();
1944 }
1945 return true;
1946}
1947
reed@android.com8a1c16f2008-12-17 15:59:43 +00001948const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001949 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001950}
1951
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001952const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001953 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001954}
1955
robertphillips175dd9b2016-04-28 14:32:04 -07001956GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001957 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001958 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001959}
1960
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001961GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001962 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001963 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001964}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001965
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001966void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1967 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001968 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001969 if (outer.isEmpty()) {
1970 return;
1971 }
1972 if (inner.isEmpty()) {
1973 this->drawRRect(outer, paint);
1974 return;
1975 }
1976
1977 // We don't have this method (yet), but technically this is what we should
1978 // be able to assert...
1979 // SkASSERT(outer.contains(inner));
1980 //
1981 // For now at least check for containment of bounds
1982 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1983
1984 this->onDrawDRRect(outer, inner, paint);
1985}
1986
reed41af9662015-01-05 07:49:08 -08001987// These need to stop being virtual -- clients need to override the onDraw... versions
1988
1989void SkCanvas::drawPaint(const SkPaint& paint) {
1990 this->onDrawPaint(paint);
1991}
1992
1993void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1994 this->onDrawRect(r, paint);
1995}
1996
1997void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1998 this->onDrawOval(r, paint);
1999}
2000
2001void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
2002 this->onDrawRRect(rrect, paint);
2003}
2004
2005void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
2006 this->onDrawPoints(mode, count, pts, paint);
2007}
2008
2009void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
2010 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
2011 const uint16_t indices[], int indexCount, const SkPaint& paint) {
2012 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
2013 indices, indexCount, paint);
2014}
2015
2016void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
2017 this->onDrawPath(path, paint);
2018}
2019
reeda85d4d02015-05-06 12:56:48 -07002020void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002021 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07002022 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08002023}
2024
reede47829b2015-08-06 10:02:53 -07002025void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
2026 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002027 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002028 if (dst.isEmpty() || src.isEmpty()) {
2029 return;
2030 }
2031 this->onDrawImageRect(image, &src, dst, paint, constraint);
2032}
reed41af9662015-01-05 07:49:08 -08002033
reed84984ef2015-07-17 07:09:43 -07002034void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
2035 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002036 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002037 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002038}
2039
reede47829b2015-08-06 10:02:53 -07002040void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
2041 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08002042 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07002043 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
2044 constraint);
2045}
reede47829b2015-08-06 10:02:53 -07002046
reed4c21dc52015-06-25 12:32:03 -07002047void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2048 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002049 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07002050 if (dst.isEmpty()) {
2051 return;
2052 }
msarett552bca92016-08-03 06:53:26 -07002053 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
2054 this->onDrawImageNine(image, center, dst, paint);
2055 } else {
reede47829b2015-08-06 10:02:53 -07002056 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002057 }
reed4c21dc52015-06-25 12:32:03 -07002058}
2059
reed41af9662015-01-05 07:49:08 -08002060void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002061 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002062 return;
2063 }
reed41af9662015-01-05 07:49:08 -08002064 this->onDrawBitmap(bitmap, dx, dy, paint);
2065}
2066
reede47829b2015-08-06 10:02:53 -07002067void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002068 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002069 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002070 return;
2071 }
reede47829b2015-08-06 10:02:53 -07002072 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002073}
2074
reed84984ef2015-07-17 07:09:43 -07002075void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2076 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002077 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002078}
2079
reede47829b2015-08-06 10:02:53 -07002080void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2081 SrcRectConstraint constraint) {
2082 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2083 constraint);
2084}
reede47829b2015-08-06 10:02:53 -07002085
reed41af9662015-01-05 07:49:08 -08002086void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2087 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002088 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002089 return;
2090 }
msarett552bca92016-08-03 06:53:26 -07002091 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
2092 this->onDrawBitmapNine(bitmap, center, dst, paint);
2093 } else {
reeda5517e22015-07-14 10:54:12 -07002094 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002095 }
msarett552bca92016-08-03 06:53:26 -07002096
reed41af9662015-01-05 07:49:08 -08002097}
2098
msarettc573a402016-08-02 08:05:56 -07002099void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2100 const SkPaint* paint) {
2101 sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
2102 this->drawImageLattice(image.get(), lattice, dst, paint);
2103}
2104
2105void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2106 const SkPaint* paint) {
2107 RETURN_ON_NULL(image);
2108 if (dst.isEmpty()) {
2109 return;
2110 }
msarett552bca92016-08-03 06:53:26 -07002111 if (SkLatticeIter::Valid(image->width(), image->height(), lattice)) {
2112 this->onDrawImageLattice(image, lattice, dst, paint);
2113 } else {
msarettc573a402016-08-02 08:05:56 -07002114 this->drawImageRect(image, dst, paint);
2115 }
msarettc573a402016-08-02 08:05:56 -07002116}
2117
reed71c3c762015-06-24 10:29:17 -07002118void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2119 const SkColor colors[], int count, SkXfermode::Mode mode,
2120 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002121 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002122 if (count <= 0) {
2123 return;
2124 }
2125 SkASSERT(atlas);
2126 SkASSERT(xform);
2127 SkASSERT(tex);
2128 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2129}
2130
reedf70b5312016-03-04 16:36:20 -08002131void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2132 if (key) {
2133 this->onDrawAnnotation(rect, key, value);
2134 }
2135}
2136
reede47829b2015-08-06 10:02:53 -07002137void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2138 const SkPaint* paint, SrcRectConstraint constraint) {
2139 if (src) {
2140 this->drawImageRect(image, *src, dst, paint, constraint);
2141 } else {
2142 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2143 dst, paint, constraint);
2144 }
2145}
2146void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2147 const SkPaint* paint, SrcRectConstraint constraint) {
2148 if (src) {
2149 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2150 } else {
2151 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2152 dst, paint, constraint);
2153 }
2154}
2155
tomhudsoncb3bd182016-05-18 07:24:16 -07002156void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2157 SkIRect layer_bounds = this->getTopLayerBounds();
2158 if (matrix) {
2159 *matrix = this->getTotalMatrix();
2160 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2161 }
2162 if (clip_bounds) {
2163 this->getClipDeviceBounds(clip_bounds);
2164 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2165 }
2166}
2167
reed@android.com8a1c16f2008-12-17 15:59:43 +00002168//////////////////////////////////////////////////////////////////////////////
2169// These are the virtual drawing methods
2170//////////////////////////////////////////////////////////////////////////////
2171
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002172void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002173 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002174 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2175 }
2176}
2177
reed41af9662015-01-05 07:49:08 -08002178void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002179 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002180 this->internalDrawPaint(paint);
2181}
2182
2183void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002184 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002185
2186 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002187 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002188 }
2189
reed@google.com4e2b3d32011-04-07 14:18:59 +00002190 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002191}
2192
reed41af9662015-01-05 07:49:08 -08002193void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2194 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002195 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196 if ((long)count <= 0) {
2197 return;
2198 }
2199
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002200 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002201 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002202 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002203 // special-case 2 points (common for drawing a single line)
2204 if (2 == count) {
2205 r.set(pts[0], pts[1]);
2206 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002207 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002208 }
senorblanco87e066e2015-10-28 11:23:36 -07002209 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2210 return;
2211 }
2212 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002213 }
reed@google.coma584aed2012-05-16 14:06:02 +00002214
halcanary96fcdcc2015-08-27 07:41:13 -07002215 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002216
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002217 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002218
reed@android.com8a1c16f2008-12-17 15:59:43 +00002219 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002220 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221 }
reed@google.com4b226022011-01-11 18:32:13 +00002222
reed@google.com4e2b3d32011-04-07 14:18:59 +00002223 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224}
2225
reed41af9662015-01-05 07:49:08 -08002226void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002227 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002228 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002229 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002230 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002231 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2232 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2233 SkRect tmp(r);
2234 tmp.sort();
2235
senorblanco87e066e2015-10-28 11:23:36 -07002236 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2237 return;
2238 }
2239 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002240 }
reed@google.com4b226022011-01-11 18:32:13 +00002241
reedc83a2972015-07-16 07:40:45 -07002242 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002243
2244 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002245 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002246 }
2247
reed@google.com4e2b3d32011-04-07 14:18:59 +00002248 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002249}
2250
reed41af9662015-01-05 07:49:08 -08002251void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002252 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002253 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002254 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002255 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002256 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2257 return;
2258 }
2259 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002260 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002261
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002262 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002263
2264 while (iter.next()) {
2265 iter.fDevice->drawOval(iter, oval, looper.paint());
2266 }
2267
2268 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002269}
2270
reed41af9662015-01-05 07:49:08 -08002271void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002272 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002273 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002274 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002275 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002276 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2277 return;
2278 }
2279 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002280 }
2281
2282 if (rrect.isRect()) {
2283 // call the non-virtual version
2284 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002285 return;
2286 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002287 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002288 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2289 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002290 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002291
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002292 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002293
2294 while (iter.next()) {
2295 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2296 }
2297
2298 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002299}
2300
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002301void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2302 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002303 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002304 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002305 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002306 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2307 return;
2308 }
2309 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002310 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002311
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002312 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002313
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002314 while (iter.next()) {
2315 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2316 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002317
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002318 LOOPER_END
2319}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002320
reed41af9662015-01-05 07:49:08 -08002321void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002322 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002323 if (!path.isFinite()) {
2324 return;
2325 }
2326
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002327 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002328 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002329 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002330 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002331 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2332 return;
2333 }
2334 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002335 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002336
2337 const SkRect& r = path.getBounds();
2338 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002339 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002340 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002341 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002342 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002343 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002344
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002345 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002346
2347 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002348 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002349 }
2350
reed@google.com4e2b3d32011-04-07 14:18:59 +00002351 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002352}
2353
reed262a71b2015-12-05 13:07:27 -08002354bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002355 if (!paint.getImageFilter()) {
2356 return false;
2357 }
2358
2359 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002360 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002361 return false;
2362 }
2363
2364 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2365 // Once we can filter and the filter will return a result larger than itself, we should be
2366 // able to remove this constraint.
2367 // skbug.com/4526
2368 //
2369 SkPoint pt;
2370 ctm.mapXY(x, y, &pt);
2371 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2372 return ir.contains(fMCRec->fRasterClip.getBounds());
2373}
2374
reeda85d4d02015-05-06 12:56:48 -07002375void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002376 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002377 SkRect bounds = SkRect::MakeXYWH(x, y,
2378 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002379 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002380 SkRect tmp = bounds;
2381 if (paint) {
2382 paint->computeFastBounds(tmp, &tmp);
2383 }
2384 if (this->quickReject(tmp)) {
2385 return;
2386 }
reeda85d4d02015-05-06 12:56:48 -07002387 }
halcanary9d524f22016-03-29 09:03:52 -07002388
reeda85d4d02015-05-06 12:56:48 -07002389 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002390 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002391 paint = lazy.init();
2392 }
reed262a71b2015-12-05 13:07:27 -08002393
reeda2217ef2016-07-20 06:04:34 -07002394 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002395 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2396 *paint);
2397 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002398 special = this->getDevice()->makeSpecial(image);
2399 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002400 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002401 }
2402 }
2403
reed262a71b2015-12-05 13:07:27 -08002404 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2405
reeda85d4d02015-05-06 12:56:48 -07002406 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002407 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002408 if (special) {
2409 SkPoint pt;
2410 iter.fMatrix->mapXY(x, y, &pt);
2411 iter.fDevice->drawSpecial(iter, special.get(),
2412 SkScalarRoundToInt(pt.fX),
2413 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002414 } else {
2415 iter.fDevice->drawImage(iter, image, x, y, pnt);
2416 }
reeda85d4d02015-05-06 12:56:48 -07002417 }
halcanary9d524f22016-03-29 09:03:52 -07002418
reeda85d4d02015-05-06 12:56:48 -07002419 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002420}
2421
msarettc573a402016-08-02 08:05:56 -07002422void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2423 const SkPaint* paint) {
2424 if (nullptr == paint || paint->canComputeFastBounds()) {
2425 SkRect storage;
2426 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2427 return;
2428 }
2429 }
2430
2431 SkLazyPaint lazy;
2432 if (nullptr == paint) {
2433 paint = lazy.init();
2434 }
2435
2436 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2437
2438 while (iter.next()) {
2439 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2440 }
2441
2442 LOOPER_END
2443}
2444
reed41af9662015-01-05 07:49:08 -08002445void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002446 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002447 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002448 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002449 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002450 if (paint) {
2451 paint->computeFastBounds(dst, &storage);
2452 }
2453 if (this->quickReject(storage)) {
2454 return;
2455 }
reeda85d4d02015-05-06 12:56:48 -07002456 }
2457 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002458 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002459 paint = lazy.init();
2460 }
halcanary9d524f22016-03-29 09:03:52 -07002461
senorblancoc41e7e12015-12-07 12:51:30 -08002462 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002463 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002464
reeda85d4d02015-05-06 12:56:48 -07002465 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002466 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002467 }
halcanary9d524f22016-03-29 09:03:52 -07002468
reeda85d4d02015-05-06 12:56:48 -07002469 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002470}
2471
reed41af9662015-01-05 07:49:08 -08002472void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002473 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002474 SkDEBUGCODE(bitmap.validate();)
2475
reed33366972015-10-08 09:22:02 -07002476 if (bitmap.drawsNothing()) {
2477 return;
2478 }
2479
2480 SkLazyPaint lazy;
2481 if (nullptr == paint) {
2482 paint = lazy.init();
2483 }
2484
2485 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2486
2487 SkRect storage;
2488 const SkRect* bounds = nullptr;
2489 if (paint->canComputeFastBounds()) {
2490 bitmap.getBounds(&storage);
2491 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002492 SkRect tmp = storage;
2493 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2494 return;
2495 }
2496 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002497 }
reed@google.com4b226022011-01-11 18:32:13 +00002498
reeda2217ef2016-07-20 06:04:34 -07002499 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002500 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2501 *paint);
2502 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002503 special = this->getDevice()->makeSpecial(bitmap);
2504 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002505 drawAsSprite = false;
2506 }
2507 }
2508
reed262a71b2015-12-05 13:07:27 -08002509 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002510
2511 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002512 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002513 if (special) {
reed262a71b2015-12-05 13:07:27 -08002514 SkPoint pt;
2515 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002516 iter.fDevice->drawSpecial(iter, special.get(),
2517 SkScalarRoundToInt(pt.fX),
2518 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002519 } else {
2520 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2521 }
reed33366972015-10-08 09:22:02 -07002522 }
msarettfbfa2582016-08-12 08:29:08 -07002523
reed33366972015-10-08 09:22:02 -07002524 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002525}
2526
reed@google.com9987ec32011-09-07 11:57:52 +00002527// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002528void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002529 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002530 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002531 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002532 return;
2533 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002534
halcanary96fcdcc2015-08-27 07:41:13 -07002535 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002536 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002537 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2538 return;
2539 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002540 }
reed@google.com3d608122011-11-21 15:16:16 +00002541
reed@google.com33535f32012-09-25 15:37:50 +00002542 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002543 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002544 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002545 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002546
senorblancoc41e7e12015-12-07 12:51:30 -08002547 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002548 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002549
reed@google.com33535f32012-09-25 15:37:50 +00002550 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002551 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002552 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002553
reed@google.com33535f32012-09-25 15:37:50 +00002554 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002555}
2556
reed41af9662015-01-05 07:49:08 -08002557void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002558 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002559 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002560 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002561 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002562}
2563
reed4c21dc52015-06-25 12:32:03 -07002564void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2565 const SkPaint* paint) {
2566 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002567
halcanary96fcdcc2015-08-27 07:41:13 -07002568 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002569 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002570 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2571 return;
2572 }
reed@google.com3d608122011-11-21 15:16:16 +00002573 }
halcanary9d524f22016-03-29 09:03:52 -07002574
reed4c21dc52015-06-25 12:32:03 -07002575 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002576 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002577 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002578 }
halcanary9d524f22016-03-29 09:03:52 -07002579
senorblancoc41e7e12015-12-07 12:51:30 -08002580 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002581
reed4c21dc52015-06-25 12:32:03 -07002582 while (iter.next()) {
2583 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002584 }
halcanary9d524f22016-03-29 09:03:52 -07002585
reed4c21dc52015-06-25 12:32:03 -07002586 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002587}
2588
reed41af9662015-01-05 07:49:08 -08002589void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2590 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002591 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002592 SkDEBUGCODE(bitmap.validate();)
2593
halcanary96fcdcc2015-08-27 07:41:13 -07002594 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002595 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002596 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2597 return;
2598 }
reed4c21dc52015-06-25 12:32:03 -07002599 }
halcanary9d524f22016-03-29 09:03:52 -07002600
reed4c21dc52015-06-25 12:32:03 -07002601 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002602 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002603 paint = lazy.init();
2604 }
halcanary9d524f22016-03-29 09:03:52 -07002605
senorblancoc41e7e12015-12-07 12:51:30 -08002606 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002607
reed4c21dc52015-06-25 12:32:03 -07002608 while (iter.next()) {
2609 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2610 }
halcanary9d524f22016-03-29 09:03:52 -07002611
reed4c21dc52015-06-25 12:32:03 -07002612 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002613}
2614
reed@google.comf67e4cf2011-03-15 20:56:58 +00002615class SkDeviceFilteredPaint {
2616public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002617 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002618 uint32_t filteredFlags = device->filterTextFlags(paint);
2619 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002620 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002621 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002622 fPaint = newPaint;
2623 } else {
2624 fPaint = &paint;
2625 }
2626 }
2627
reed@google.comf67e4cf2011-03-15 20:56:58 +00002628 const SkPaint& paint() const { return *fPaint; }
2629
2630private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002631 const SkPaint* fPaint;
2632 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002633};
2634
bungeman@google.com52c748b2011-08-22 21:30:43 +00002635void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2636 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002637 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002638 draw.fDevice->drawRect(draw, r, paint);
2639 } else {
2640 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002641 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002642 draw.fDevice->drawRect(draw, r, p);
2643 }
2644}
2645
2646void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2647 const char text[], size_t byteLength,
2648 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002649 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002650
2651 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002652 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002653 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002654 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002655 return;
2656 }
2657
2658 SkScalar width = 0;
2659 SkPoint start;
2660
2661 start.set(0, 0); // to avoid warning
2662 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2663 SkPaint::kStrikeThruText_Flag)) {
2664 width = paint.measureText(text, byteLength);
2665
2666 SkScalar offsetX = 0;
2667 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2668 offsetX = SkScalarHalf(width);
2669 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2670 offsetX = width;
2671 }
2672 start.set(x - offsetX, y);
2673 }
2674
2675 if (0 == width) {
2676 return;
2677 }
2678
2679 uint32_t flags = paint.getFlags();
2680
2681 if (flags & (SkPaint::kUnderlineText_Flag |
2682 SkPaint::kStrikeThruText_Flag)) {
2683 SkScalar textSize = paint.getTextSize();
2684 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2685 SkRect r;
2686
2687 r.fLeft = start.fX;
2688 r.fRight = start.fX + width;
2689
2690 if (flags & SkPaint::kUnderlineText_Flag) {
2691 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2692 start.fY);
2693 r.fTop = offset;
2694 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002695 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002696 }
2697 if (flags & SkPaint::kStrikeThruText_Flag) {
2698 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2699 start.fY);
2700 r.fTop = offset;
2701 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002702 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002703 }
2704 }
2705}
2706
reed@google.come0d9ce82014-04-23 04:00:17 +00002707void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2708 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002709 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002710
2711 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002712 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002713 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002714 DrawTextDecorations(iter, dfp.paint(),
2715 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002716 }
2717
reed@google.com4e2b3d32011-04-07 14:18:59 +00002718 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002719}
2720
reed@google.come0d9ce82014-04-23 04:00:17 +00002721void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2722 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002723 SkPoint textOffset = SkPoint::Make(0, 0);
2724
halcanary96fcdcc2015-08-27 07:41:13 -07002725 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002726
reed@android.com8a1c16f2008-12-17 15:59:43 +00002727 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002728 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002729 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002730 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002731 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002732
reed@google.com4e2b3d32011-04-07 14:18:59 +00002733 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002734}
2735
reed@google.come0d9ce82014-04-23 04:00:17 +00002736void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2737 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002738
2739 SkPoint textOffset = SkPoint::Make(0, constY);
2740
halcanary96fcdcc2015-08-27 07:41:13 -07002741 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002742
reed@android.com8a1c16f2008-12-17 15:59:43 +00002743 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002744 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002745 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002746 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002747 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002748
reed@google.com4e2b3d32011-04-07 14:18:59 +00002749 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002750}
2751
reed@google.come0d9ce82014-04-23 04:00:17 +00002752void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2753 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002754 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002755
reed@android.com8a1c16f2008-12-17 15:59:43 +00002756 while (iter.next()) {
2757 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002758 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002759 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002760
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002761 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002762}
2763
reed45561a02016-07-07 12:47:17 -07002764void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2765 const SkRect* cullRect, const SkPaint& paint) {
2766 if (cullRect && this->quickReject(*cullRect)) {
2767 return;
2768 }
2769
2770 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2771
2772 while (iter.next()) {
2773 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2774 }
2775
2776 LOOPER_END
2777}
2778
fmalita00d5c2c2014-08-21 08:53:26 -07002779void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2780 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002781
fmalita85d5eb92015-03-04 11:20:12 -08002782 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002783 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002784 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002785 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002786 SkRect tmp;
2787 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2788 return;
2789 }
2790 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002791 }
2792
fmalita024f9962015-03-03 19:08:17 -08002793 // We cannot filter in the looper as we normally do, because the paint is
2794 // incomplete at this point (text-related attributes are embedded within blob run paints).
2795 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002796 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002797
fmalita85d5eb92015-03-04 11:20:12 -08002798 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002799
fmalitaaa1b9122014-08-28 14:32:24 -07002800 while (iter.next()) {
2801 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002802 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002803 }
2804
fmalitaaa1b9122014-08-28 14:32:24 -07002805 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002806
2807 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002808}
2809
reed@google.come0d9ce82014-04-23 04:00:17 +00002810// These will become non-virtual, so they always call the (virtual) onDraw... method
2811void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2812 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002813 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002814 if (byteLength) {
2815 this->onDrawText(text, byteLength, x, y, paint);
2816 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002817}
2818void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2819 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002820 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002821 if (byteLength) {
2822 this->onDrawPosText(text, byteLength, pos, paint);
2823 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002824}
2825void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2826 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002827 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002828 if (byteLength) {
2829 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2830 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002831}
2832void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2833 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002834 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002835 if (byteLength) {
2836 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2837 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002838}
reed45561a02016-07-07 12:47:17 -07002839void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2840 const SkRect* cullRect, const SkPaint& paint) {
2841 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2842 if (byteLength) {
2843 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2844 }
2845}
fmalita00d5c2c2014-08-21 08:53:26 -07002846void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2847 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002848 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002849 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002850 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002851}
reed@google.come0d9ce82014-04-23 04:00:17 +00002852
reed41af9662015-01-05 07:49:08 -08002853void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2854 const SkPoint verts[], const SkPoint texs[],
2855 const SkColor colors[], SkXfermode* xmode,
2856 const uint16_t indices[], int indexCount,
2857 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002858 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002859 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002860
reed@android.com8a1c16f2008-12-17 15:59:43 +00002861 while (iter.next()) {
2862 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002863 colors, xmode, indices, indexCount,
2864 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002865 }
reed@google.com4b226022011-01-11 18:32:13 +00002866
reed@google.com4e2b3d32011-04-07 14:18:59 +00002867 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002868}
2869
dandovb3c9d1c2014-08-12 08:34:29 -07002870void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2871 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002872 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002873 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002874 return;
2875 }
mtklein6cfa73a2014-08-13 13:33:49 -07002876
dandovecfff212014-08-04 10:02:00 -07002877 // Since a patch is always within the convex hull of the control points, we discard it when its
2878 // bounding rectangle is completely outside the current clip.
2879 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002880 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002881 if (this->quickReject(bounds)) {
2882 return;
2883 }
mtklein6cfa73a2014-08-13 13:33:49 -07002884
dandovb3c9d1c2014-08-12 08:34:29 -07002885 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2886}
2887
2888void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2889 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2890
halcanary96fcdcc2015-08-27 07:41:13 -07002891 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002892
dandovecfff212014-08-04 10:02:00 -07002893 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002894 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002895 }
mtklein6cfa73a2014-08-13 13:33:49 -07002896
dandovecfff212014-08-04 10:02:00 -07002897 LOOPER_END
2898}
2899
reeda8db7282015-07-07 10:22:31 -07002900void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002901 RETURN_ON_NULL(dr);
2902 if (x || y) {
2903 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2904 this->onDrawDrawable(dr, &matrix);
2905 } else {
2906 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002907 }
2908}
2909
reeda8db7282015-07-07 10:22:31 -07002910void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002911 RETURN_ON_NULL(dr);
2912 if (matrix && matrix->isIdentity()) {
2913 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002914 }
reede3b38ce2016-01-08 09:18:44 -08002915 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002916}
2917
2918void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2919 SkRect bounds = dr->getBounds();
2920 if (matrix) {
2921 matrix->mapRect(&bounds);
2922 }
2923 if (this->quickReject(bounds)) {
2924 return;
2925 }
2926 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002927}
2928
reed71c3c762015-06-24 10:29:17 -07002929void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2930 const SkColor colors[], int count, SkXfermode::Mode mode,
2931 const SkRect* cull, const SkPaint* paint) {
2932 if (cull && this->quickReject(*cull)) {
2933 return;
2934 }
2935
2936 SkPaint pnt;
2937 if (paint) {
2938 pnt = *paint;
2939 }
halcanary9d524f22016-03-29 09:03:52 -07002940
halcanary96fcdcc2015-08-27 07:41:13 -07002941 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002942 while (iter.next()) {
2943 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2944 }
2945 LOOPER_END
2946}
2947
reedf70b5312016-03-04 16:36:20 -08002948void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2949 SkASSERT(key);
2950
2951 SkPaint paint;
2952 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2953 while (iter.next()) {
2954 iter.fDevice->drawAnnotation(iter, rect, key, value);
2955 }
2956 LOOPER_END
2957}
2958
reed@android.com8a1c16f2008-12-17 15:59:43 +00002959//////////////////////////////////////////////////////////////////////////////
2960// These methods are NOT virtual, and therefore must call back into virtual
2961// methods, rather than actually drawing themselves.
2962//////////////////////////////////////////////////////////////////////////////
2963
2964void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002965 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002966 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002967 SkPaint paint;
2968
2969 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002970 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002971 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002972 }
2973 this->drawPaint(paint);
2974}
2975
reed@android.com845fdac2009-06-23 03:01:32 +00002976void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002977 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002978 SkPaint paint;
2979
2980 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00002981 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002982 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002983 }
2984 this->drawPaint(paint);
2985}
2986
2987void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002988 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002989 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002990
reed@android.com8a1c16f2008-12-17 15:59:43 +00002991 pt.set(x, y);
2992 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2993}
2994
2995void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002996 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002997 SkPoint pt;
2998 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002999
reed@android.com8a1c16f2008-12-17 15:59:43 +00003000 pt.set(x, y);
3001 paint.setColor(color);
3002 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
3003}
3004
3005void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
3006 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003007 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003008 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00003009
reed@android.com8a1c16f2008-12-17 15:59:43 +00003010 pts[0].set(x0, y0);
3011 pts[1].set(x1, y1);
3012 this->drawPoints(kLines_PointMode, 2, pts, paint);
3013}
3014
3015void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
3016 SkScalar right, SkScalar bottom,
3017 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003018 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003019 SkRect r;
3020
3021 r.set(left, top, right, bottom);
3022 this->drawRect(r, paint);
3023}
3024
3025void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
3026 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003027 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003028 if (radius < 0) {
3029 radius = 0;
3030 }
3031
3032 SkRect r;
3033 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003034 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003035}
3036
3037void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3038 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003039 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003040 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003041 SkRRect rrect;
3042 rrect.setRectXY(r, rx, ry);
3043 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003044 } else {
3045 this->drawRect(r, paint);
3046 }
3047}
3048
reed@android.com8a1c16f2008-12-17 15:59:43 +00003049void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3050 SkScalar sweepAngle, bool useCenter,
3051 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003052 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003053 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
3054 this->drawOval(oval, paint);
3055 } else {
3056 SkPath path;
3057 if (useCenter) {
3058 path.moveTo(oval.centerX(), oval.centerY());
3059 }
3060 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
3061 if (useCenter) {
3062 path.close();
3063 }
3064 this->drawPath(path, paint);
3065 }
3066}
3067
3068void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3069 const SkPath& path, SkScalar hOffset,
3070 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003071 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003072 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003073
reed@android.com8a1c16f2008-12-17 15:59:43 +00003074 matrix.setTranslate(hOffset, vOffset);
3075 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3076}
3077
reed@android.comf76bacf2009-05-13 14:00:33 +00003078///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003079
3080/**
3081 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3082 * against the playback cost of recursing into the subpicture to get at its actual ops.
3083 *
3084 * For now we pick a conservatively small value, though measurement (and other heuristics like
3085 * the type of ops contained) may justify changing this value.
3086 */
3087#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003088
reedd5fa1a42014-08-09 11:08:05 -07003089void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003090 RETURN_ON_NULL(picture);
3091
reed1c2c4412015-04-30 13:09:24 -07003092 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003093 if (matrix && matrix->isIdentity()) {
3094 matrix = nullptr;
3095 }
3096 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3097 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3098 picture->playback(this);
3099 } else {
3100 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003101 }
3102}
robertphillips9b14f262014-06-04 05:40:44 -07003103
reedd5fa1a42014-08-09 11:08:05 -07003104void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3105 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003106 if (!paint || paint->canComputeFastBounds()) {
3107 SkRect bounds = picture->cullRect();
3108 if (paint) {
3109 paint->computeFastBounds(bounds, &bounds);
3110 }
3111 if (matrix) {
3112 matrix->mapRect(&bounds);
3113 }
3114 if (this->quickReject(bounds)) {
3115 return;
3116 }
3117 }
3118
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003119 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003120 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003121}
3122
vjiaoblack95302da2016-07-21 10:25:54 -07003123#ifdef SK_EXPERIMENTAL_SHADOWING
3124void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3125 const SkMatrix* matrix,
3126 const SkPaint* paint) {
3127 RETURN_ON_NULL(picture);
3128
3129 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3130
3131 this->onDrawShadowedPicture(picture, matrix, paint);
3132}
3133
3134void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3135 const SkMatrix* matrix,
3136 const SkPaint* paint) {
vjiaoblack904527d2016-08-09 09:32:09 -07003137 if (!paint || paint->canComputeFastBounds()) {
3138 SkRect bounds = picture->cullRect();
3139 if (paint) {
3140 paint->computeFastBounds(bounds, &bounds);
3141 }
3142 if (matrix) {
3143 matrix->mapRect(&bounds);
3144 }
3145 if (this->quickReject(bounds)) {
3146 return;
3147 }
3148 }
3149
3150 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3151
3152 for (int i = 0; i < fLights->numLights(); ++i) {
3153 // skip over ambient lights; they don't cast shadows
3154 // lights that have shadow maps do not need updating (because lights are immutable)
3155
3156 if (SkLights::Light::kAmbient_LightType == fLights->light(i).type() ||
3157 fLights->light(i).getShadowMap() != nullptr) {
3158 continue;
3159 }
3160
3161 // TODO: compute the correct size of the depth map from the light properties
3162 // TODO: maybe add a kDepth_8_SkColorType
3163 // TODO: find actual max depth of picture
3164 SkISize shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3165 fLights->light(i), 255,
3166 picture->cullRect().width(),
3167 picture->cullRect().height());
3168
3169 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3170 kBGRA_8888_SkColorType,
3171 kOpaque_SkAlphaType);
3172
3173 // Create a new surface (that matches the backend of canvas)
3174 // for each shadow map
3175 sk_sp<SkSurface> surf(this->makeSurface(info));
3176
3177 // Wrap another SPFCanvas around the surface
3178 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3179 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3180
3181 // set the depth map canvas to have the light we're drawing.
3182 SkLights::Builder builder;
3183 builder.add(fLights->light(i));
3184 sk_sp<SkLights> curLight = builder.finish();
3185
3186 depthMapCanvas->setLights(std::move(curLight));
3187 depthMapCanvas->drawPicture(picture);
3188
3189 fLights->light(i).setShadowMap(surf->makeImageSnapshot());
3190 }
3191
3192 sk_sp<SkImage> povDepthMap;
3193 sk_sp<SkImage> diffuseMap;
3194
3195 // TODO: pass the depth to the shader in vertices, or uniforms
3196 // so we don't have to render depth and color separately
3197
3198 // povDepthMap
3199 {
3200 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003201 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3202 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003203 sk_sp<SkLights> povLight = builder.finish();
3204
3205 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3206 picture->cullRect().height(),
3207 kBGRA_8888_SkColorType,
3208 kOpaque_SkAlphaType);
3209
3210 // Create a new surface (that matches the backend of canvas)
3211 // to create the povDepthMap
3212 sk_sp<SkSurface> surf(this->makeSurface(info));
3213
3214 // Wrap another SPFCanvas around the surface
3215 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3216 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3217
3218 // set the depth map canvas to have the light as the user's POV
3219 depthMapCanvas->setLights(std::move(povLight));
3220
3221 depthMapCanvas->drawPicture(picture);
3222
3223 povDepthMap = surf->makeImageSnapshot();
3224 }
3225
3226 // diffuseMap
3227 {
3228 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3229 picture->cullRect().height(),
3230 kBGRA_8888_SkColorType,
3231 kOpaque_SkAlphaType);
3232
3233 sk_sp<SkSurface> surf(this->makeSurface(info));
3234 surf->getCanvas()->drawPicture(picture);
3235
3236 diffuseMap = surf->makeImageSnapshot();
3237 }
3238
3239 SkPaint shadowPaint;
3240
3241 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3242 SkShader::kClamp_TileMode);
3243
3244 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3245 SkShader::kClamp_TileMode);
3246
3247 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3248 std::move(diffuseShader),
3249 std::move(fLights),
3250 diffuseMap->width(),
3251 diffuseMap->height());
3252
3253 shadowPaint.setShader(shadowShader);
3254
3255 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003256}
3257#endif
3258
reed@android.com8a1c16f2008-12-17 15:59:43 +00003259///////////////////////////////////////////////////////////////////////////////
3260///////////////////////////////////////////////////////////////////////////////
3261
3262SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003263 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003264
3265 SkASSERT(canvas);
3266
3267 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3268 fDone = !fImpl->next();
3269}
3270
3271SkCanvas::LayerIter::~LayerIter() {
3272 fImpl->~SkDrawIter();
3273}
3274
3275void SkCanvas::LayerIter::next() {
3276 fDone = !fImpl->next();
3277}
3278
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003279SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003280 return fImpl->getDevice();
3281}
3282
3283const SkMatrix& SkCanvas::LayerIter::matrix() const {
3284 return fImpl->getMatrix();
3285}
3286
3287const SkPaint& SkCanvas::LayerIter::paint() const {
3288 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003289 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003290 paint = &fDefaultPaint;
3291 }
3292 return *paint;
3293}
3294
reed1e7f5e72016-04-27 07:49:17 -07003295const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003296int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3297int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003298
3299///////////////////////////////////////////////////////////////////////////////
3300
fmalitac3b589a2014-06-05 12:40:07 -07003301SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003302
3303///////////////////////////////////////////////////////////////////////////////
3304
3305static bool supported_for_raster_canvas(const SkImageInfo& info) {
3306 switch (info.alphaType()) {
3307 case kPremul_SkAlphaType:
3308 case kOpaque_SkAlphaType:
3309 break;
3310 default:
3311 return false;
3312 }
3313
3314 switch (info.colorType()) {
3315 case kAlpha_8_SkColorType:
3316 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003317 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003318 break;
3319 default:
3320 return false;
3321 }
3322
3323 return true;
3324}
3325
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003326SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3327 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003328 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003329 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003330
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003331 SkBitmap bitmap;
3332 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003333 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003334 }
halcanary385fe4d2015-08-26 13:07:48 -07003335 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003336}
reedd5fa1a42014-08-09 11:08:05 -07003337
3338///////////////////////////////////////////////////////////////////////////////
3339
3340SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003341 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003342 : fCanvas(canvas)
3343 , fSaveCount(canvas->getSaveCount())
3344{
bsalomon49f085d2014-09-05 13:34:00 -07003345 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003346 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003347 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003348 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003349 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003350 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003351 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003352 canvas->save();
3353 }
mtklein6cfa73a2014-08-13 13:33:49 -07003354
bsalomon49f085d2014-09-05 13:34:00 -07003355 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003356 canvas->concat(*matrix);
3357 }
3358}
3359
3360SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3361 fCanvas->restoreToCount(fSaveCount);
3362}
reede8f30622016-03-23 18:59:25 -07003363
3364#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3365SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3366 return this->makeSurface(info, props).release();
3367}
3368#endif