blob: 539fd36028ac214dad18d49df808b34b6a187ea9 [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
msarett16882062016-08-16 09:31:08 -07002060void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2061 const SkPaint* paint) {
2062 RETURN_ON_NULL(image);
2063 if (dst.isEmpty()) {
2064 return;
2065 }
2066 if (SkLatticeIter::Valid(image->width(), image->height(), lattice)) {
2067 this->onDrawImageLattice(image, lattice, dst, paint);
2068 } else {
2069 this->drawImageRect(image, dst, paint);
2070 }
2071}
2072
reed41af9662015-01-05 07:49:08 -08002073void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002074 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002075 return;
2076 }
reed41af9662015-01-05 07:49:08 -08002077 this->onDrawBitmap(bitmap, dx, dy, paint);
2078}
2079
reede47829b2015-08-06 10:02:53 -07002080void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07002081 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002082 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07002083 return;
2084 }
reede47829b2015-08-06 10:02:53 -07002085 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08002086}
2087
reed84984ef2015-07-17 07:09:43 -07002088void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
2089 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07002090 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07002091}
2092
reede47829b2015-08-06 10:02:53 -07002093void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
2094 SrcRectConstraint constraint) {
2095 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
2096 constraint);
2097}
reede47829b2015-08-06 10:02:53 -07002098
reed41af9662015-01-05 07:49:08 -08002099void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2100 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07002101 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07002102 return;
2103 }
msarett552bca92016-08-03 06:53:26 -07002104 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
2105 this->onDrawBitmapNine(bitmap, center, dst, paint);
2106 } else {
reeda5517e22015-07-14 10:54:12 -07002107 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07002108 }
reed41af9662015-01-05 07:49:08 -08002109}
2110
msarettc573a402016-08-02 08:05:56 -07002111void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
2112 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07002113 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07002114 return;
2115 }
msarett16882062016-08-16 09:31:08 -07002116 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), lattice)) {
2117 this->onDrawBitmapLattice(bitmap, lattice, dst, paint);
msarett552bca92016-08-03 06:53:26 -07002118 } else {
msarett16882062016-08-16 09:31:08 -07002119 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002120 }
msarettc573a402016-08-02 08:05:56 -07002121}
2122
reed71c3c762015-06-24 10:29:17 -07002123void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2124 const SkColor colors[], int count, SkXfermode::Mode mode,
2125 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002126 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002127 if (count <= 0) {
2128 return;
2129 }
2130 SkASSERT(atlas);
2131 SkASSERT(xform);
2132 SkASSERT(tex);
2133 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2134}
2135
reedf70b5312016-03-04 16:36:20 -08002136void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2137 if (key) {
2138 this->onDrawAnnotation(rect, key, value);
2139 }
2140}
2141
reede47829b2015-08-06 10:02:53 -07002142void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2143 const SkPaint* paint, SrcRectConstraint constraint) {
2144 if (src) {
2145 this->drawImageRect(image, *src, dst, paint, constraint);
2146 } else {
2147 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2148 dst, paint, constraint);
2149 }
2150}
2151void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2152 const SkPaint* paint, SrcRectConstraint constraint) {
2153 if (src) {
2154 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2155 } else {
2156 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2157 dst, paint, constraint);
2158 }
2159}
2160
tomhudsoncb3bd182016-05-18 07:24:16 -07002161void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2162 SkIRect layer_bounds = this->getTopLayerBounds();
2163 if (matrix) {
2164 *matrix = this->getTotalMatrix();
2165 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2166 }
2167 if (clip_bounds) {
2168 this->getClipDeviceBounds(clip_bounds);
2169 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2170 }
2171}
2172
reed@android.com8a1c16f2008-12-17 15:59:43 +00002173//////////////////////////////////////////////////////////////////////////////
2174// These are the virtual drawing methods
2175//////////////////////////////////////////////////////////////////////////////
2176
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002177void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002178 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002179 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2180 }
2181}
2182
reed41af9662015-01-05 07:49:08 -08002183void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002184 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002185 this->internalDrawPaint(paint);
2186}
2187
2188void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002189 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002190
2191 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002192 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002193 }
2194
reed@google.com4e2b3d32011-04-07 14:18:59 +00002195 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002196}
2197
reed41af9662015-01-05 07:49:08 -08002198void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2199 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002200 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002201 if ((long)count <= 0) {
2202 return;
2203 }
2204
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002205 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002206 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002207 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002208 // special-case 2 points (common for drawing a single line)
2209 if (2 == count) {
2210 r.set(pts[0], pts[1]);
2211 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002212 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002213 }
senorblanco87e066e2015-10-28 11:23:36 -07002214 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2215 return;
2216 }
2217 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002218 }
reed@google.coma584aed2012-05-16 14:06:02 +00002219
halcanary96fcdcc2015-08-27 07:41:13 -07002220 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002221
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002222 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002223
reed@android.com8a1c16f2008-12-17 15:59:43 +00002224 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002225 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002226 }
reed@google.com4b226022011-01-11 18:32:13 +00002227
reed@google.com4e2b3d32011-04-07 14:18:59 +00002228 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002229}
2230
reed41af9662015-01-05 07:49:08 -08002231void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002232 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002233 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002234 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002235 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002236 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2237 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2238 SkRect tmp(r);
2239 tmp.sort();
2240
senorblanco87e066e2015-10-28 11:23:36 -07002241 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2242 return;
2243 }
2244 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002245 }
reed@google.com4b226022011-01-11 18:32:13 +00002246
reedc83a2972015-07-16 07:40:45 -07002247 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002248
2249 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002250 iter.fDevice->drawRect(iter, r, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002251 }
2252
reed@google.com4e2b3d32011-04-07 14:18:59 +00002253 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002254}
2255
reed41af9662015-01-05 07:49:08 -08002256void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002257 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002258 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002259 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002260 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002261 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2262 return;
2263 }
2264 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002265 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002266
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002267 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002268
2269 while (iter.next()) {
2270 iter.fDevice->drawOval(iter, oval, looper.paint());
2271 }
2272
2273 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002274}
2275
reed41af9662015-01-05 07:49:08 -08002276void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002277 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002278 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002279 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002280 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002281 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2282 return;
2283 }
2284 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002285 }
2286
2287 if (rrect.isRect()) {
2288 // call the non-virtual version
2289 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002290 return;
2291 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002292 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002293 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2294 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002295 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002296
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002297 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002298
2299 while (iter.next()) {
2300 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2301 }
2302
2303 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002304}
2305
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002306void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2307 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002308 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002309 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002310 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002311 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2312 return;
2313 }
2314 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002315 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002316
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002317 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002318
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002319 while (iter.next()) {
2320 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2321 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002322
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002323 LOOPER_END
2324}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002325
reed41af9662015-01-05 07:49:08 -08002326void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002327 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002328 if (!path.isFinite()) {
2329 return;
2330 }
2331
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002332 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002333 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002334 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002335 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002336 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2337 return;
2338 }
2339 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002340 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002341
2342 const SkRect& r = path.getBounds();
2343 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002344 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002345 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002346 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002347 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002348 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002349
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002350 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002351
2352 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002353 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002354 }
2355
reed@google.com4e2b3d32011-04-07 14:18:59 +00002356 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002357}
2358
reed262a71b2015-12-05 13:07:27 -08002359bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002360 if (!paint.getImageFilter()) {
2361 return false;
2362 }
2363
2364 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002365 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002366 return false;
2367 }
2368
2369 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2370 // Once we can filter and the filter will return a result larger than itself, we should be
2371 // able to remove this constraint.
2372 // skbug.com/4526
2373 //
2374 SkPoint pt;
2375 ctm.mapXY(x, y, &pt);
2376 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2377 return ir.contains(fMCRec->fRasterClip.getBounds());
2378}
2379
reeda85d4d02015-05-06 12:56:48 -07002380void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002381 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002382 SkRect bounds = SkRect::MakeXYWH(x, y,
2383 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002384 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002385 SkRect tmp = bounds;
2386 if (paint) {
2387 paint->computeFastBounds(tmp, &tmp);
2388 }
2389 if (this->quickReject(tmp)) {
2390 return;
2391 }
reeda85d4d02015-05-06 12:56:48 -07002392 }
halcanary9d524f22016-03-29 09:03:52 -07002393
reeda85d4d02015-05-06 12:56:48 -07002394 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002395 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002396 paint = lazy.init();
2397 }
reed262a71b2015-12-05 13:07:27 -08002398
reeda2217ef2016-07-20 06:04:34 -07002399 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002400 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2401 *paint);
2402 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002403 special = this->getDevice()->makeSpecial(image);
2404 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002405 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002406 }
2407 }
2408
reed262a71b2015-12-05 13:07:27 -08002409 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2410
reeda85d4d02015-05-06 12:56:48 -07002411 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002412 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002413 if (special) {
2414 SkPoint pt;
2415 iter.fMatrix->mapXY(x, y, &pt);
2416 iter.fDevice->drawSpecial(iter, special.get(),
2417 SkScalarRoundToInt(pt.fX),
2418 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002419 } else {
2420 iter.fDevice->drawImage(iter, image, x, y, pnt);
2421 }
reeda85d4d02015-05-06 12:56:48 -07002422 }
halcanary9d524f22016-03-29 09:03:52 -07002423
reeda85d4d02015-05-06 12:56:48 -07002424 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002425}
2426
reed41af9662015-01-05 07:49:08 -08002427void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002428 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002429 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002430 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002431 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002432 if (paint) {
2433 paint->computeFastBounds(dst, &storage);
2434 }
2435 if (this->quickReject(storage)) {
2436 return;
2437 }
reeda85d4d02015-05-06 12:56:48 -07002438 }
2439 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002440 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002441 paint = lazy.init();
2442 }
halcanary9d524f22016-03-29 09:03:52 -07002443
senorblancoc41e7e12015-12-07 12:51:30 -08002444 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002445 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002446
reeda85d4d02015-05-06 12:56:48 -07002447 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002448 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002449 }
halcanary9d524f22016-03-29 09:03:52 -07002450
reeda85d4d02015-05-06 12:56:48 -07002451 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002452}
2453
reed41af9662015-01-05 07:49:08 -08002454void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002455 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002456 SkDEBUGCODE(bitmap.validate();)
2457
reed33366972015-10-08 09:22:02 -07002458 if (bitmap.drawsNothing()) {
2459 return;
2460 }
2461
2462 SkLazyPaint lazy;
2463 if (nullptr == paint) {
2464 paint = lazy.init();
2465 }
2466
2467 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2468
2469 SkRect storage;
2470 const SkRect* bounds = nullptr;
2471 if (paint->canComputeFastBounds()) {
2472 bitmap.getBounds(&storage);
2473 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002474 SkRect tmp = storage;
2475 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2476 return;
2477 }
2478 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002479 }
reed@google.com4b226022011-01-11 18:32:13 +00002480
reeda2217ef2016-07-20 06:04:34 -07002481 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002482 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2483 *paint);
2484 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002485 special = this->getDevice()->makeSpecial(bitmap);
2486 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002487 drawAsSprite = false;
2488 }
2489 }
2490
reed262a71b2015-12-05 13:07:27 -08002491 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002492
2493 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002494 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002495 if (special) {
reed262a71b2015-12-05 13:07:27 -08002496 SkPoint pt;
2497 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002498 iter.fDevice->drawSpecial(iter, special.get(),
2499 SkScalarRoundToInt(pt.fX),
2500 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002501 } else {
2502 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2503 }
reed33366972015-10-08 09:22:02 -07002504 }
msarettfbfa2582016-08-12 08:29:08 -07002505
reed33366972015-10-08 09:22:02 -07002506 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002507}
2508
reed@google.com9987ec32011-09-07 11:57:52 +00002509// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002510void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002511 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002512 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002513 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002514 return;
2515 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002516
halcanary96fcdcc2015-08-27 07:41:13 -07002517 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002518 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002519 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2520 return;
2521 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002522 }
reed@google.com3d608122011-11-21 15:16:16 +00002523
reed@google.com33535f32012-09-25 15:37:50 +00002524 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002525 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002526 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002527 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002528
senorblancoc41e7e12015-12-07 12:51:30 -08002529 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002530 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002531
reed@google.com33535f32012-09-25 15:37:50 +00002532 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002533 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002534 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002535
reed@google.com33535f32012-09-25 15:37:50 +00002536 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002537}
2538
reed41af9662015-01-05 07:49:08 -08002539void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002540 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002541 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002542 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002543 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002544}
2545
reed4c21dc52015-06-25 12:32:03 -07002546void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2547 const SkPaint* paint) {
2548 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002549
halcanary96fcdcc2015-08-27 07:41:13 -07002550 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002551 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002552 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2553 return;
2554 }
reed@google.com3d608122011-11-21 15:16:16 +00002555 }
halcanary9d524f22016-03-29 09:03:52 -07002556
reed4c21dc52015-06-25 12:32:03 -07002557 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002558 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002559 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002560 }
halcanary9d524f22016-03-29 09:03:52 -07002561
senorblancoc41e7e12015-12-07 12:51:30 -08002562 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002563
reed4c21dc52015-06-25 12:32:03 -07002564 while (iter.next()) {
2565 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002566 }
halcanary9d524f22016-03-29 09:03:52 -07002567
reed4c21dc52015-06-25 12:32:03 -07002568 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002569}
2570
reed41af9662015-01-05 07:49:08 -08002571void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2572 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002573 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002574 SkDEBUGCODE(bitmap.validate();)
2575
halcanary96fcdcc2015-08-27 07:41:13 -07002576 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002577 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002578 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2579 return;
2580 }
reed4c21dc52015-06-25 12:32:03 -07002581 }
halcanary9d524f22016-03-29 09:03:52 -07002582
reed4c21dc52015-06-25 12:32:03 -07002583 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002584 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002585 paint = lazy.init();
2586 }
halcanary9d524f22016-03-29 09:03:52 -07002587
senorblancoc41e7e12015-12-07 12:51:30 -08002588 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002589
reed4c21dc52015-06-25 12:32:03 -07002590 while (iter.next()) {
2591 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2592 }
halcanary9d524f22016-03-29 09:03:52 -07002593
reed4c21dc52015-06-25 12:32:03 -07002594 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002595}
2596
msarett16882062016-08-16 09:31:08 -07002597void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2598 const SkPaint* paint) {
2599 if (nullptr == paint || paint->canComputeFastBounds()) {
2600 SkRect storage;
2601 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2602 return;
2603 }
2604 }
2605
2606 SkLazyPaint lazy;
2607 if (nullptr == paint) {
2608 paint = lazy.init();
2609 }
2610
2611 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2612
2613 while (iter.next()) {
2614 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2615 }
2616
2617 LOOPER_END
2618}
2619
2620void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2621 const SkRect& dst, const SkPaint* paint) {
2622 if (nullptr == paint || paint->canComputeFastBounds()) {
2623 SkRect storage;
2624 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2625 return;
2626 }
2627 }
2628
2629 SkLazyPaint lazy;
2630 if (nullptr == paint) {
2631 paint = lazy.init();
2632 }
2633
2634 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2635
2636 while (iter.next()) {
2637 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2638 }
2639
2640 LOOPER_END
2641}
2642
reed@google.comf67e4cf2011-03-15 20:56:58 +00002643class SkDeviceFilteredPaint {
2644public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002645 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002646 uint32_t filteredFlags = device->filterTextFlags(paint);
2647 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002648 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002649 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002650 fPaint = newPaint;
2651 } else {
2652 fPaint = &paint;
2653 }
2654 }
2655
reed@google.comf67e4cf2011-03-15 20:56:58 +00002656 const SkPaint& paint() const { return *fPaint; }
2657
2658private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002659 const SkPaint* fPaint;
2660 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002661};
2662
bungeman@google.com52c748b2011-08-22 21:30:43 +00002663void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2664 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002665 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002666 draw.fDevice->drawRect(draw, r, paint);
2667 } else {
2668 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002669 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002670 draw.fDevice->drawRect(draw, r, p);
2671 }
2672}
2673
2674void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2675 const char text[], size_t byteLength,
2676 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002677 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002678
2679 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002680 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002681 draw.fRC->isEmpty() ||
halcanary96fcdcc2015-08-27 07:41:13 -07002682 (paint.getAlpha() == 0 && paint.getXfermode() == nullptr)) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002683 return;
2684 }
2685
2686 SkScalar width = 0;
2687 SkPoint start;
2688
2689 start.set(0, 0); // to avoid warning
2690 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2691 SkPaint::kStrikeThruText_Flag)) {
2692 width = paint.measureText(text, byteLength);
2693
2694 SkScalar offsetX = 0;
2695 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2696 offsetX = SkScalarHalf(width);
2697 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2698 offsetX = width;
2699 }
2700 start.set(x - offsetX, y);
2701 }
2702
2703 if (0 == width) {
2704 return;
2705 }
2706
2707 uint32_t flags = paint.getFlags();
2708
2709 if (flags & (SkPaint::kUnderlineText_Flag |
2710 SkPaint::kStrikeThruText_Flag)) {
2711 SkScalar textSize = paint.getTextSize();
2712 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2713 SkRect r;
2714
2715 r.fLeft = start.fX;
2716 r.fRight = start.fX + width;
2717
2718 if (flags & SkPaint::kUnderlineText_Flag) {
2719 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2720 start.fY);
2721 r.fTop = offset;
2722 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002723 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002724 }
2725 if (flags & SkPaint::kStrikeThruText_Flag) {
2726 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2727 start.fY);
2728 r.fTop = offset;
2729 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002730 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002731 }
2732 }
2733}
2734
reed@google.come0d9ce82014-04-23 04:00:17 +00002735void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2736 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002737 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002738
2739 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002740 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002741 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002742 DrawTextDecorations(iter, dfp.paint(),
2743 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002744 }
2745
reed@google.com4e2b3d32011-04-07 14:18:59 +00002746 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002747}
2748
reed@google.come0d9ce82014-04-23 04:00:17 +00002749void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2750 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002751 SkPoint textOffset = SkPoint::Make(0, 0);
2752
halcanary96fcdcc2015-08-27 07:41:13 -07002753 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002754
reed@android.com8a1c16f2008-12-17 15:59:43 +00002755 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002756 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002757 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002758 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002759 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002760
reed@google.com4e2b3d32011-04-07 14:18:59 +00002761 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002762}
2763
reed@google.come0d9ce82014-04-23 04:00:17 +00002764void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2765 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002766
2767 SkPoint textOffset = SkPoint::Make(0, constY);
2768
halcanary96fcdcc2015-08-27 07:41:13 -07002769 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002770
reed@android.com8a1c16f2008-12-17 15:59:43 +00002771 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002772 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002773 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002774 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002775 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002776
reed@google.com4e2b3d32011-04-07 14:18:59 +00002777 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002778}
2779
reed@google.come0d9ce82014-04-23 04:00:17 +00002780void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2781 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002782 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002783
reed@android.com8a1c16f2008-12-17 15:59:43 +00002784 while (iter.next()) {
2785 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002786 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002787 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002788
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002789 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002790}
2791
reed45561a02016-07-07 12:47:17 -07002792void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2793 const SkRect* cullRect, const SkPaint& paint) {
2794 if (cullRect && this->quickReject(*cullRect)) {
2795 return;
2796 }
2797
2798 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2799
2800 while (iter.next()) {
2801 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2802 }
2803
2804 LOOPER_END
2805}
2806
fmalita00d5c2c2014-08-21 08:53:26 -07002807void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2808 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002809
fmalita85d5eb92015-03-04 11:20:12 -08002810 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002811 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002812 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002813 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002814 SkRect tmp;
2815 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2816 return;
2817 }
2818 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002819 }
2820
fmalita024f9962015-03-03 19:08:17 -08002821 // We cannot filter in the looper as we normally do, because the paint is
2822 // incomplete at this point (text-related attributes are embedded within blob run paints).
2823 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002824 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002825
fmalita85d5eb92015-03-04 11:20:12 -08002826 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002827
fmalitaaa1b9122014-08-28 14:32:24 -07002828 while (iter.next()) {
2829 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002830 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002831 }
2832
fmalitaaa1b9122014-08-28 14:32:24 -07002833 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002834
2835 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002836}
2837
reed@google.come0d9ce82014-04-23 04:00:17 +00002838// These will become non-virtual, so they always call the (virtual) onDraw... method
2839void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2840 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002841 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002842 if (byteLength) {
2843 this->onDrawText(text, byteLength, x, y, paint);
2844 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002845}
2846void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2847 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002848 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002849 if (byteLength) {
2850 this->onDrawPosText(text, byteLength, pos, paint);
2851 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002852}
2853void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2854 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002855 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002856 if (byteLength) {
2857 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2858 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002859}
2860void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2861 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002862 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002863 if (byteLength) {
2864 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2865 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002866}
reed45561a02016-07-07 12:47:17 -07002867void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2868 const SkRect* cullRect, const SkPaint& paint) {
2869 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2870 if (byteLength) {
2871 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2872 }
2873}
fmalita00d5c2c2014-08-21 08:53:26 -07002874void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2875 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002876 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002877 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002878 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002879}
reed@google.come0d9ce82014-04-23 04:00:17 +00002880
reed41af9662015-01-05 07:49:08 -08002881void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2882 const SkPoint verts[], const SkPoint texs[],
2883 const SkColor colors[], SkXfermode* xmode,
2884 const uint16_t indices[], int indexCount,
2885 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002886 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002887 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002888
reed@android.com8a1c16f2008-12-17 15:59:43 +00002889 while (iter.next()) {
2890 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002891 colors, xmode, indices, indexCount,
2892 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002893 }
reed@google.com4b226022011-01-11 18:32:13 +00002894
reed@google.com4e2b3d32011-04-07 14:18:59 +00002895 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002896}
2897
dandovb3c9d1c2014-08-12 08:34:29 -07002898void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2899 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002900 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002901 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002902 return;
2903 }
mtklein6cfa73a2014-08-13 13:33:49 -07002904
dandovecfff212014-08-04 10:02:00 -07002905 // Since a patch is always within the convex hull of the control points, we discard it when its
2906 // bounding rectangle is completely outside the current clip.
2907 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002908 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002909 if (this->quickReject(bounds)) {
2910 return;
2911 }
mtklein6cfa73a2014-08-13 13:33:49 -07002912
dandovb3c9d1c2014-08-12 08:34:29 -07002913 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2914}
2915
2916void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2917 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
2918
halcanary96fcdcc2015-08-27 07:41:13 -07002919 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002920
dandovecfff212014-08-04 10:02:00 -07002921 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002922 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002923 }
mtklein6cfa73a2014-08-13 13:33:49 -07002924
dandovecfff212014-08-04 10:02:00 -07002925 LOOPER_END
2926}
2927
reeda8db7282015-07-07 10:22:31 -07002928void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002929 RETURN_ON_NULL(dr);
2930 if (x || y) {
2931 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2932 this->onDrawDrawable(dr, &matrix);
2933 } else {
2934 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002935 }
2936}
2937
reeda8db7282015-07-07 10:22:31 -07002938void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002939 RETURN_ON_NULL(dr);
2940 if (matrix && matrix->isIdentity()) {
2941 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002942 }
reede3b38ce2016-01-08 09:18:44 -08002943 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002944}
2945
2946void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
2947 SkRect bounds = dr->getBounds();
2948 if (matrix) {
2949 matrix->mapRect(&bounds);
2950 }
2951 if (this->quickReject(bounds)) {
2952 return;
2953 }
2954 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002955}
2956
reed71c3c762015-06-24 10:29:17 -07002957void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2958 const SkColor colors[], int count, SkXfermode::Mode mode,
2959 const SkRect* cull, const SkPaint* paint) {
2960 if (cull && this->quickReject(*cull)) {
2961 return;
2962 }
2963
2964 SkPaint pnt;
2965 if (paint) {
2966 pnt = *paint;
2967 }
halcanary9d524f22016-03-29 09:03:52 -07002968
halcanary96fcdcc2015-08-27 07:41:13 -07002969 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002970 while (iter.next()) {
2971 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2972 }
2973 LOOPER_END
2974}
2975
reedf70b5312016-03-04 16:36:20 -08002976void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2977 SkASSERT(key);
2978
2979 SkPaint paint;
2980 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2981 while (iter.next()) {
2982 iter.fDevice->drawAnnotation(iter, rect, key, value);
2983 }
2984 LOOPER_END
2985}
2986
reed@android.com8a1c16f2008-12-17 15:59:43 +00002987//////////////////////////////////////////////////////////////////////////////
2988// These methods are NOT virtual, and therefore must call back into virtual
2989// methods, rather than actually drawing themselves.
2990//////////////////////////////////////////////////////////////////////////////
2991
2992void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
reed@android.com845fdac2009-06-23 03:01:32 +00002993 SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08002994 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002995 SkPaint paint;
2996
2997 paint.setARGB(a, r, g, b);
reed@android.com845fdac2009-06-23 03:01:32 +00002998 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00002999 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003000 }
3001 this->drawPaint(paint);
3002}
3003
reed@android.com845fdac2009-06-23 03:01:32 +00003004void SkCanvas::drawColor(SkColor c, SkXfermode::Mode mode) {
danakj9881d632014-11-26 12:41:06 -08003005 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003006 SkPaint paint;
3007
3008 paint.setColor(c);
reed@android.com845fdac2009-06-23 03:01:32 +00003009 if (SkXfermode::kSrcOver_Mode != mode) {
reed@android.com0baf1932009-06-24 12:41:42 +00003010 paint.setXfermodeMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003011 }
3012 this->drawPaint(paint);
3013}
3014
3015void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003016 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003017 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00003018
reed@android.com8a1c16f2008-12-17 15:59:43 +00003019 pt.set(x, y);
3020 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
3021}
3022
3023void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08003024 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003025 SkPoint pt;
3026 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00003027
reed@android.com8a1c16f2008-12-17 15:59:43 +00003028 pt.set(x, y);
3029 paint.setColor(color);
3030 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
3031}
3032
3033void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
3034 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003035 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003036 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00003037
reed@android.com8a1c16f2008-12-17 15:59:43 +00003038 pts[0].set(x0, y0);
3039 pts[1].set(x1, y1);
3040 this->drawPoints(kLines_PointMode, 2, pts, paint);
3041}
3042
3043void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
3044 SkScalar right, SkScalar bottom,
3045 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003046 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003047 SkRect r;
3048
3049 r.set(left, top, right, bottom);
3050 this->drawRect(r, paint);
3051}
3052
3053void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
3054 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003055 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003056 if (radius < 0) {
3057 radius = 0;
3058 }
3059
3060 SkRect r;
3061 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003062 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003063}
3064
3065void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3066 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003067 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003068 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003069 SkRRect rrect;
3070 rrect.setRectXY(r, rx, ry);
3071 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003072 } else {
3073 this->drawRect(r, paint);
3074 }
3075}
3076
reed@android.com8a1c16f2008-12-17 15:59:43 +00003077void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3078 SkScalar sweepAngle, bool useCenter,
3079 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003080 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003081 if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
3082 this->drawOval(oval, paint);
3083 } else {
3084 SkPath path;
3085 if (useCenter) {
3086 path.moveTo(oval.centerX(), oval.centerY());
3087 }
3088 path.arcTo(oval, startAngle, sweepAngle, !useCenter);
3089 if (useCenter) {
3090 path.close();
3091 }
3092 this->drawPath(path, paint);
3093 }
3094}
3095
3096void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3097 const SkPath& path, SkScalar hOffset,
3098 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003099 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003100 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003101
reed@android.com8a1c16f2008-12-17 15:59:43 +00003102 matrix.setTranslate(hOffset, vOffset);
3103 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3104}
3105
reed@android.comf76bacf2009-05-13 14:00:33 +00003106///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003107
3108/**
3109 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3110 * against the playback cost of recursing into the subpicture to get at its actual ops.
3111 *
3112 * For now we pick a conservatively small value, though measurement (and other heuristics like
3113 * the type of ops contained) may justify changing this value.
3114 */
3115#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003116
reedd5fa1a42014-08-09 11:08:05 -07003117void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003118 RETURN_ON_NULL(picture);
3119
reed1c2c4412015-04-30 13:09:24 -07003120 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003121 if (matrix && matrix->isIdentity()) {
3122 matrix = nullptr;
3123 }
3124 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3125 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3126 picture->playback(this);
3127 } else {
3128 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003129 }
3130}
robertphillips9b14f262014-06-04 05:40:44 -07003131
reedd5fa1a42014-08-09 11:08:05 -07003132void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3133 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003134 if (!paint || paint->canComputeFastBounds()) {
3135 SkRect bounds = picture->cullRect();
3136 if (paint) {
3137 paint->computeFastBounds(bounds, &bounds);
3138 }
3139 if (matrix) {
3140 matrix->mapRect(&bounds);
3141 }
3142 if (this->quickReject(bounds)) {
3143 return;
3144 }
3145 }
3146
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003147 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003148 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003149}
3150
vjiaoblack95302da2016-07-21 10:25:54 -07003151#ifdef SK_EXPERIMENTAL_SHADOWING
3152void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3153 const SkMatrix* matrix,
3154 const SkPaint* paint) {
3155 RETURN_ON_NULL(picture);
3156
3157 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3158
3159 this->onDrawShadowedPicture(picture, matrix, paint);
3160}
3161
3162void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3163 const SkMatrix* matrix,
3164 const SkPaint* paint) {
vjiaoblack904527d2016-08-09 09:32:09 -07003165 if (!paint || paint->canComputeFastBounds()) {
3166 SkRect bounds = picture->cullRect();
3167 if (paint) {
3168 paint->computeFastBounds(bounds, &bounds);
3169 }
3170 if (matrix) {
3171 matrix->mapRect(&bounds);
3172 }
3173 if (this->quickReject(bounds)) {
3174 return;
3175 }
3176 }
3177
3178 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3179
3180 for (int i = 0; i < fLights->numLights(); ++i) {
3181 // skip over ambient lights; they don't cast shadows
3182 // lights that have shadow maps do not need updating (because lights are immutable)
3183
3184 if (SkLights::Light::kAmbient_LightType == fLights->light(i).type() ||
3185 fLights->light(i).getShadowMap() != nullptr) {
3186 continue;
3187 }
3188
3189 // TODO: compute the correct size of the depth map from the light properties
3190 // TODO: maybe add a kDepth_8_SkColorType
3191 // TODO: find actual max depth of picture
3192 SkISize shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3193 fLights->light(i), 255,
3194 picture->cullRect().width(),
3195 picture->cullRect().height());
3196
3197 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3198 kBGRA_8888_SkColorType,
3199 kOpaque_SkAlphaType);
3200
3201 // Create a new surface (that matches the backend of canvas)
3202 // for each shadow map
3203 sk_sp<SkSurface> surf(this->makeSurface(info));
3204
3205 // Wrap another SPFCanvas around the surface
3206 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3207 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3208
3209 // set the depth map canvas to have the light we're drawing.
3210 SkLights::Builder builder;
3211 builder.add(fLights->light(i));
3212 sk_sp<SkLights> curLight = builder.finish();
3213
3214 depthMapCanvas->setLights(std::move(curLight));
3215 depthMapCanvas->drawPicture(picture);
3216
3217 fLights->light(i).setShadowMap(surf->makeImageSnapshot());
3218 }
3219
3220 sk_sp<SkImage> povDepthMap;
3221 sk_sp<SkImage> diffuseMap;
3222
3223 // TODO: pass the depth to the shader in vertices, or uniforms
3224 // so we don't have to render depth and color separately
3225
3226 // povDepthMap
3227 {
3228 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003229 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3230 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003231 sk_sp<SkLights> povLight = builder.finish();
3232
3233 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3234 picture->cullRect().height(),
3235 kBGRA_8888_SkColorType,
3236 kOpaque_SkAlphaType);
3237
3238 // Create a new surface (that matches the backend of canvas)
3239 // to create the povDepthMap
3240 sk_sp<SkSurface> surf(this->makeSurface(info));
3241
3242 // Wrap another SPFCanvas around the surface
3243 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3244 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3245
3246 // set the depth map canvas to have the light as the user's POV
3247 depthMapCanvas->setLights(std::move(povLight));
3248
3249 depthMapCanvas->drawPicture(picture);
3250
3251 povDepthMap = surf->makeImageSnapshot();
3252 }
3253
3254 // diffuseMap
3255 {
3256 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3257 picture->cullRect().height(),
3258 kBGRA_8888_SkColorType,
3259 kOpaque_SkAlphaType);
3260
3261 sk_sp<SkSurface> surf(this->makeSurface(info));
3262 surf->getCanvas()->drawPicture(picture);
3263
3264 diffuseMap = surf->makeImageSnapshot();
3265 }
3266
3267 SkPaint shadowPaint;
3268
3269 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3270 SkShader::kClamp_TileMode);
3271
3272 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3273 SkShader::kClamp_TileMode);
3274
3275 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3276 std::move(diffuseShader),
3277 std::move(fLights),
3278 diffuseMap->width(),
3279 diffuseMap->height());
3280
3281 shadowPaint.setShader(shadowShader);
3282
3283 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003284}
3285#endif
3286
reed@android.com8a1c16f2008-12-17 15:59:43 +00003287///////////////////////////////////////////////////////////////////////////////
3288///////////////////////////////////////////////////////////////////////////////
3289
3290SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
bungeman99fe8222015-08-20 07:57:51 -07003291 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003292
3293 SkASSERT(canvas);
3294
3295 fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
3296 fDone = !fImpl->next();
3297}
3298
3299SkCanvas::LayerIter::~LayerIter() {
3300 fImpl->~SkDrawIter();
3301}
3302
3303void SkCanvas::LayerIter::next() {
3304 fDone = !fImpl->next();
3305}
3306
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003307SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003308 return fImpl->getDevice();
3309}
3310
3311const SkMatrix& SkCanvas::LayerIter::matrix() const {
3312 return fImpl->getMatrix();
3313}
3314
3315const SkPaint& SkCanvas::LayerIter::paint() const {
3316 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003317 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003318 paint = &fDefaultPaint;
3319 }
3320 return *paint;
3321}
3322
reed1e7f5e72016-04-27 07:49:17 -07003323const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003324int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3325int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003326
3327///////////////////////////////////////////////////////////////////////////////
3328
fmalitac3b589a2014-06-05 12:40:07 -07003329SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003330
3331///////////////////////////////////////////////////////////////////////////////
3332
3333static bool supported_for_raster_canvas(const SkImageInfo& info) {
3334 switch (info.alphaType()) {
3335 case kPremul_SkAlphaType:
3336 case kOpaque_SkAlphaType:
3337 break;
3338 default:
3339 return false;
3340 }
3341
3342 switch (info.colorType()) {
3343 case kAlpha_8_SkColorType:
3344 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003345 case kN32_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003346 break;
3347 default:
3348 return false;
3349 }
3350
3351 return true;
3352}
3353
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003354SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3355 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003356 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003357 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003358
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003359 SkBitmap bitmap;
3360 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003361 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003362 }
halcanary385fe4d2015-08-26 13:07:48 -07003363 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003364}
reedd5fa1a42014-08-09 11:08:05 -07003365
3366///////////////////////////////////////////////////////////////////////////////
3367
3368SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003369 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003370 : fCanvas(canvas)
3371 , fSaveCount(canvas->getSaveCount())
3372{
bsalomon49f085d2014-09-05 13:34:00 -07003373 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003374 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003375 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003376 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003377 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003378 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003379 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003380 canvas->save();
3381 }
mtklein6cfa73a2014-08-13 13:33:49 -07003382
bsalomon49f085d2014-09-05 13:34:00 -07003383 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003384 canvas->concat(*matrix);
3385 }
3386}
3387
3388SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3389 fCanvas->restoreToCount(fSaveCount);
3390}
reede8f30622016-03-23 18:59:25 -07003391
3392#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3393SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3394 return this->makeSurface(info, props).release();
3395}
3396#endif