blob: cd4dcbc2a509349838e5957b76f4535f00083c0c [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00002 * Copyright 2008 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00003 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00004 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00006 */
7
bungemand3ebb482015-08-05 13:57:49 -07008#include "SkBitmapDevice.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkCanvas.h"
reedd5fa1a42014-08-09 11:08:05 -070010#include "SkCanvasPriv.h"
bungemand3ebb482015-08-05 13:57:49 -070011#include "SkClipStack.h"
reeddbc3cef2015-04-29 12:18:57 -070012#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000013#include "SkDraw.h"
reed3cb38402015-02-06 08:36:15 -080014#include "SkDrawable.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000015#include "SkDrawFilter.h"
16#include "SkDrawLooper.h"
piotaixrb5fae932014-09-24 13:03:30 -070017#include "SkImage.h"
reed262a71b2015-12-05 13:07:27 -080018#include "SkImage_Base.h"
senorblanco900c3672016-04-27 11:31:23 -070019#include "SkImageFilter.h"
20#include "SkImageFilterCache.h"
msarettc573a402016-08-02 08:05:56 -070021#include "SkLatticeIter.h"
reed262a71b2015-12-05 13:07:27 -080022#include "SkMatrixUtils.h"
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +000023#include "SkMetaData.h"
msarettfbfa2582016-08-12 08:29:08 -070024#include "SkNx.h"
reedc83a2972015-07-16 07:40:45 -070025#include "SkPaintPriv.h"
dandovb3c9d1c2014-08-12 08:34:29 -070026#include "SkPatchUtils.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000027#include "SkPicture.h"
vjiaoblackb2796fd2016-09-09 09:22:39 -070028#include "SkRadialShadowMapShader.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#include <new>
reed@android.com8a1c16f2008-12-17 15:59:43 +000042
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000043#if SK_SUPPORT_GPU
robertphillips7354a4b2015-12-16 05:08:27 -080044#include "GrContext.h"
commit-bot@chromium.org644629c2013-11-21 06:21:58 +000045#include "GrRenderTarget.h"
bsalomon614d8f92016-07-13 15:42:40 -070046#include "SkGrPriv.h"
vjiaoblacke6f5d562016-08-25 06:30:23 -070047
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
reedc83a2972015-07-16 07:40:45 -070052/*
53 * Return true if the drawing this rect would hit every pixels in the canvas.
54 *
55 * Returns false if
56 * - rect does not contain the canvas' bounds
57 * - paint is not fill
58 * - paint would blur or otherwise change the coverage of the rect
59 */
60bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint,
61 ShaderOverrideOpacity overrideOpacity) const {
bungeman99fe8222015-08-20 07:57:51 -070062 static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity ==
63 (int)kNone_ShaderOverrideOpacity,
64 "need_matching_enums0");
65 static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity ==
66 (int)kOpaque_ShaderOverrideOpacity,
67 "need_matching_enums1");
68 static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity ==
69 (int)kNotOpaque_ShaderOverrideOpacity,
70 "need_matching_enums2");
reedc83a2972015-07-16 07:40:45 -070071
72 const SkISize size = this->getBaseLayerSize();
73 const SkRect bounds = SkRect::MakeIWH(size.width(), size.height());
74 if (!this->getClipStack()->quickContains(bounds)) {
75 return false;
76 }
77
78 if (rect) {
halcanaryc5769b22016-08-10 07:13:21 -070079 if (!this->getTotalMatrix().isScaleTranslate()) {
reedc83a2972015-07-16 07:40:45 -070080 return false; // conservative
81 }
halcanaryc5769b22016-08-10 07:13:21 -070082
83 SkRect devRect;
84 this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect);
85 if (!devRect.contains(bounds)) {
reedc83a2972015-07-16 07:40:45 -070086 return false;
87 }
88 }
89
90 if (paint) {
91 SkPaint::Style paintStyle = paint->getStyle();
92 if (!(paintStyle == SkPaint::kFill_Style ||
93 paintStyle == SkPaint::kStrokeAndFill_Style)) {
94 return false;
95 }
96 if (paint->getMaskFilter() || paint->getLooper()
97 || paint->getPathEffect() || paint->getImageFilter()) {
98 return false; // conservative
99 }
100 }
101 return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity);
102}
103
104///////////////////////////////////////////////////////////////////////////////////////////////////
105
reedd990e2f2014-12-22 11:58:30 -0800106static bool gIgnoreSaveLayerBounds;
107void SkCanvas::Internal_Private_SetIgnoreSaveLayerBounds(bool ignore) {
108 gIgnoreSaveLayerBounds = ignore;
109}
110bool SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds() {
111 return gIgnoreSaveLayerBounds;
112}
113
reed0acf1b42014-12-22 16:12:38 -0800114static bool gTreatSpriteAsBitmap;
115void SkCanvas::Internal_Private_SetTreatSpriteAsBitmap(bool spriteAsBitmap) {
116 gTreatSpriteAsBitmap = spriteAsBitmap;
117}
118bool SkCanvas::Internal_Private_GetTreatSpriteAsBitmap() {
119 return gTreatSpriteAsBitmap;
120}
121
reed@google.comda17f752012-08-16 18:27:05 +0000122// experimental for faster tiled drawing...
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123//#define SK_TRACE_SAVERESTORE
124
125#ifdef SK_TRACE_SAVERESTORE
126 static int gLayerCounter;
127 static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
128 static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
129
130 static int gRecCounter;
131 static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
132 static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
133
134 static int gCanvasCounter;
135 static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
136 static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
137#else
138 #define inc_layer()
139 #define dec_layer()
140 #define inc_rec()
141 #define dec_rec()
142 #define inc_canvas()
143 #define dec_canvas()
144#endif
145
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000146typedef SkTLazy<SkPaint> SkLazyPaint;
147
reedc83a2972015-07-16 07:40:45 -0700148void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) {
reed@google.com97af1a62012-08-28 12:19:02 +0000149 if (fSurfaceBase) {
reedc83a2972015-07-16 07:40:45 -0700150 fSurfaceBase->aboutToDraw(willOverwritesEntireSurface
151 ? SkSurface::kDiscard_ContentChangeMode
152 : SkSurface::kRetain_ContentChangeMode);
153 }
154}
155
156void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint,
157 ShaderOverrideOpacity overrideOpacity) {
158 if (fSurfaceBase) {
159 SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode;
160 // Since willOverwriteAllPixels() may not be complete free to call, we only do so if
161 // there is an outstanding snapshot, since w/o that, there will be no copy-on-write
162 // and therefore we don't care which mode we're in.
163 //
164 if (fSurfaceBase->outstandingImageSnapshot()) {
165 if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) {
166 mode = SkSurface::kDiscard_ContentChangeMode;
167 }
168 }
169 fSurfaceBase->aboutToDraw(mode);
reed@google.com97af1a62012-08-28 12:19:02 +0000170 }
171}
172
reed@android.com8a1c16f2008-12-17 15:59:43 +0000173///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000175/* This is the record we keep for each SkBaseDevice that the user installs.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 The clip/matrix/proc are fields that reflect the top of the save/restore
177 stack. Whenever the canvas changes, it marks a dirty flag, and then before
178 these are used (assuming we're not on a layer) we rebuild these cache
179 values: they reflect the top of the save stack, but translated and clipped
180 by the device's XY offset and bitmap-bounds.
181*/
182struct DeviceCM {
183 DeviceCM* fNext;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000184 SkBaseDevice* fDevice;
reed@google.com045e62d2011-10-24 12:19:46 +0000185 SkRasterClip fClip;
reed@google.com6f8f2922011-03-04 22:27:10 +0000186 SkPaint* fPaint; // may be null (in the future)
reed61f501f2015-04-29 08:34:00 -0700187 const SkMatrix* fMatrix;
188 SkMatrix fMatrixStorage;
reed8c30a812016-04-20 16:36:51 -0700189 SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
reed@android.com8a1c16f2008-12-17 15:59:43 +0000190
reed96e657d2015-03-10 17:30:07 -0700191 DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas,
reed7503d602016-07-15 14:23:29 -0700192 bool conservativeRasterClip, const SkMatrix& stashed)
halcanary96fcdcc2015-08-27 07:41:13 -0700193 : fNext(nullptr)
reedd9544982014-09-09 18:46:22 -0700194 , fClip(conservativeRasterClip)
reed8c30a812016-04-20 16:36:51 -0700195 , fStashedMatrix(stashed)
reedd9544982014-09-09 18:46:22 -0700196 {
reed2c9e2002016-07-25 08:05:22 -0700197 SkSafeRef(device);
reed@google.com4b226022011-01-11 18:32:13 +0000198 fDevice = device;
halcanary96fcdcc2015-08-27 07:41:13 -0700199 fPaint = paint ? new SkPaint(*paint) : nullptr;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000200 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000202 ~DeviceCM() {
reed2c9e2002016-07-25 08:05:22 -0700203 SkSafeUnref(fDevice);
halcanary385fe4d2015-08-26 13:07:48 -0700204 delete fPaint;
bungeman@google.com88edf1e2011-08-08 19:41:56 +0000205 }
reed@google.com4b226022011-01-11 18:32:13 +0000206
mtkleinfeaadee2015-04-08 11:25:48 -0700207 void reset(const SkIRect& bounds) {
208 SkASSERT(!fPaint);
209 SkASSERT(!fNext);
210 SkASSERT(fDevice);
211 fClip.setRect(bounds);
212 }
213
reed@google.com045e62d2011-10-24 12:19:46 +0000214 void updateMC(const SkMatrix& totalMatrix, const SkRasterClip& totalClip,
reedde6c5312016-09-02 12:10:07 -0700215 SkRasterClip* updateClip) {
reed@google.com6f8f2922011-03-04 22:27:10 +0000216 int x = fDevice->getOrigin().x();
217 int y = fDevice->getOrigin().y();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000218 int width = fDevice->width();
219 int height = fDevice->height();
reed@google.com4b226022011-01-11 18:32:13 +0000220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221 if ((x | y) == 0) {
222 fMatrix = &totalMatrix;
223 fClip = totalClip;
224 } else {
225 fMatrixStorage = totalMatrix;
226 fMatrixStorage.postTranslate(SkIntToScalar(-x),
227 SkIntToScalar(-y));
228 fMatrix = &fMatrixStorage;
reed@google.com4b226022011-01-11 18:32:13 +0000229
reed@android.com8a1c16f2008-12-17 15:59:43 +0000230 totalClip.translate(-x, -y, &fClip);
231 }
232
reed@google.com045e62d2011-10-24 12:19:46 +0000233 fClip.op(SkIRect::MakeWH(width, height), SkRegion::kIntersect_Op);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234
235 // intersect clip, but don't translate it (yet)
reed@google.com4b226022011-01-11 18:32:13 +0000236
reed@android.com8a1c16f2008-12-17 15:59:43 +0000237 if (updateClip) {
reed@google.com045e62d2011-10-24 12:19:46 +0000238 updateClip->op(SkIRect::MakeXYWH(x, y, width, height),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 SkRegion::kDifference_Op);
240 }
reed@google.com4b226022011-01-11 18:32:13 +0000241
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242#ifdef SK_DEBUG
243 if (!fClip.isEmpty()) {
244 SkIRect deviceR;
245 deviceR.set(0, 0, width, height);
246 SkASSERT(deviceR.contains(fClip.getBounds()));
247 }
248#endif
reed@android.comf2b98d62010-12-20 18:26:13 +0000249 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000250};
251
252/* This is the record we keep for each save/restore level in the stack.
253 Since a level optionally copies the matrix and/or stack, we have pointers
254 for these fields. If the value is copied for this level, the copy is
255 stored in the ...Storage field, and the pointer points to that. If the
256 value is not copied for this level, we ignore ...Storage, and just point
257 at the corresponding value in the previous level in the stack.
258*/
259class SkCanvas::MCRec {
260public:
reed1f836ee2014-07-07 07:49:34 -0700261 SkDrawFilter* fFilter; // the current filter (or null)
reedd9544982014-09-09 18:46:22 -0700262 DeviceCM* fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 /* If there are any layers in the stack, this points to the top-most
264 one that is at or below this level in the stack (so we know what
265 bitmap/device to draw into from this level. This value is NOT
266 reference counted, since the real owner is either our fLayer field,
267 or a previous one in a lower level.)
268 */
reed2ff1fce2014-12-11 07:07:37 -0800269 DeviceCM* fTopLayer;
270 SkRasterClip fRasterClip;
271 SkMatrix fMatrix;
272 int fDeferredSaveCount;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273
vjiaoblacke5de1302016-07-13 14:05:28 -0700274 // This is the current cumulative depth (aggregate of all done translateZ calls)
275 SkScalar fCurDrawDepth;
276
reedd9544982014-09-09 18:46:22 -0700277 MCRec(bool conservativeRasterClip) : fRasterClip(conservativeRasterClip) {
halcanary96fcdcc2015-08-27 07:41:13 -0700278 fFilter = nullptr;
279 fLayer = nullptr;
280 fTopLayer = nullptr;
reed2ff1fce2014-12-11 07:07:37 -0800281 fMatrix.reset();
282 fDeferredSaveCount = 0;
vjiaoblacke5de1302016-07-13 14:05:28 -0700283 fCurDrawDepth = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700284
reedd9544982014-09-09 18:46:22 -0700285 // don't bother initializing fNext
286 inc_rec();
287 }
vjiaoblacke5de1302016-07-13 14:05:28 -0700288 MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix),
289 fCurDrawDepth(prev.fCurDrawDepth) {
reedd9544982014-09-09 18:46:22 -0700290 fFilter = SkSafeRef(prev.fFilter);
halcanary96fcdcc2015-08-27 07:41:13 -0700291 fLayer = nullptr;
reedd9544982014-09-09 18:46:22 -0700292 fTopLayer = prev.fTopLayer;
reed2ff1fce2014-12-11 07:07:37 -0800293 fDeferredSaveCount = 0;
piotaixrb5fae932014-09-24 13:03:30 -0700294
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 // don't bother initializing fNext
296 inc_rec();
297 }
298 ~MCRec() {
reed@google.com82065d62011-02-07 15:30:46 +0000299 SkSafeUnref(fFilter);
halcanary385fe4d2015-08-26 13:07:48 -0700300 delete fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301 dec_rec();
302 }
mtkleinfeaadee2015-04-08 11:25:48 -0700303
304 void reset(const SkIRect& bounds) {
305 SkASSERT(fLayer);
306 SkASSERT(fDeferredSaveCount == 0);
307
308 fMatrix.reset();
309 fRasterClip.setRect(bounds);
310 fLayer->reset(bounds);
311 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312};
313
reed02f9ed72016-09-06 09:06:18 -0700314static SkIRect compute_device_bounds(SkBaseDevice* device) {
315 return SkIRect::MakeXYWH(device->getOrigin().x(), device->getOrigin().y(),
316 device->width(), device->height());
317}
318
reed@android.com8a1c16f2008-12-17 15:59:43 +0000319class SkDrawIter : public SkDraw {
320public:
reed3aafe112016-08-18 12:45:34 -0700321 SkDrawIter(SkCanvas* canvas) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 canvas->updateDeviceCMCache();
323
bungeman6bd52842016-10-27 09:30:08 -0700324 fClipStack = canvas->getClipStack();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 fCurrLayer = canvas->fMCRec->fTopLayer;
reed02f9ed72016-09-06 09:06:18 -0700326
327 fMultiDeviceCS = nullptr;
328 if (fCurrLayer->fNext) {
bungeman6bd52842016-10-27 09:30:08 -0700329 fMultiDeviceCS = canvas->fClipStack.get();
reed02f9ed72016-09-06 09:06:18 -0700330 fMultiDeviceCS->save();
331 }
332 }
333
334 ~SkDrawIter() {
335 if (fMultiDeviceCS) {
336 fMultiDeviceCS->restore();
337 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 }
reed@google.com4b226022011-01-11 18:32:13 +0000339
reed@android.com8a1c16f2008-12-17 15:59:43 +0000340 bool next() {
reed02f9ed72016-09-06 09:06:18 -0700341 if (fMultiDeviceCS && fDevice) {
342 // remove the previous device's bounds
reed73603f32016-09-20 08:42:38 -0700343 fMultiDeviceCS->clipDevRect(compute_device_bounds(fDevice), SkCanvas::kDifference_Op);
reed02f9ed72016-09-06 09:06:18 -0700344 }
345
reed@android.com8a1c16f2008-12-17 15:59:43 +0000346 // skip over recs with empty clips
reed3aafe112016-08-18 12:45:34 -0700347 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
348 fCurrLayer = fCurrLayer->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000349 }
350
reed@google.comf68c5e22012-02-24 16:38:58 +0000351 const DeviceCM* rec = fCurrLayer;
352 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000353
354 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000355 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000356 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700357 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700358 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700359 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000361 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000362
363 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700364 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000365
reed@android.com8a1c16f2008-12-17 15:59:43 +0000366 return true;
367 }
368 return false;
369 }
reed@google.com4b226022011-01-11 18:32:13 +0000370
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000371 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700372 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000373 int getX() const { return fDevice->getOrigin().x(); }
374 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000375 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000377
reed@android.com8a1c16f2008-12-17 15:59:43 +0000378private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379 const DeviceCM* fCurrLayer;
380 const SkPaint* fPaint; // May be null.
reed02f9ed72016-09-06 09:06:18 -0700381 SkClipStack* fMultiDeviceCS;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000382
383 typedef SkDraw INHERITED;
384};
385
386/////////////////////////////////////////////////////////////////////////////
387
reeddbc3cef2015-04-29 12:18:57 -0700388static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
389 return lazy->isValid() ? lazy->get() : lazy->set(orig);
390}
391
392/**
393 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700394 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700395 */
reedd053ce92016-03-22 10:17:23 -0700396static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700397 SkImageFilter* imgf = paint.getImageFilter();
398 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700399 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700400 }
401
reedd053ce92016-03-22 10:17:23 -0700402 SkColorFilter* imgCFPtr;
403 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700404 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700405 }
reedd053ce92016-03-22 10:17:23 -0700406 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700407
408 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700409 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700410 // there is no existing paint colorfilter, so we can just return the imagefilter's
411 return imgCF;
412 }
413
414 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
415 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700416 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700417}
418
senorblanco87e066e2015-10-28 11:23:36 -0700419/**
420 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
421 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
422 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
423 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
424 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
425 * conservative "effective" bounds based on the settings in the paint... with one exception. This
426 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
427 * deliberately ignored.
428 */
429static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
430 const SkRect& rawBounds,
431 SkRect* storage) {
432 SkPaint tmpUnfiltered(paint);
433 tmpUnfiltered.setImageFilter(nullptr);
434 if (tmpUnfiltered.canComputeFastBounds()) {
435 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
436 } else {
437 return rawBounds;
438 }
439}
440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441class AutoDrawLooper {
442public:
senorblanco87e066e2015-10-28 11:23:36 -0700443 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
444 // paint. It's used to determine the size of the offscreen layer for filters.
445 // If null, the clip will be used instead.
reed3aafe112016-08-18 12:45:34 -0700446 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700447 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000448 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800449#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800451#else
452 fFilter = nullptr;
453#endif
reed4a8126e2014-09-22 07:29:03 -0700454 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000455 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700456 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000457 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458
reedd053ce92016-03-22 10:17:23 -0700459 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700460 if (simplifiedCF) {
461 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700462 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700463 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700464 fPaint = paint;
465 }
466
467 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700468 /**
469 * We implement ImageFilters for a given draw by creating a layer, then applying the
470 * imagefilter to the pixels of that layer (its backing surface/image), and then
471 * we call restore() to xfer that layer to the main canvas.
472 *
473 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
474 * 2. Generate the src pixels:
475 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
476 * return (fPaint). We then draw the primitive (using srcover) into a cleared
477 * buffer/surface.
478 * 3. Restore the layer created in #1
479 * The imagefilter is passed the buffer/surface from the layer (now filled with the
480 * src pixels of the primitive). It returns a new "filtered" buffer, which we
481 * draw onto the previous layer using the xfermode from the original paint.
482 */
reed@google.com8926b162012-03-23 15:36:36 +0000483 SkPaint tmp;
Mike Reed5e257172016-11-01 11:22:05 -0400484 tmp.setImageFilter(sk_ref_sp(fPaint->getImageFilter()));
reed374772b2016-10-05 17:33:02 -0700485 tmp.setBlendMode(fPaint->getBlendMode());
senorblanco87e066e2015-10-28 11:23:36 -0700486 SkRect storage;
487 if (rawBounds) {
488 // Make rawBounds include all paint outsets except for those due to image filters.
489 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
490 }
reedbfd5f172016-01-07 11:28:08 -0800491 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700492 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700493 fTempLayerForImageFilter = true;
494 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000495 }
496
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000497 if (SkDrawLooper* looper = paint.getLooper()) {
498 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
499 looper->contextSize());
500 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000501 fIsSimple = false;
502 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700503 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000504 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700505 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000506 }
507 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000508
reed@android.com8a1c16f2008-12-17 15:59:43 +0000509 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700510 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000511 fCanvas->internalRestore();
512 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000513 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000514 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000515
reed@google.com4e2b3d32011-04-07 14:18:59 +0000516 const SkPaint& paint() const {
517 SkASSERT(fPaint);
518 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000519 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000520
reed@google.com129ec222012-05-15 13:24:09 +0000521 bool next(SkDrawFilter::Type drawType) {
522 if (fDone) {
523 return false;
524 } else if (fIsSimple) {
525 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000526 return !fPaint->nothingToDraw();
527 } else {
528 return this->doNext(drawType);
529 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000530 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000531
reed@android.com8a1c16f2008-12-17 15:59:43 +0000532private:
reeddbc3cef2015-04-29 12:18:57 -0700533 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
534 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000535 SkCanvas* fCanvas;
536 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000537 SkDrawFilter* fFilter;
538 const SkPaint* fPaint;
539 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700540 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000541 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000542 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000543 SkDrawLooper::Context* fLooperContext;
544 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000545
546 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000547};
548
reed@google.com129ec222012-05-15 13:24:09 +0000549bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700550 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000551 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700552 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000553
reeddbc3cef2015-04-29 12:18:57 -0700554 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
555 *fLazyPaintInit.get() : fOrigPaint);
reed@google.com129ec222012-05-15 13:24:09 +0000556
reed5c476fb2015-04-20 08:04:21 -0700557 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700558 paint->setImageFilter(nullptr);
reed374772b2016-10-05 17:33:02 -0700559 paint->setBlendMode(SkBlendMode::kSrcOver);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000560 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000561
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000562 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000563 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000564 return false;
565 }
566 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000567 if (!fFilter->filter(paint, drawType)) {
568 fDone = true;
569 return false;
570 }
halcanary96fcdcc2015-08-27 07:41:13 -0700571 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000572 // no looper means we only draw once
573 fDone = true;
574 }
575 }
576 fPaint = paint;
577
578 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000579 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000580 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000581 }
582
583 // call this after any possible paint modifiers
584 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700585 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000586 return false;
587 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000588 return true;
589}
590
reed@android.com8a1c16f2008-12-17 15:59:43 +0000591////////// macros to place around the internal draw calls //////////////////
592
reed3aafe112016-08-18 12:45:34 -0700593#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
594 this->predrawNotify(); \
595 AutoDrawLooper looper(this, paint, skipLayerForFilter, bounds); \
596 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
reed262a71b2015-12-05 13:07:27 -0800597 SkDrawIter iter(this);
598
599
reed@google.com8926b162012-03-23 15:36:36 +0000600#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000601 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700602 AutoDrawLooper looper(this, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000603 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000604 SkDrawIter iter(this);
605
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000606#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000607 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700608 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000609 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000610 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000611
reedc83a2972015-07-16 07:40:45 -0700612#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
613 this->predrawNotify(bounds, &paint, auxOpaque); \
reed3aafe112016-08-18 12:45:34 -0700614 AutoDrawLooper looper(this, paint, false, bounds); \
reedc83a2972015-07-16 07:40:45 -0700615 while (looper.next(type)) { \
616 SkDrawIter iter(this);
617
reed@google.com4e2b3d32011-04-07 14:18:59 +0000618#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000619
620////////////////////////////////////////////////////////////////////////////
621
msarettfbfa2582016-08-12 08:29:08 -0700622static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
623 if (bounds.isEmpty()) {
624 return SkRect::MakeEmpty();
625 }
626
627 // Expand bounds out by 1 in case we are anti-aliasing. We store the
628 // bounds as floats to enable a faster quick reject implementation.
629 SkRect dst;
630 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
631 return dst;
632}
633
mtkleinfeaadee2015-04-08 11:25:48 -0700634void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
635 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700636 fClipStack->reset();
637 fMCRec->reset(bounds);
638
639 // We're peering through a lot of structs here. Only at this scope do we
640 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
641 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
msarettfbfa2582016-08-12 08:29:08 -0700642 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700643 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700644}
645
reedd9544982014-09-09 18:46:22 -0700646SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800647 if (device && device->forceConservativeRasterClip()) {
648 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
649 }
650 // Since init() is only called once by our constructors, it is safe to perform this
651 // const-cast.
652 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
653
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000654 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700655 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800656 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700657 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700658#ifdef SK_EXPERIMENTAL_SHADOWING
659 fLights = nullptr;
660#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000661
halcanary385fe4d2015-08-26 13:07:48 -0700662 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700663
reed@android.com8a1c16f2008-12-17 15:59:43 +0000664 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700665 new (fMCRec) MCRec(fConservativeRasterClip);
msarett9637ea92016-08-18 14:03:30 -0700666 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000667
reeda499f902015-05-01 09:34:31 -0700668 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
669 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700670 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700671 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700672
reed@android.com8a1c16f2008-12-17 15:59:43 +0000673 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674
halcanary96fcdcc2015-08-27 07:41:13 -0700675 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000676
reedf92c8662014-08-18 08:02:43 -0700677 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700678 // The root device and the canvas should always have the same pixel geometry
679 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700680 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800681 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700682 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700683 }
msarettfbfa2582016-08-12 08:29:08 -0700684
reedf92c8662014-08-18 08:02:43 -0700685 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000686}
687
reed@google.comcde92112011-07-06 20:00:52 +0000688SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000689 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700690 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800691 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000692{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000693 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000694
halcanary96fcdcc2015-08-27 07:41:13 -0700695 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000696}
697
reedd9544982014-09-09 18:46:22 -0700698static SkBitmap make_nopixels(int width, int height) {
699 SkBitmap bitmap;
700 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
701 return bitmap;
702}
703
704class SkNoPixelsBitmapDevice : public SkBitmapDevice {
705public:
robertphillipsfcf78292015-06-19 11:49:52 -0700706 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
707 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800708 {
709 this->setOrigin(bounds.x(), bounds.y());
710 }
reedd9544982014-09-09 18:46:22 -0700711
712private:
piotaixrb5fae932014-09-24 13:03:30 -0700713
reedd9544982014-09-09 18:46:22 -0700714 typedef SkBitmapDevice INHERITED;
715};
716
reed96a857e2015-01-25 10:33:58 -0800717SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000718 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800719 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800720 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000721{
722 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700723
halcanary385fe4d2015-08-26 13:07:48 -0700724 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
725 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700726}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000727
reed78e27682014-11-19 08:04:34 -0800728SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700729 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700730 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800731 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700732{
733 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700734
halcanary385fe4d2015-08-26 13:07:48 -0700735 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700736}
737
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000738SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000739 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700740 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800741 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000742{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000743 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700744
reedd9544982014-09-09 18:46:22 -0700745 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000746}
747
robertphillipsfcf78292015-06-19 11:49:52 -0700748SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
749 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700750 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800751 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700752{
753 inc_canvas();
754
755 this->init(device, flags);
756}
757
reed4a8126e2014-09-22 07:29:03 -0700758SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700759 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700760 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800761 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700762{
763 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700764
Hal Canary704cd322016-11-07 14:13:52 -0500765 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
766 this->init(device.get(), kDefault_InitFlags);
reed4a8126e2014-09-22 07:29:03 -0700767}
reed29c857d2014-09-21 10:25:07 -0700768
reed4a8126e2014-09-22 07:29:03 -0700769SkCanvas::SkCanvas(const SkBitmap& bitmap)
770 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
771 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800772 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700773{
774 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700775
Hal Canary704cd322016-11-07 14:13:52 -0500776 sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
777 this->init(device.get(), kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000778}
779
780SkCanvas::~SkCanvas() {
781 // free up the contents of our deque
782 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000783
reed@android.com8a1c16f2008-12-17 15:59:43 +0000784 this->internalRestore(); // restore the last, since we're going away
785
halcanary385fe4d2015-08-26 13:07:48 -0700786 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000787
reed@android.com8a1c16f2008-12-17 15:59:43 +0000788 dec_canvas();
789}
790
fmalita53d9f1c2016-01-25 06:23:54 -0800791#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000792SkDrawFilter* SkCanvas::getDrawFilter() const {
793 return fMCRec->fFilter;
794}
795
796SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700797 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000798 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
799 return filter;
800}
fmalita77650002016-01-21 18:47:11 -0800801#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000802
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000803SkMetaData& SkCanvas::getMetaData() {
804 // metadata users are rare, so we lazily allocate it. If that changes we
805 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700806 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000807 fMetaData = new SkMetaData;
808 }
809 return *fMetaData;
810}
811
reed@android.com8a1c16f2008-12-17 15:59:43 +0000812///////////////////////////////////////////////////////////////////////////////
813
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000814void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700815 this->onFlush();
816}
817
818void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000819 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000820 if (device) {
821 device->flush();
822 }
823}
824
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000825SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000826 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000827 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
828}
829
senorblancoafc7cce2016-02-02 18:44:15 -0800830SkIRect SkCanvas::getTopLayerBounds() const {
831 SkBaseDevice* d = this->getTopDevice();
832 if (!d) {
833 return SkIRect::MakeEmpty();
834 }
835 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
836}
837
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000838SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000839 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000840 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000841 SkASSERT(rec && rec->fLayer);
842 return rec->fLayer->fDevice;
843}
844
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000845SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000846 if (updateMatrixClip) {
847 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
848 }
reed@google.com9266fed2011-03-30 00:18:03 +0000849 return fMCRec->fTopLayer->fDevice;
850}
851
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000852bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700853 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000854 return false;
855 }
856
857 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700858 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700859 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000860 return false;
861 }
862 weAllocated = true;
863 }
864
reedcf01e312015-05-23 19:14:51 -0700865 SkAutoPixmapUnlock unlocker;
866 if (bitmap->requestLock(&unlocker)) {
867 const SkPixmap& pm = unlocker.pixmap();
868 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
869 return true;
870 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000871 }
872
873 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700874 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000875 }
876 return false;
877}
reed@google.com51df9e32010-12-23 19:29:18 +0000878
bsalomon@google.comc6980972011-11-02 19:57:21 +0000879bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000880 SkIRect r = srcRect;
881 const SkISize size = this->getBaseLayerSize();
882 if (!r.intersect(0, 0, size.width(), size.height())) {
883 bitmap->reset();
884 return false;
885 }
886
reed84825042014-09-02 12:50:45 -0700887 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000888 // bitmap will already be reset.
889 return false;
890 }
891 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
892 bitmap->reset();
893 return false;
894 }
895 return true;
896}
897
reed96472de2014-12-10 09:53:42 -0800898bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000899 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000900 if (!device) {
901 return false;
902 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000903 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800904
reed96472de2014-12-10 09:53:42 -0800905 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
906 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000907 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000908 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000909
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000910 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800911 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000912}
913
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000914bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700915 SkAutoPixmapUnlock unlocker;
916 if (bitmap.requestLock(&unlocker)) {
917 const SkPixmap& pm = unlocker.pixmap();
918 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000919 }
920 return false;
921}
922
923bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
924 int x, int y) {
925 switch (origInfo.colorType()) {
926 case kUnknown_SkColorType:
927 case kIndex_8_SkColorType:
928 return false;
929 default:
930 break;
931 }
halcanary96fcdcc2015-08-27 07:41:13 -0700932 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000933 return false;
934 }
935
936 const SkISize size = this->getBaseLayerSize();
937 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
938 if (!target.intersect(0, 0, size.width(), size.height())) {
939 return false;
940 }
941
942 SkBaseDevice* device = this->getDevice();
943 if (!device) {
944 return false;
945 }
946
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000947 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700948 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000949
950 // if x or y are negative, then we have to adjust pixels
951 if (x > 0) {
952 x = 0;
953 }
954 if (y > 0) {
955 y = 0;
956 }
957 // here x,y are either 0 or negative
958 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
959
reed4af35f32014-06-27 17:47:49 -0700960 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700961 const bool completeOverwrite = info.dimensions() == size;
962 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700963
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000964 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000965 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000966}
reed@google.com51df9e32010-12-23 19:29:18 +0000967
reed@android.com8a1c16f2008-12-17 15:59:43 +0000968//////////////////////////////////////////////////////////////////////////////
969
reed@android.com8a1c16f2008-12-17 15:59:43 +0000970void SkCanvas::updateDeviceCMCache() {
971 if (fDeviceCMDirty) {
972 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700973 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000974 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000975
halcanary96fcdcc2015-08-27 07:41:13 -0700976 if (nullptr == layer->fNext) { // only one layer
reedde6c5312016-09-02 12:10:07 -0700977 layer->updateMC(totalMatrix, totalClip, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000978 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000979 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000980 do {
reedde6c5312016-09-02 12:10:07 -0700981 layer->updateMC(totalMatrix, clip, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700982 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 }
984 fDeviceCMDirty = false;
985 }
986}
987
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988///////////////////////////////////////////////////////////////////////////////
989
reed2ff1fce2014-12-11 07:07:37 -0800990void SkCanvas::checkForDeferredSave() {
991 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800992 this->doSave();
993 }
994}
995
reedf0090cb2014-11-26 08:55:51 -0800996int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -0800997#ifdef SK_DEBUG
998 int count = 0;
999 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1000 for (;;) {
1001 const MCRec* rec = (const MCRec*)iter.next();
1002 if (!rec) {
1003 break;
1004 }
1005 count += 1 + rec->fDeferredSaveCount;
1006 }
1007 SkASSERT(count == fSaveCount);
1008#endif
1009 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001010}
1011
1012int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001013 fSaveCount += 1;
1014 fMCRec->fDeferredSaveCount += 1;
1015 return this->getSaveCount() - 1; // return our prev value
1016}
1017
1018void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001019 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001020
1021 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1022 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001023 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001024}
1025
1026void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001027 if (fMCRec->fDeferredSaveCount > 0) {
1028 SkASSERT(fSaveCount > 1);
1029 fSaveCount -= 1;
1030 fMCRec->fDeferredSaveCount -= 1;
1031 } else {
1032 // check for underflow
1033 if (fMCStack.count() > 1) {
1034 this->willRestore();
1035 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001036 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001037 this->internalRestore();
1038 this->didRestore();
1039 }
reedf0090cb2014-11-26 08:55:51 -08001040 }
1041}
1042
1043void SkCanvas::restoreToCount(int count) {
1044 // sanity check
1045 if (count < 1) {
1046 count = 1;
1047 }
mtkleinf0f14112014-12-12 08:46:25 -08001048
reedf0090cb2014-11-26 08:55:51 -08001049 int n = this->getSaveCount() - count;
1050 for (int i = 0; i < n; ++i) {
1051 this->restore();
1052 }
1053}
1054
reed2ff1fce2014-12-11 07:07:37 -08001055void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001056 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001057 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001058 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001059
reed687fa1c2015-04-07 08:00:56 -07001060 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061}
1062
reed4960eee2015-12-18 07:09:18 -08001063bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed4960eee2015-12-18 07:09:18 -08001064 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001065}
1066
reed4960eee2015-12-18 07:09:18 -08001067bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001068 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001069 SkIRect clipBounds;
1070 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001071 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001072 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001073
reed96e657d2015-03-10 17:30:07 -07001074 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1075
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001076 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001077 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001078 if (bounds && !imageFilter->canComputeFastBounds()) {
1079 bounds = nullptr;
1080 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001081 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001082 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001083 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001084 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001085
reed96e657d2015-03-10 17:30:07 -07001086 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001087 r.roundOut(&ir);
1088 // early exit if the layer's bounds are clipped out
1089 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001090 if (BoundsAffectsClip(saveLayerFlags)) {
reed1f836ee2014-07-07 07:49:34 -07001091 fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001092 fDeviceClipBounds.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001093 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001094 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001095 }
1096 } else { // no user bounds, so just use the clip
1097 ir = clipBounds;
1098 }
reed180aec42015-03-11 10:39:04 -07001099 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100
reed4960eee2015-12-18 07:09:18 -08001101 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001102 // Simplify the current clips since they will be applied properly during restore()
reed73603f32016-09-20 08:42:38 -07001103 fClipStack->clipDevRect(ir, kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001104 fMCRec->fRasterClip.setRect(ir);
msarettfbfa2582016-08-12 08:29:08 -07001105 fDeviceClipBounds = qr_clip_bounds(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001106 }
1107
1108 if (intersection) {
1109 *intersection = ir;
1110 }
1111 return true;
1112}
1113
reed4960eee2015-12-18 07:09:18 -08001114
reed4960eee2015-12-18 07:09:18 -08001115int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1116 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001117}
1118
reed70ee31b2015-12-10 13:44:45 -08001119int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001120 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1121}
1122
1123int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1124 SaveLayerRec rec(origRec);
1125 if (gIgnoreSaveLayerBounds) {
1126 rec.fBounds = nullptr;
1127 }
1128 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1129 fSaveCount += 1;
1130 this->internalSaveLayer(rec, strategy);
1131 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001132}
1133
reeda2217ef2016-07-20 06:04:34 -07001134void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1135 SkBaseDevice* dst, const SkMatrix& ctm,
1136 const SkClipStack* clipStack) {
1137 SkDraw draw;
1138 SkRasterClip rc;
1139 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1140 if (!dst->accessPixels(&draw.fDst)) {
1141 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001142 }
reeda2217ef2016-07-20 06:04:34 -07001143 draw.fMatrix = &SkMatrix::I();
1144 draw.fRC = &rc;
1145 draw.fClipStack = clipStack;
1146 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001147
1148 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001149 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001150
1151 int x = src->getOrigin().x() - dst->getOrigin().x();
1152 int y = src->getOrigin().y() - dst->getOrigin().y();
1153 auto special = src->snapSpecial();
1154 if (special) {
1155 dst->drawSpecial(draw, special.get(), x, y, p);
1156 }
robertphillips7354a4b2015-12-16 05:08:27 -08001157}
reed70ee31b2015-12-10 13:44:45 -08001158
reed129ed1c2016-02-22 06:42:31 -08001159static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1160 const SkPaint* paint) {
1161 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1162 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001163 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001164 const bool hasImageFilter = paint && paint->getImageFilter();
1165
1166 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1167 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1168 // force to L32
1169 return SkImageInfo::MakeN32(w, h, alphaType);
1170 } else {
1171 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001172 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001173 }
1174}
1175
reed4960eee2015-12-18 07:09:18 -08001176void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1177 const SkRect* bounds = rec.fBounds;
1178 const SkPaint* paint = rec.fPaint;
1179 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1180
reed8c30a812016-04-20 16:36:51 -07001181 SkLazyPaint lazyP;
1182 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1183 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001184 SkMatrix remainder;
1185 SkSize scale;
1186 /*
1187 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1188 * but they do handle scaling. To accommodate this, we do the following:
1189 *
1190 * 1. Stash off the current CTM
1191 * 2. Decompose the CTM into SCALE and REMAINDER
1192 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1193 * contains the REMAINDER
1194 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1195 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1196 * of the original imagefilter, and draw that (via drawSprite)
1197 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1198 *
1199 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1200 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1201 */
reed96a04f32016-04-25 09:25:15 -07001202 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001203 stashedMatrix.decomposeScale(&scale, &remainder))
1204 {
1205 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1206 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1207 SkPaint* p = lazyP.set(*paint);
1208 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1209 SkFilterQuality::kLow_SkFilterQuality,
1210 sk_ref_sp(imageFilter)));
1211 imageFilter = p->getImageFilter();
1212 paint = p;
1213 }
reed8c30a812016-04-20 16:36:51 -07001214
junov@chromium.orga907ac32012-02-24 21:54:07 +00001215 // do this before we create the layer. We don't call the public save() since
1216 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001217 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001218
1219 fDeviceCMDirty = true;
1220
1221 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001222 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001223 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001224 }
1225
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001226 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1227 // the clipRectBounds() call above?
1228 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001229 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001230 }
1231
reed4960eee2015-12-18 07:09:18 -08001232 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001233 SkPixelGeometry geo = fProps.pixelGeometry();
1234 if (paint) {
reed76033be2015-03-14 10:54:31 -07001235 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001236 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001237 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001238 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001239 }
1240 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001241
robertphillips5139e502016-07-19 05:10:40 -07001242 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001243 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001244 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001245 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001246 }
reedb2db8982014-11-13 12:41:02 -08001247
robertphillips5139e502016-07-19 05:10:40 -07001248 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001249 paint);
1250
Hal Canary704cd322016-11-07 14:13:52 -05001251 sk_sp<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001252 {
reed70ee31b2015-12-10 13:44:45 -08001253 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001254 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001255 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001256 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001257 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001258 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1259 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001260 return;
reed61f501f2015-04-29 08:34:00 -07001261 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001262 }
robertphillips5139e502016-07-19 05:10:40 -07001263 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001264
Hal Canary704cd322016-11-07 14:13:52 -05001265 DeviceCM* layer =
1266 new DeviceCM(newDevice.get(), paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001267
1268 layer->fNext = fMCRec->fTopLayer;
1269 fMCRec->fLayer = layer;
1270 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001271
1272 if (rec.fBackdrop) {
Hal Canary704cd322016-11-07 14:13:52 -05001273 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(),
reeda2217ef2016-07-20 06:04:34 -07001274 fMCRec->fMatrix, this->getClipStack());
1275 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001276}
1277
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001278int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001279 if (0xFF == alpha) {
1280 return this->saveLayer(bounds, nullptr);
1281 } else {
1282 SkPaint tmpPaint;
1283 tmpPaint.setAlpha(alpha);
1284 return this->saveLayer(bounds, &tmpPaint);
1285 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001286}
1287
reed@android.com8a1c16f2008-12-17 15:59:43 +00001288void SkCanvas::internalRestore() {
1289 SkASSERT(fMCStack.count() != 0);
1290
1291 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292
reed687fa1c2015-04-07 08:00:56 -07001293 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001294
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001295 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296 DeviceCM* layer = fMCRec->fLayer; // may be null
1297 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001298 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001299
1300 // now do the normal restore()
1301 fMCRec->~MCRec(); // balanced in save()
1302 fMCStack.pop_back();
1303 fMCRec = (MCRec*)fMCStack.back();
1304
1305 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1306 since if we're being recorded, we don't want to record this (the
1307 recorder will have already recorded the restore).
1308 */
bsalomon49f085d2014-09-05 13:34:00 -07001309 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001310 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001311 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001312 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001313 // restore what we smashed in internalSaveLayer
1314 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001315 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001316 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001317 delete layer;
reedb679ca82015-04-07 04:40:48 -07001318 } else {
1319 // we're at the root
reeda499f902015-05-01 09:34:31 -07001320 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001321 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001322 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001323 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001324 }
msarettfbfa2582016-08-12 08:29:08 -07001325
1326 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001327 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001328 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1329 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001330}
1331
reede8f30622016-03-23 18:59:25 -07001332sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001333 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001334 props = &fProps;
1335 }
1336 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001337}
1338
reede8f30622016-03-23 18:59:25 -07001339sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001340 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001341 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001342}
1343
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001344SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001345 return this->onImageInfo();
1346}
1347
1348SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001349 SkBaseDevice* dev = this->getDevice();
1350 if (dev) {
1351 return dev->imageInfo();
1352 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001353 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001354 }
1355}
1356
brianosman898235c2016-04-06 07:38:23 -07001357bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001358 return this->onGetProps(props);
1359}
1360
1361bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001362 SkBaseDevice* dev = this->getDevice();
1363 if (dev) {
1364 if (props) {
1365 *props = fProps;
1366 }
1367 return true;
1368 } else {
1369 return false;
1370 }
1371}
1372
reed6ceeebd2016-03-09 14:26:26 -08001373bool SkCanvas::peekPixels(SkPixmap* pmap) {
1374 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001375}
1376
reed884e97c2015-05-26 11:31:54 -07001377bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001378 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001379 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001380}
1381
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001382void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001383 SkPixmap pmap;
1384 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001385 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001386 }
1387 if (info) {
1388 *info = pmap.info();
1389 }
1390 if (rowBytes) {
1391 *rowBytes = pmap.rowBytes();
1392 }
1393 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001394 *origin = this->getTopDevice(false)->getOrigin();
1395 }
reed884e97c2015-05-26 11:31:54 -07001396 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001397}
1398
reed884e97c2015-05-26 11:31:54 -07001399bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001400 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001401 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001402}
1403
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001405
reed7503d602016-07-15 14:23:29 -07001406void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001407 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001408 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001409 paint = &tmp;
1410 }
reed@google.com4b226022011-01-11 18:32:13 +00001411
reed@google.com8926b162012-03-23 15:36:36 +00001412 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001413
reed@android.com8a1c16f2008-12-17 15:59:43 +00001414 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001415 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001416 paint = &looper.paint();
1417 SkImageFilter* filter = paint->getImageFilter();
1418 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
Hal Canary43513542016-10-14 11:19:43 -04001419 sk_sp<SkSpecialImage> specialImage;
1420 if (filter && (specialImage = srcDev->snapSpecial())) {
1421 dstDev->drawSpecial(iter, specialImage.get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001422 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001423 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001424 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425 }
reeda2217ef2016-07-20 06:04:34 -07001426
reed@google.com4e2b3d32011-04-07 14:18:59 +00001427 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001428}
1429
reed32704672015-12-16 08:27:10 -08001430/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001431
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001432void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001433 if (dx || dy) {
1434 this->checkForDeferredSave();
1435 fDeviceCMDirty = true;
1436 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001437
reedfe69b502016-09-12 06:31:48 -07001438 // Translate shouldn't affect the is-scale-translateness of the matrix.
1439 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001440
reedfe69b502016-09-12 06:31:48 -07001441 this->didTranslate(dx,dy);
1442 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001443}
1444
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001445void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001446 SkMatrix m;
1447 m.setScale(sx, sy);
1448 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001449}
1450
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001451void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001452 SkMatrix m;
1453 m.setRotate(degrees);
1454 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001455}
1456
bungeman7438bfc2016-07-12 15:01:19 -07001457void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1458 SkMatrix m;
1459 m.setRotate(degrees, px, py);
1460 this->concat(m);
1461}
1462
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001463void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001464 SkMatrix m;
1465 m.setSkew(sx, sy);
1466 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001467}
1468
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001469void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001470 if (matrix.isIdentity()) {
1471 return;
1472 }
1473
reed2ff1fce2014-12-11 07:07:37 -08001474 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001475 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001476 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001477 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001478 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001479}
1480
reed8c30a812016-04-20 16:36:51 -07001481void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001482 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001483 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001484 fIsScaleTranslate = matrix.isScaleTranslate();
reed8c30a812016-04-20 16:36:51 -07001485}
1486
1487void SkCanvas::setMatrix(const SkMatrix& matrix) {
1488 this->checkForDeferredSave();
1489 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001490 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491}
1492
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001494 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001495}
1496
vjiaoblack95302da2016-07-21 10:25:54 -07001497#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001498void SkCanvas::translateZ(SkScalar z) {
1499 this->checkForDeferredSave();
1500 this->fMCRec->fCurDrawDepth += z;
1501 this->didTranslateZ(z);
1502}
1503
1504SkScalar SkCanvas::getZ() const {
1505 return this->fMCRec->fCurDrawDepth;
1506}
1507
vjiaoblack95302da2016-07-21 10:25:54 -07001508void SkCanvas::setLights(sk_sp<SkLights> lights) {
1509 this->fLights = lights;
1510}
1511
1512sk_sp<SkLights> SkCanvas::getLights() const {
1513 return this->fLights;
1514}
1515#endif
1516
reed@android.com8a1c16f2008-12-17 15:59:43 +00001517//////////////////////////////////////////////////////////////////////////////
1518
reed73603f32016-09-20 08:42:38 -07001519void SkCanvas::clipRect(const SkRect& rect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001520 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001521 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1522 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001523}
1524
reed73603f32016-09-20 08:42:38 -07001525void SkCanvas::onClipRect(const SkRect& rect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001526 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reedc64eff52015-11-21 12:39:45 -08001527 AutoValidateClip avc(this);
Brian Salomona3b45d42016-10-03 11:36:16 -04001528 fClipStack->clipRect(rect, fMCRec->fMatrix, op, isAA);
1529 fMCRec->fRasterClip.op(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1530 isAA);
reedc64eff52015-11-21 12:39:45 -08001531 fDeviceCMDirty = true;
msarettfbfa2582016-08-12 08:29:08 -07001532 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001533}
1534
reed73603f32016-09-20 08:42:38 -07001535void SkCanvas::clipRRect(const SkRRect& rrect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001536 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001537 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001538 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001539 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1540 } else {
1541 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001542 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001543}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001544
reed73603f32016-09-20 08:42:38 -07001545void SkCanvas::onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001546 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001547
Brian Salomona3b45d42016-10-03 11:36:16 -04001548 fDeviceCMDirty = true;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001549
Brian Salomona3b45d42016-10-03 11:36:16 -04001550 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1551 fClipStack->clipRRect(rrect, fMCRec->fMatrix, op, isAA);
1552 fMCRec->fRasterClip.op(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1553 isAA);
1554 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1555 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001556}
1557
reed73603f32016-09-20 08:42:38 -07001558void SkCanvas::clipPath(const SkPath& path, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001559 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001560 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001561
1562 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1563 SkRect r;
1564 if (path.isRect(&r)) {
1565 this->onClipRect(r, op, edgeStyle);
1566 return;
1567 }
1568 SkRRect rrect;
1569 if (path.isOval(&r)) {
1570 rrect.setOval(r);
1571 this->onClipRRect(rrect, op, edgeStyle);
1572 return;
1573 }
1574 if (path.isRRect(&rrect)) {
1575 this->onClipRRect(rrect, op, edgeStyle);
1576 return;
1577 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001578 }
robertphillips39f05382015-11-24 09:30:12 -08001579
1580 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001581}
1582
reed73603f32016-09-20 08:42:38 -07001583void SkCanvas::onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001584 AutoValidateClip avc(this);
1585
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586 fDeviceCMDirty = true;
Brian Salomona3b45d42016-10-03 11:36:16 -04001587 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588
Brian Salomona3b45d42016-10-03 11:36:16 -04001589 fClipStack->clipPath(path, fMCRec->fMatrix, op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001590
Brian Salomona3b45d42016-10-03 11:36:16 -04001591 const SkPath* rasterClipPath = &path;
1592 const SkMatrix* matrix = &fMCRec->fMatrix;
1593 SkPath tempPath;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001594 if (fAllowSimplifyClip) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001595 isAA = getClipStack()->asPath(&tempPath);
1596 rasterClipPath = &tempPath;
1597 matrix = &SkMatrix::I();
reed73603f32016-09-20 08:42:38 -07001598 op = kReplace_Op;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001599 }
Brian Salomona3b45d42016-10-03 11:36:16 -04001600 fMCRec->fRasterClip.op(*rasterClipPath, *matrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1601 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001602 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001603}
1604
reed73603f32016-09-20 08:42:38 -07001605void SkCanvas::clipRegion(const SkRegion& rgn, ClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001606 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001607 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001608}
1609
reed73603f32016-09-20 08:42:38 -07001610void SkCanvas::onClipRegion(const SkRegion& rgn, ClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001611 AutoValidateClip avc(this);
1612
reed@android.com8a1c16f2008-12-17 15:59:43 +00001613 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001614
reed@google.com5c3d1472011-02-22 19:12:23 +00001615 // todo: signal fClipStack that we have a region, and therefore (I guess)
1616 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001617 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001618
reed73603f32016-09-20 08:42:38 -07001619 fMCRec->fRasterClip.op(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001620 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001621}
1622
reed@google.com819c9212011-02-23 18:56:55 +00001623#ifdef SK_DEBUG
1624void SkCanvas::validateClip() const {
1625 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001626 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001627 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001628 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001629 return;
1630 }
1631
reed@google.com819c9212011-02-23 18:56:55 +00001632 SkIRect ir;
1633 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001634 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001635
reed687fa1c2015-04-07 08:00:56 -07001636 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001637 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001638 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001639 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001640 case SkClipStack::Element::kRect_Type:
1641 element->getRect().round(&ir);
reed73603f32016-09-20 08:42:38 -07001642 tmpClip.op(ir, (SkRegion::Op)element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001643 break;
1644 case SkClipStack::Element::kEmpty_Type:
1645 tmpClip.setEmpty();
1646 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001647 default: {
1648 SkPath path;
1649 element->asPath(&path);
Brian Salomona3b45d42016-10-03 11:36:16 -04001650 tmpClip.op(path, SkMatrix::I(), this->getTopLayerBounds(),
1651 (SkRegion::Op)element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001652 break;
1653 }
reed@google.com819c9212011-02-23 18:56:55 +00001654 }
1655 }
reed@google.com819c9212011-02-23 18:56:55 +00001656}
1657#endif
1658
reed@google.com90c07ea2012-04-13 13:50:27 +00001659void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001660 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001661 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001662
halcanary96fcdcc2015-08-27 07:41:13 -07001663 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001664 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001665 }
1666}
1667
reed@google.com5c3d1472011-02-22 19:12:23 +00001668///////////////////////////////////////////////////////////////////////////////
1669
reed@google.com754de5f2014-02-24 19:38:20 +00001670bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001671 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001672}
1673
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001674bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001675 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001676}
1677
msarettfbfa2582016-08-12 08:29:08 -07001678static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1679#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1680 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1681 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1682 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1683 return 0xF != _mm_movemask_ps(mask);
1684#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1685 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1686 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1687 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1688 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1689#else
1690 SkRect devRectAsRect;
1691 SkRect devClipAsRect;
1692 devRect.store(&devRectAsRect.fLeft);
1693 devClip.store(&devClipAsRect.fLeft);
1694 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1695#endif
1696}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001697
msarettfbfa2582016-08-12 08:29:08 -07001698// It's important for this function to not be inlined. Otherwise the compiler will share code
1699// between the fast path and the slow path, resulting in two slow paths.
1700static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1701 const SkMatrix& matrix) {
1702 SkRect deviceRect;
1703 matrix.mapRect(&deviceRect, src);
1704 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1705}
1706
1707bool SkCanvas::quickReject(const SkRect& src) const {
1708#ifdef SK_DEBUG
1709 // Verify that fDeviceClipBounds are set properly.
1710 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001711 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001712 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001713 } else {
msarettfbfa2582016-08-12 08:29:08 -07001714 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001715 }
msarettfbfa2582016-08-12 08:29:08 -07001716
msarett9637ea92016-08-18 14:03:30 -07001717 // Verify that fIsScaleTranslate is set properly.
1718 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001719#endif
1720
msarett9637ea92016-08-18 14:03:30 -07001721 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001722 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1723 }
1724
1725 // We inline the implementation of mapScaleTranslate() for the fast path.
1726 float sx = fMCRec->fMatrix.getScaleX();
1727 float sy = fMCRec->fMatrix.getScaleY();
1728 float tx = fMCRec->fMatrix.getTranslateX();
1729 float ty = fMCRec->fMatrix.getTranslateY();
1730 Sk4f scale(sx, sy, sx, sy);
1731 Sk4f trans(tx, ty, tx, ty);
1732
1733 // Apply matrix.
1734 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1735
1736 // Make sure left < right, top < bottom.
1737 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1738 Sk4f min = Sk4f::Min(ltrb, rblt);
1739 Sk4f max = Sk4f::Max(ltrb, rblt);
1740 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1741 // ARM this sequence generates the fastest (a single instruction).
1742 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1743
1744 // Check if the device rect is NaN or outside the clip.
1745 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001746}
1747
reed@google.com3b3e8952012-08-16 20:53:31 +00001748bool SkCanvas::quickReject(const SkPath& path) const {
1749 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001750}
1751
reed@google.com3b3e8952012-08-16 20:53:31 +00001752bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001753 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001754 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001755 return false;
1756 }
1757
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001758 SkMatrix inverse;
1759 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001760 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001761 if (bounds) {
1762 bounds->setEmpty();
1763 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001764 return false;
1765 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001766
bsalomon49f085d2014-09-05 13:34:00 -07001767 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001768 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001769 // adjust it outwards in case we are antialiasing
1770 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001771
reed@google.com8f4d2302013-12-17 16:44:46 +00001772 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1773 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 inverse.mapRect(bounds, r);
1775 }
1776 return true;
1777}
1778
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001779bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001780 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001781 if (clip.isEmpty()) {
1782 if (bounds) {
1783 bounds->setEmpty();
1784 }
1785 return false;
1786 }
1787
bsalomon49f085d2014-09-05 13:34:00 -07001788 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001789 *bounds = clip.getBounds();
1790 }
1791 return true;
1792}
1793
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001795 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001796}
1797
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001798const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001799 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001800}
1801
Brian Osman11052242016-10-27 14:47:55 -04001802GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001803 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001804 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001805}
1806
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001807GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001808 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001809 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001810}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001811
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001812void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1813 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001814 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001815 if (outer.isEmpty()) {
1816 return;
1817 }
1818 if (inner.isEmpty()) {
1819 this->drawRRect(outer, paint);
1820 return;
1821 }
1822
1823 // We don't have this method (yet), but technically this is what we should
1824 // be able to assert...
1825 // SkASSERT(outer.contains(inner));
1826 //
1827 // For now at least check for containment of bounds
1828 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1829
1830 this->onDrawDRRect(outer, inner, paint);
1831}
1832
reed41af9662015-01-05 07:49:08 -08001833// These need to stop being virtual -- clients need to override the onDraw... versions
1834
1835void SkCanvas::drawPaint(const SkPaint& paint) {
1836 this->onDrawPaint(paint);
1837}
1838
1839void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1840 this->onDrawRect(r, paint);
1841}
1842
msarettdca352e2016-08-26 06:37:45 -07001843void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
1844 if (region.isEmpty()) {
1845 return;
1846 }
1847
1848 if (region.isRect()) {
1849 return this->drawIRect(region.getBounds(), paint);
1850 }
1851
1852 this->onDrawRegion(region, paint);
1853}
1854
reed41af9662015-01-05 07:49:08 -08001855void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1856 this->onDrawOval(r, paint);
1857}
1858
1859void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1860 this->onDrawRRect(rrect, paint);
1861}
1862
1863void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1864 this->onDrawPoints(mode, count, pts, paint);
1865}
1866
1867void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001868 const SkPoint texs[], const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08001869 const uint16_t indices[], int indexCount, const SkPaint& paint) {
Mike Reedfaba3712016-11-03 14:45:31 -04001870 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, bmode,
reed41af9662015-01-05 07:49:08 -08001871 indices, indexCount, paint);
1872}
1873
1874void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1875 this->onDrawPath(path, paint);
1876}
1877
reeda85d4d02015-05-06 12:56:48 -07001878void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001879 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001880 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001881}
1882
reede47829b2015-08-06 10:02:53 -07001883void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1884 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001885 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001886 if (dst.isEmpty() || src.isEmpty()) {
1887 return;
1888 }
1889 this->onDrawImageRect(image, &src, dst, paint, constraint);
1890}
reed41af9662015-01-05 07:49:08 -08001891
reed84984ef2015-07-17 07:09:43 -07001892void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1893 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001894 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001895 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001896}
1897
reede47829b2015-08-06 10:02:53 -07001898void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1899 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001900 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001901 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1902 constraint);
1903}
reede47829b2015-08-06 10:02:53 -07001904
reed4c21dc52015-06-25 12:32:03 -07001905void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1906 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001907 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001908 if (dst.isEmpty()) {
1909 return;
1910 }
msarett552bca92016-08-03 06:53:26 -07001911 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1912 this->onDrawImageNine(image, center, dst, paint);
1913 } else {
reede47829b2015-08-06 10:02:53 -07001914 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001915 }
reed4c21dc52015-06-25 12:32:03 -07001916}
1917
msarett16882062016-08-16 09:31:08 -07001918void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1919 const SkPaint* paint) {
1920 RETURN_ON_NULL(image);
1921 if (dst.isEmpty()) {
1922 return;
1923 }
msarett71df2d72016-09-30 12:41:42 -07001924
1925 SkIRect bounds;
1926 Lattice latticePlusBounds = lattice;
1927 if (!latticePlusBounds.fBounds) {
1928 bounds = SkIRect::MakeWH(image->width(), image->height());
1929 latticePlusBounds.fBounds = &bounds;
1930 }
1931
1932 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
1933 this->onDrawImageLattice(image, latticePlusBounds, dst, paint);
msarett16882062016-08-16 09:31:08 -07001934 } else {
1935 this->drawImageRect(image, dst, paint);
1936 }
1937}
1938
reed41af9662015-01-05 07:49:08 -08001939void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001940 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001941 return;
1942 }
reed41af9662015-01-05 07:49:08 -08001943 this->onDrawBitmap(bitmap, dx, dy, paint);
1944}
1945
reede47829b2015-08-06 10:02:53 -07001946void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001947 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001948 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001949 return;
1950 }
reede47829b2015-08-06 10:02:53 -07001951 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001952}
1953
reed84984ef2015-07-17 07:09:43 -07001954void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1955 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001956 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001957}
1958
reede47829b2015-08-06 10:02:53 -07001959void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1960 SrcRectConstraint constraint) {
1961 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1962 constraint);
1963}
reede47829b2015-08-06 10:02:53 -07001964
reed41af9662015-01-05 07:49:08 -08001965void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1966 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001967 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001968 return;
1969 }
msarett552bca92016-08-03 06:53:26 -07001970 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
1971 this->onDrawBitmapNine(bitmap, center, dst, paint);
1972 } else {
reeda5517e22015-07-14 10:54:12 -07001973 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001974 }
reed41af9662015-01-05 07:49:08 -08001975}
1976
msarettc573a402016-08-02 08:05:56 -07001977void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
1978 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07001979 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07001980 return;
1981 }
msarett71df2d72016-09-30 12:41:42 -07001982
1983 SkIRect bounds;
1984 Lattice latticePlusBounds = lattice;
1985 if (!latticePlusBounds.fBounds) {
1986 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1987 latticePlusBounds.fBounds = &bounds;
1988 }
1989
1990 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
1991 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, paint);
msarett552bca92016-08-03 06:53:26 -07001992 } else {
msarett16882062016-08-16 09:31:08 -07001993 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07001994 }
msarettc573a402016-08-02 08:05:56 -07001995}
1996
reed71c3c762015-06-24 10:29:17 -07001997void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001998 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07001999 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002000 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002001 if (count <= 0) {
2002 return;
2003 }
2004 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002005 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002006 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002007}
2008
reedf70b5312016-03-04 16:36:20 -08002009void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2010 if (key) {
2011 this->onDrawAnnotation(rect, key, value);
2012 }
2013}
2014
reede47829b2015-08-06 10:02:53 -07002015void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2016 const SkPaint* paint, SrcRectConstraint constraint) {
2017 if (src) {
2018 this->drawImageRect(image, *src, dst, paint, constraint);
2019 } else {
2020 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2021 dst, paint, constraint);
2022 }
2023}
2024void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2025 const SkPaint* paint, SrcRectConstraint constraint) {
2026 if (src) {
2027 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2028 } else {
2029 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2030 dst, paint, constraint);
2031 }
2032}
2033
tomhudsoncb3bd182016-05-18 07:24:16 -07002034void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2035 SkIRect layer_bounds = this->getTopLayerBounds();
2036 if (matrix) {
2037 *matrix = this->getTotalMatrix();
2038 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2039 }
2040 if (clip_bounds) {
2041 this->getClipDeviceBounds(clip_bounds);
2042 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2043 }
2044}
2045
reed@android.com8a1c16f2008-12-17 15:59:43 +00002046//////////////////////////////////////////////////////////////////////////////
2047// These are the virtual drawing methods
2048//////////////////////////////////////////////////////////////////////////////
2049
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002050void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002051 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002052 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2053 }
2054}
2055
reed41af9662015-01-05 07:49:08 -08002056void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002057 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002058 this->internalDrawPaint(paint);
2059}
2060
2061void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002062 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002063
2064 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002065 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066 }
2067
reed@google.com4e2b3d32011-04-07 14:18:59 +00002068 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002069}
2070
reed41af9662015-01-05 07:49:08 -08002071void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2072 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002073 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002074 if ((long)count <= 0) {
2075 return;
2076 }
2077
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002078 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002079 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002080 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002081 // special-case 2 points (common for drawing a single line)
2082 if (2 == count) {
2083 r.set(pts[0], pts[1]);
2084 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002085 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002086 }
senorblanco87e066e2015-10-28 11:23:36 -07002087 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2088 return;
2089 }
2090 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002091 }
reed@google.coma584aed2012-05-16 14:06:02 +00002092
halcanary96fcdcc2015-08-27 07:41:13 -07002093 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002095 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002096
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002098 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002099 }
reed@google.com4b226022011-01-11 18:32:13 +00002100
reed@google.com4e2b3d32011-04-07 14:18:59 +00002101 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002102}
2103
reed4a167172016-08-18 17:15:25 -07002104static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
2105 return ((intptr_t)paint.getImageFilter() |
2106#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
2107 (intptr_t)canvas->getDrawFilter() |
2108#endif
2109 (intptr_t)paint.getLooper() ) != 0;
2110}
2111
reed41af9662015-01-05 07:49:08 -08002112void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002113 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002114 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002115 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002116 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002117 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2118 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2119 SkRect tmp(r);
2120 tmp.sort();
2121
senorblanco87e066e2015-10-28 11:23:36 -07002122 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2123 return;
2124 }
2125 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002126 }
reed@google.com4b226022011-01-11 18:32:13 +00002127
reed4a167172016-08-18 17:15:25 -07002128 if (needs_autodrawlooper(this, paint)) {
2129 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002130
reed4a167172016-08-18 17:15:25 -07002131 while (iter.next()) {
2132 iter.fDevice->drawRect(iter, r, looper.paint());
2133 }
2134
2135 LOOPER_END
2136 } else {
2137 this->predrawNotify(bounds, &paint, false);
2138 SkDrawIter iter(this);
2139 while (iter.next()) {
2140 iter.fDevice->drawRect(iter, r, paint);
2141 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002142 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002143}
2144
msarett44df6512016-08-25 13:54:30 -07002145void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
2146 SkRect storage;
2147 SkRect regionRect = SkRect::Make(region.getBounds());
2148 const SkRect* bounds = nullptr;
2149 if (paint.canComputeFastBounds()) {
2150 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2151 return;
2152 }
2153 bounds = &regionRect;
2154 }
2155
2156 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
2157
2158 while (iter.next()) {
2159 iter.fDevice->drawRegion(iter, region, looper.paint());
2160 }
2161
2162 LOOPER_END
2163}
2164
reed41af9662015-01-05 07:49:08 -08002165void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002166 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002167 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002168 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002169 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002170 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2171 return;
2172 }
2173 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002174 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002175
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002176 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002177
2178 while (iter.next()) {
2179 iter.fDevice->drawOval(iter, oval, looper.paint());
2180 }
2181
2182 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002183}
2184
bsalomonac3aa242016-08-19 11:25:19 -07002185void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2186 SkScalar sweepAngle, bool useCenter,
2187 const SkPaint& paint) {
2188 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
2189 const SkRect* bounds = nullptr;
2190 if (paint.canComputeFastBounds()) {
2191 SkRect storage;
2192 // Note we're using the entire oval as the bounds.
2193 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2194 return;
2195 }
2196 bounds = &oval;
2197 }
2198
2199 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
2200
2201 while (iter.next()) {
2202 iter.fDevice->drawArc(iter, oval, startAngle, sweepAngle, useCenter, looper.paint());
2203 }
2204
2205 LOOPER_END
2206}
2207
reed41af9662015-01-05 07:49:08 -08002208void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002209 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002210 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002211 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002212 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002213 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2214 return;
2215 }
2216 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002217 }
2218
2219 if (rrect.isRect()) {
2220 // call the non-virtual version
2221 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002222 return;
2223 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002224 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002225 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2226 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002227 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002228
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002229 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002230
2231 while (iter.next()) {
2232 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2233 }
2234
2235 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002236}
2237
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002238void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2239 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002240 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002241 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002242 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002243 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2244 return;
2245 }
2246 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002247 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002248
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002249 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002250
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002251 while (iter.next()) {
2252 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2253 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002254
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002255 LOOPER_END
2256}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002257
reed41af9662015-01-05 07:49:08 -08002258void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002259 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002260 if (!path.isFinite()) {
2261 return;
2262 }
2263
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002264 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002265 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002266 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002267 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002268 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2269 return;
2270 }
2271 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002272 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002273
2274 const SkRect& r = path.getBounds();
2275 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002276 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002277 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002278 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002279 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002280 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002282 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002283
2284 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002285 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002286 }
2287
reed@google.com4e2b3d32011-04-07 14:18:59 +00002288 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002289}
2290
reed262a71b2015-12-05 13:07:27 -08002291bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002292 if (!paint.getImageFilter()) {
2293 return false;
2294 }
2295
2296 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002297 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002298 return false;
2299 }
2300
2301 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2302 // Once we can filter and the filter will return a result larger than itself, we should be
2303 // able to remove this constraint.
2304 // skbug.com/4526
2305 //
2306 SkPoint pt;
2307 ctm.mapXY(x, y, &pt);
2308 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2309 return ir.contains(fMCRec->fRasterClip.getBounds());
2310}
2311
reeda85d4d02015-05-06 12:56:48 -07002312void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002313 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002314 SkRect bounds = SkRect::MakeXYWH(x, y,
2315 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002316 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002317 SkRect tmp = bounds;
2318 if (paint) {
2319 paint->computeFastBounds(tmp, &tmp);
2320 }
2321 if (this->quickReject(tmp)) {
2322 return;
2323 }
reeda85d4d02015-05-06 12:56:48 -07002324 }
halcanary9d524f22016-03-29 09:03:52 -07002325
reeda85d4d02015-05-06 12:56:48 -07002326 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002327 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002328 paint = lazy.init();
2329 }
reed262a71b2015-12-05 13:07:27 -08002330
reeda2217ef2016-07-20 06:04:34 -07002331 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002332 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2333 *paint);
2334 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002335 special = this->getDevice()->makeSpecial(image);
2336 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002337 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002338 }
2339 }
2340
reed262a71b2015-12-05 13:07:27 -08002341 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2342
reeda85d4d02015-05-06 12:56:48 -07002343 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002344 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002345 if (special) {
2346 SkPoint pt;
2347 iter.fMatrix->mapXY(x, y, &pt);
2348 iter.fDevice->drawSpecial(iter, special.get(),
2349 SkScalarRoundToInt(pt.fX),
2350 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002351 } else {
2352 iter.fDevice->drawImage(iter, image, x, y, pnt);
2353 }
reeda85d4d02015-05-06 12:56:48 -07002354 }
halcanary9d524f22016-03-29 09:03:52 -07002355
reeda85d4d02015-05-06 12:56:48 -07002356 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002357}
2358
reed41af9662015-01-05 07:49:08 -08002359void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002360 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002361 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002362 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002363 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002364 if (paint) {
2365 paint->computeFastBounds(dst, &storage);
2366 }
2367 if (this->quickReject(storage)) {
2368 return;
2369 }
reeda85d4d02015-05-06 12:56:48 -07002370 }
2371 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002372 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002373 paint = lazy.init();
2374 }
halcanary9d524f22016-03-29 09:03:52 -07002375
senorblancoc41e7e12015-12-07 12:51:30 -08002376 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002377 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002378
reeda85d4d02015-05-06 12:56:48 -07002379 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002380 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002381 }
halcanary9d524f22016-03-29 09:03:52 -07002382
reeda85d4d02015-05-06 12:56:48 -07002383 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002384}
2385
reed41af9662015-01-05 07:49:08 -08002386void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002387 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002388 SkDEBUGCODE(bitmap.validate();)
2389
reed33366972015-10-08 09:22:02 -07002390 if (bitmap.drawsNothing()) {
2391 return;
2392 }
2393
2394 SkLazyPaint lazy;
2395 if (nullptr == paint) {
2396 paint = lazy.init();
2397 }
2398
2399 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2400
2401 SkRect storage;
2402 const SkRect* bounds = nullptr;
2403 if (paint->canComputeFastBounds()) {
2404 bitmap.getBounds(&storage);
2405 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002406 SkRect tmp = storage;
2407 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2408 return;
2409 }
2410 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002411 }
reed@google.com4b226022011-01-11 18:32:13 +00002412
reeda2217ef2016-07-20 06:04:34 -07002413 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002414 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2415 *paint);
2416 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002417 special = this->getDevice()->makeSpecial(bitmap);
2418 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002419 drawAsSprite = false;
2420 }
2421 }
2422
reed262a71b2015-12-05 13:07:27 -08002423 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002424
2425 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002426 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002427 if (special) {
reed262a71b2015-12-05 13:07:27 -08002428 SkPoint pt;
2429 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002430 iter.fDevice->drawSpecial(iter, special.get(),
2431 SkScalarRoundToInt(pt.fX),
2432 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002433 } else {
2434 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2435 }
reed33366972015-10-08 09:22:02 -07002436 }
msarettfbfa2582016-08-12 08:29:08 -07002437
reed33366972015-10-08 09:22:02 -07002438 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002439}
2440
reed@google.com9987ec32011-09-07 11:57:52 +00002441// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002442void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002443 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002444 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002445 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002446 return;
2447 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002448
halcanary96fcdcc2015-08-27 07:41:13 -07002449 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002450 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002451 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2452 return;
2453 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002454 }
reed@google.com3d608122011-11-21 15:16:16 +00002455
reed@google.com33535f32012-09-25 15:37:50 +00002456 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002457 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002458 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002460
senorblancoc41e7e12015-12-07 12:51:30 -08002461 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002462 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002463
reed@google.com33535f32012-09-25 15:37:50 +00002464 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002465 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002466 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002467
reed@google.com33535f32012-09-25 15:37:50 +00002468 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002469}
2470
reed41af9662015-01-05 07:49:08 -08002471void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002472 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002473 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002474 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002475 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002476}
2477
reed4c21dc52015-06-25 12:32:03 -07002478void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2479 const SkPaint* paint) {
2480 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002481
halcanary96fcdcc2015-08-27 07:41:13 -07002482 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002483 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002484 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2485 return;
2486 }
reed@google.com3d608122011-11-21 15:16:16 +00002487 }
halcanary9d524f22016-03-29 09:03:52 -07002488
reed4c21dc52015-06-25 12:32:03 -07002489 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002490 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002491 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002492 }
halcanary9d524f22016-03-29 09:03:52 -07002493
senorblancoc41e7e12015-12-07 12:51:30 -08002494 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002495
reed4c21dc52015-06-25 12:32:03 -07002496 while (iter.next()) {
2497 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002498 }
halcanary9d524f22016-03-29 09:03:52 -07002499
reed4c21dc52015-06-25 12:32:03 -07002500 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002501}
2502
reed41af9662015-01-05 07:49:08 -08002503void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2504 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002505 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002506 SkDEBUGCODE(bitmap.validate();)
2507
halcanary96fcdcc2015-08-27 07:41:13 -07002508 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002509 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002510 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2511 return;
2512 }
reed4c21dc52015-06-25 12:32:03 -07002513 }
halcanary9d524f22016-03-29 09:03:52 -07002514
reed4c21dc52015-06-25 12:32:03 -07002515 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002516 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002517 paint = lazy.init();
2518 }
halcanary9d524f22016-03-29 09:03:52 -07002519
senorblancoc41e7e12015-12-07 12:51:30 -08002520 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002521
reed4c21dc52015-06-25 12:32:03 -07002522 while (iter.next()) {
2523 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2524 }
halcanary9d524f22016-03-29 09:03:52 -07002525
reed4c21dc52015-06-25 12:32:03 -07002526 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002527}
2528
msarett16882062016-08-16 09:31:08 -07002529void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2530 const SkPaint* paint) {
2531 if (nullptr == paint || paint->canComputeFastBounds()) {
2532 SkRect storage;
2533 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2534 return;
2535 }
2536 }
2537
2538 SkLazyPaint lazy;
2539 if (nullptr == paint) {
2540 paint = lazy.init();
2541 }
2542
2543 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2544
2545 while (iter.next()) {
2546 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2547 }
2548
2549 LOOPER_END
2550}
2551
2552void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2553 const SkRect& dst, const SkPaint* paint) {
2554 if (nullptr == paint || paint->canComputeFastBounds()) {
2555 SkRect storage;
2556 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2557 return;
2558 }
2559 }
2560
2561 SkLazyPaint lazy;
2562 if (nullptr == paint) {
2563 paint = lazy.init();
2564 }
2565
2566 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2567
2568 while (iter.next()) {
2569 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2570 }
2571
2572 LOOPER_END
2573}
2574
reed@google.comf67e4cf2011-03-15 20:56:58 +00002575class SkDeviceFilteredPaint {
2576public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002577 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002578 uint32_t filteredFlags = device->filterTextFlags(paint);
2579 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002580 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002581 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002582 fPaint = newPaint;
2583 } else {
2584 fPaint = &paint;
2585 }
2586 }
2587
reed@google.comf67e4cf2011-03-15 20:56:58 +00002588 const SkPaint& paint() const { return *fPaint; }
2589
2590private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002591 const SkPaint* fPaint;
2592 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002593};
2594
bungeman@google.com52c748b2011-08-22 21:30:43 +00002595void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2596 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002597 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002598 draw.fDevice->drawRect(draw, r, paint);
2599 } else {
2600 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002601 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002602 draw.fDevice->drawRect(draw, r, p);
2603 }
2604}
2605
2606void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2607 const char text[], size_t byteLength,
2608 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002609 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002610
2611 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002612 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002613 draw.fRC->isEmpty() ||
reed374772b2016-10-05 17:33:02 -07002614 (paint.getAlpha() == 0 && paint.isSrcOver())) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002615 return;
2616 }
2617
2618 SkScalar width = 0;
2619 SkPoint start;
2620
2621 start.set(0, 0); // to avoid warning
2622 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2623 SkPaint::kStrikeThruText_Flag)) {
2624 width = paint.measureText(text, byteLength);
2625
2626 SkScalar offsetX = 0;
2627 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2628 offsetX = SkScalarHalf(width);
2629 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2630 offsetX = width;
2631 }
2632 start.set(x - offsetX, y);
2633 }
2634
2635 if (0 == width) {
2636 return;
2637 }
2638
2639 uint32_t flags = paint.getFlags();
2640
2641 if (flags & (SkPaint::kUnderlineText_Flag |
2642 SkPaint::kStrikeThruText_Flag)) {
2643 SkScalar textSize = paint.getTextSize();
2644 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2645 SkRect r;
2646
2647 r.fLeft = start.fX;
2648 r.fRight = start.fX + width;
2649
2650 if (flags & SkPaint::kUnderlineText_Flag) {
2651 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2652 start.fY);
2653 r.fTop = offset;
2654 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002655 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002656 }
2657 if (flags & SkPaint::kStrikeThruText_Flag) {
2658 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2659 start.fY);
2660 r.fTop = offset;
2661 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002662 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002663 }
2664 }
2665}
2666
reed@google.come0d9ce82014-04-23 04:00:17 +00002667void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2668 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002669 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002670
2671 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002672 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002673 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002674 DrawTextDecorations(iter, dfp.paint(),
2675 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002676 }
2677
reed@google.com4e2b3d32011-04-07 14:18:59 +00002678 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002679}
2680
reed@google.come0d9ce82014-04-23 04:00:17 +00002681void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2682 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002683 SkPoint textOffset = SkPoint::Make(0, 0);
2684
halcanary96fcdcc2015-08-27 07:41:13 -07002685 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002686
reed@android.com8a1c16f2008-12-17 15:59:43 +00002687 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002688 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002689 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002690 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002691 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002692
reed@google.com4e2b3d32011-04-07 14:18:59 +00002693 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002694}
2695
reed@google.come0d9ce82014-04-23 04:00:17 +00002696void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2697 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002698
2699 SkPoint textOffset = SkPoint::Make(0, constY);
2700
halcanary96fcdcc2015-08-27 07:41:13 -07002701 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002702
reed@android.com8a1c16f2008-12-17 15:59:43 +00002703 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002704 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002705 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002706 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002707 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002708
reed@google.com4e2b3d32011-04-07 14:18:59 +00002709 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002710}
2711
reed@google.come0d9ce82014-04-23 04:00:17 +00002712void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2713 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002714 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002715
reed@android.com8a1c16f2008-12-17 15:59:43 +00002716 while (iter.next()) {
2717 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002718 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002719 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002720
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002721 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002722}
2723
reed45561a02016-07-07 12:47:17 -07002724void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2725 const SkRect* cullRect, const SkPaint& paint) {
2726 if (cullRect && this->quickReject(*cullRect)) {
2727 return;
2728 }
2729
2730 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2731
2732 while (iter.next()) {
2733 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2734 }
2735
2736 LOOPER_END
2737}
2738
fmalita00d5c2c2014-08-21 08:53:26 -07002739void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2740 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002741
fmalita85d5eb92015-03-04 11:20:12 -08002742 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002743 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002744 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002745 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002746 SkRect tmp;
2747 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2748 return;
2749 }
2750 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002751 }
2752
fmalita024f9962015-03-03 19:08:17 -08002753 // We cannot filter in the looper as we normally do, because the paint is
2754 // incomplete at this point (text-related attributes are embedded within blob run paints).
2755 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002756 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002757
fmalita85d5eb92015-03-04 11:20:12 -08002758 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002759
fmalitaaa1b9122014-08-28 14:32:24 -07002760 while (iter.next()) {
2761 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002762 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002763 }
2764
fmalitaaa1b9122014-08-28 14:32:24 -07002765 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002766
2767 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002768}
2769
reed@google.come0d9ce82014-04-23 04:00:17 +00002770// These will become non-virtual, so they always call the (virtual) onDraw... method
2771void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2772 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002773 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002774 if (byteLength) {
2775 this->onDrawText(text, byteLength, x, y, paint);
2776 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002777}
2778void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2779 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002780 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002781 if (byteLength) {
2782 this->onDrawPosText(text, byteLength, pos, paint);
2783 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002784}
2785void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2786 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002787 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002788 if (byteLength) {
2789 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2790 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002791}
2792void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2793 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002794 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002795 if (byteLength) {
2796 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2797 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002798}
reed45561a02016-07-07 12:47:17 -07002799void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2800 const SkRect* cullRect, const SkPaint& paint) {
2801 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2802 if (byteLength) {
2803 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2804 }
2805}
fmalita00d5c2c2014-08-21 08:53:26 -07002806void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2807 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002808 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002809 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002810 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002811}
reed@google.come0d9ce82014-04-23 04:00:17 +00002812
reed41af9662015-01-05 07:49:08 -08002813void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2814 const SkPoint verts[], const SkPoint texs[],
Mike Reedfaba3712016-11-03 14:45:31 -04002815 const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08002816 const uint16_t indices[], int indexCount,
2817 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002818 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002819 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002820
reed@android.com8a1c16f2008-12-17 15:59:43 +00002821 while (iter.next()) {
2822 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
Mike Reedfaba3712016-11-03 14:45:31 -04002823 colors, bmode, indices, indexCount,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002824 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002825 }
reed@google.com4b226022011-01-11 18:32:13 +00002826
reed@google.com4e2b3d32011-04-07 14:18:59 +00002827 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002828}
2829
dandovb3c9d1c2014-08-12 08:34:29 -07002830void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002831 const SkPoint texCoords[4], SkBlendMode bmode,
2832 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002833 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002834 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002835 return;
2836 }
mtklein6cfa73a2014-08-13 13:33:49 -07002837
Mike Reedfaba3712016-11-03 14:45:31 -04002838 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002839}
2840
2841void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002842 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002843 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002844 // Since a patch is always within the convex hull of the control points, we discard it when its
2845 // bounding rectangle is completely outside the current clip.
2846 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002847 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002848 if (this->quickReject(bounds)) {
2849 return;
2850 }
mtklein6cfa73a2014-08-13 13:33:49 -07002851
halcanary96fcdcc2015-08-27 07:41:13 -07002852 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002853
dandovecfff212014-08-04 10:02:00 -07002854 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002855 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002856 }
mtklein6cfa73a2014-08-13 13:33:49 -07002857
dandovecfff212014-08-04 10:02:00 -07002858 LOOPER_END
2859}
2860
reeda8db7282015-07-07 10:22:31 -07002861void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002862 RETURN_ON_NULL(dr);
2863 if (x || y) {
2864 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2865 this->onDrawDrawable(dr, &matrix);
2866 } else {
2867 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002868 }
2869}
2870
reeda8db7282015-07-07 10:22:31 -07002871void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002872 RETURN_ON_NULL(dr);
2873 if (matrix && matrix->isIdentity()) {
2874 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002875 }
reede3b38ce2016-01-08 09:18:44 -08002876 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002877}
2878
2879void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002880 // drawable bounds are no longer reliable (e.g. android displaylist)
2881 // so don't use them for quick-reject
reeda8db7282015-07-07 10:22:31 -07002882 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002883}
2884
reed71c3c762015-06-24 10:29:17 -07002885void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002886 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002887 const SkRect* cull, const SkPaint* paint) {
2888 if (cull && this->quickReject(*cull)) {
2889 return;
2890 }
2891
2892 SkPaint pnt;
2893 if (paint) {
2894 pnt = *paint;
2895 }
halcanary9d524f22016-03-29 09:03:52 -07002896
halcanary96fcdcc2015-08-27 07:41:13 -07002897 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002898 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002899 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002900 }
2901 LOOPER_END
2902}
2903
reedf70b5312016-03-04 16:36:20 -08002904void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2905 SkASSERT(key);
2906
2907 SkPaint paint;
2908 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2909 while (iter.next()) {
2910 iter.fDevice->drawAnnotation(iter, rect, key, value);
2911 }
2912 LOOPER_END
2913}
2914
reed@android.com8a1c16f2008-12-17 15:59:43 +00002915//////////////////////////////////////////////////////////////////////////////
2916// These methods are NOT virtual, and therefore must call back into virtual
2917// methods, rather than actually drawing themselves.
2918//////////////////////////////////////////////////////////////////////////////
2919
reed374772b2016-10-05 17:33:02 -07002920void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002921 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002922 SkPaint paint;
2923
2924 paint.setARGB(a, r, g, b);
reed374772b2016-10-05 17:33:02 -07002925 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002926 this->drawPaint(paint);
2927}
2928
reed374772b2016-10-05 17:33:02 -07002929void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002930 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002931 SkPaint paint;
2932
2933 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002934 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002935 this->drawPaint(paint);
2936}
2937
2938void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002939 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002940 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002941
reed@android.com8a1c16f2008-12-17 15:59:43 +00002942 pt.set(x, y);
2943 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2944}
2945
2946void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002947 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002948 SkPoint pt;
2949 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002950
reed@android.com8a1c16f2008-12-17 15:59:43 +00002951 pt.set(x, y);
2952 paint.setColor(color);
2953 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2954}
2955
2956void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2957 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002958 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002959 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002960
reed@android.com8a1c16f2008-12-17 15:59:43 +00002961 pts[0].set(x0, y0);
2962 pts[1].set(x1, y1);
2963 this->drawPoints(kLines_PointMode, 2, pts, paint);
2964}
2965
2966void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2967 SkScalar right, SkScalar bottom,
2968 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002969 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002970 SkRect r;
2971
2972 r.set(left, top, right, bottom);
2973 this->drawRect(r, paint);
2974}
2975
2976void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2977 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002978 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002979 if (radius < 0) {
2980 radius = 0;
2981 }
2982
2983 SkRect r;
2984 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002985 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002986}
2987
2988void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2989 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002990 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002991 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002992 SkRRect rrect;
2993 rrect.setRectXY(r, rx, ry);
2994 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002995 } else {
2996 this->drawRect(r, paint);
2997 }
2998}
2999
reed@android.com8a1c16f2008-12-17 15:59:43 +00003000void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3001 SkScalar sweepAngle, bool useCenter,
3002 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003003 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
bsalomon21af9ca2016-08-25 12:29:23 -07003004 if (oval.isEmpty() || !sweepAngle) {
3005 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003006 }
bsalomon21af9ca2016-08-25 12:29:23 -07003007 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003008}
3009
3010void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3011 const SkPath& path, SkScalar hOffset,
3012 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003013 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003014 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003015
reed@android.com8a1c16f2008-12-17 15:59:43 +00003016 matrix.setTranslate(hOffset, vOffset);
3017 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3018}
3019
reed@android.comf76bacf2009-05-13 14:00:33 +00003020///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003021
3022/**
3023 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3024 * against the playback cost of recursing into the subpicture to get at its actual ops.
3025 *
3026 * For now we pick a conservatively small value, though measurement (and other heuristics like
3027 * the type of ops contained) may justify changing this value.
3028 */
3029#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003030
reedd5fa1a42014-08-09 11:08:05 -07003031void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003032 RETURN_ON_NULL(picture);
3033
reed1c2c4412015-04-30 13:09:24 -07003034 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003035 if (matrix && matrix->isIdentity()) {
3036 matrix = nullptr;
3037 }
3038 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3039 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3040 picture->playback(this);
3041 } else {
3042 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003043 }
3044}
robertphillips9b14f262014-06-04 05:40:44 -07003045
reedd5fa1a42014-08-09 11:08:05 -07003046void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3047 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003048 if (!paint || paint->canComputeFastBounds()) {
3049 SkRect bounds = picture->cullRect();
3050 if (paint) {
3051 paint->computeFastBounds(bounds, &bounds);
3052 }
3053 if (matrix) {
3054 matrix->mapRect(&bounds);
3055 }
3056 if (this->quickReject(bounds)) {
3057 return;
3058 }
3059 }
3060
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003061 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003062 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003063}
3064
vjiaoblack95302da2016-07-21 10:25:54 -07003065#ifdef SK_EXPERIMENTAL_SHADOWING
3066void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3067 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003068 const SkPaint* paint,
3069 const SkShadowParams& params) {
vjiaoblack95302da2016-07-21 10:25:54 -07003070 RETURN_ON_NULL(picture);
3071
3072 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3073
vjiaoblacke6f5d562016-08-25 06:30:23 -07003074 this->onDrawShadowedPicture(picture, matrix, paint, params);
vjiaoblack95302da2016-07-21 10:25:54 -07003075}
3076
3077void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3078 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003079 const SkPaint* paint,
3080 const SkShadowParams& params) {
vjiaoblack904527d2016-08-09 09:32:09 -07003081 if (!paint || paint->canComputeFastBounds()) {
3082 SkRect bounds = picture->cullRect();
3083 if (paint) {
3084 paint->computeFastBounds(bounds, &bounds);
3085 }
3086 if (matrix) {
3087 matrix->mapRect(&bounds);
3088 }
3089 if (this->quickReject(bounds)) {
3090 return;
3091 }
3092 }
3093
3094 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3095
vjiaoblacke6f5d562016-08-25 06:30:23 -07003096 sk_sp<SkImage> povDepthMap;
3097 sk_sp<SkImage> diffuseMap;
3098
vjiaoblack904527d2016-08-09 09:32:09 -07003099 // povDepthMap
3100 {
3101 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003102 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3103 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003104 sk_sp<SkLights> povLight = builder.finish();
3105
3106 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3107 picture->cullRect().height(),
3108 kBGRA_8888_SkColorType,
3109 kOpaque_SkAlphaType);
3110
3111 // Create a new surface (that matches the backend of canvas)
3112 // to create the povDepthMap
3113 sk_sp<SkSurface> surf(this->makeSurface(info));
3114
3115 // Wrap another SPFCanvas around the surface
3116 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3117 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3118
3119 // set the depth map canvas to have the light as the user's POV
3120 depthMapCanvas->setLights(std::move(povLight));
3121
3122 depthMapCanvas->drawPicture(picture);
vjiaoblack904527d2016-08-09 09:32:09 -07003123 povDepthMap = surf->makeImageSnapshot();
3124 }
3125
3126 // diffuseMap
3127 {
3128 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3129 picture->cullRect().height(),
3130 kBGRA_8888_SkColorType,
3131 kOpaque_SkAlphaType);
3132
3133 sk_sp<SkSurface> surf(this->makeSurface(info));
3134 surf->getCanvas()->drawPicture(picture);
3135
3136 diffuseMap = surf->makeImageSnapshot();
3137 }
vjiaoblack904527d2016-08-09 09:32:09 -07003138
3139 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3140 SkShader::kClamp_TileMode);
vjiaoblack904527d2016-08-09 09:32:09 -07003141 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3142 SkShader::kClamp_TileMode);
vjiaoblackb2796fd2016-09-09 09:22:39 -07003143
3144 // TODO: pass the depth to the shader in vertices, or uniforms
3145 // so we don't have to render depth and color separately
3146 for (int i = 0; i < fLights->numLights(); ++i) {
3147 // skip over ambient lights; they don't cast shadows
3148 // lights that have shadow maps do not need updating (because lights are immutable)
3149 sk_sp<SkImage> depthMap;
3150 SkISize shMapSize;
3151
3152 if (fLights->light(i).getShadowMap() != nullptr) {
3153 continue;
3154 }
3155
3156 if (fLights->light(i).isRadial()) {
3157 shMapSize.fHeight = 1;
3158 shMapSize.fWidth = (int) picture->cullRect().width();
3159
3160 SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1,
3161 kBGRA_8888_SkColorType,
3162 kOpaque_SkAlphaType);
3163
3164 // Create new surface (that matches the backend of canvas)
3165 // for each shadow map
3166 sk_sp<SkSurface> surf(this->makeSurface(info));
3167
3168 // Wrap another SPFCanvas around the surface
3169 SkCanvas* depthMapCanvas = surf->getCanvas();
3170
3171 SkLights::Builder builder;
3172 builder.add(fLights->light(i));
3173 sk_sp<SkLights> curLight = builder.finish();
3174
3175 sk_sp<SkShader> shadowMapShader;
3176 shadowMapShader = SkRadialShadowMapShader::Make(
3177 povDepthShader, curLight,
3178 (int) picture->cullRect().width(),
3179 (int) picture->cullRect().height());
3180
3181 SkPaint shadowMapPaint;
3182 shadowMapPaint.setShader(std::move(shadowMapShader));
3183
3184 depthMapCanvas->setLights(curLight);
3185
3186 depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(),
3187 diffuseMap->height()),
3188 shadowMapPaint);
3189
3190 depthMap = surf->makeImageSnapshot();
3191
3192 } else {
3193 // TODO: compute the correct size of the depth map from the light properties
3194 // TODO: maybe add a kDepth_8_SkColorType
3195 // TODO: find actual max depth of picture
3196 shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3197 fLights->light(i), 255,
3198 (int) picture->cullRect().width(),
3199 (int) picture->cullRect().height());
3200
3201 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3202 kBGRA_8888_SkColorType,
3203 kOpaque_SkAlphaType);
3204
3205 // Create a new surface (that matches the backend of canvas)
3206 // for each shadow map
3207 sk_sp<SkSurface> surf(this->makeSurface(info));
3208
3209 // Wrap another SPFCanvas around the surface
3210 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3211 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3212 depthMapCanvas->setShadowParams(params);
3213
3214 // set the depth map canvas to have the light we're drawing.
3215 SkLights::Builder builder;
3216 builder.add(fLights->light(i));
3217 sk_sp<SkLights> curLight = builder.finish();
3218 depthMapCanvas->setLights(std::move(curLight));
3219
3220 depthMapCanvas->drawPicture(picture);
3221 depthMap = surf->makeImageSnapshot();
3222 }
3223
3224 if (params.fType == SkShadowParams::kNoBlur_ShadowType) {
3225 fLights->light(i).setShadowMap(std::move(depthMap));
3226 } else if (params.fType == SkShadowParams::kVariance_ShadowType) {
3227 // we blur the variance map
3228 SkPaint blurPaint;
3229 blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius,
3230 params.fShadowRadius, nullptr));
3231
3232 SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3233 kBGRA_8888_SkColorType,
3234 kOpaque_SkAlphaType);
3235
3236 sk_sp<SkSurface> blurSurf(this->makeSurface(blurInfo));
3237
3238 blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
3239
3240 fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
3241 }
3242 }
3243
3244 SkPaint shadowPaint;
vjiaoblack904527d2016-08-09 09:32:09 -07003245 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3246 std::move(diffuseShader),
vjiaoblackb2796fd2016-09-09 09:22:39 -07003247 fLights,
vjiaoblack904527d2016-08-09 09:32:09 -07003248 diffuseMap->width(),
vjiaoblacke6f5d562016-08-25 06:30:23 -07003249 diffuseMap->height(),
3250 params);
vjiaoblack904527d2016-08-09 09:32:09 -07003251
3252 shadowPaint.setShader(shadowShader);
3253
3254 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003255}
3256#endif
3257
reed@android.com8a1c16f2008-12-17 15:59:43 +00003258///////////////////////////////////////////////////////////////////////////////
3259///////////////////////////////////////////////////////////////////////////////
3260
reed3aafe112016-08-18 12:45:34 -07003261SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003262 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003263
3264 SkASSERT(canvas);
3265
reed3aafe112016-08-18 12:45:34 -07003266 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003267 fDone = !fImpl->next();
3268}
3269
3270SkCanvas::LayerIter::~LayerIter() {
3271 fImpl->~SkDrawIter();
3272}
3273
3274void SkCanvas::LayerIter::next() {
3275 fDone = !fImpl->next();
3276}
3277
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003278SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003279 return fImpl->getDevice();
3280}
3281
3282const SkMatrix& SkCanvas::LayerIter::matrix() const {
3283 return fImpl->getMatrix();
3284}
3285
3286const SkPaint& SkCanvas::LayerIter::paint() const {
3287 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003288 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003289 paint = &fDefaultPaint;
3290 }
3291 return *paint;
3292}
3293
reed1e7f5e72016-04-27 07:49:17 -07003294const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003295int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3296int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003297
3298///////////////////////////////////////////////////////////////////////////////
3299
fmalitac3b589a2014-06-05 12:40:07 -07003300SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003301
3302///////////////////////////////////////////////////////////////////////////////
3303
3304static bool supported_for_raster_canvas(const SkImageInfo& info) {
3305 switch (info.alphaType()) {
3306 case kPremul_SkAlphaType:
3307 case kOpaque_SkAlphaType:
3308 break;
3309 default:
3310 return false;
3311 }
3312
3313 switch (info.colorType()) {
3314 case kAlpha_8_SkColorType:
3315 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003316 case kN32_SkColorType:
junov9e3dbdf2016-10-13 13:14:27 -07003317 case kRGBA_F16_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003318 break;
3319 default:
3320 return false;
3321 }
3322
3323 return true;
3324}
3325
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003326SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3327 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003328 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003329 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003330
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003331 SkBitmap bitmap;
3332 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003333 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003334 }
halcanary385fe4d2015-08-26 13:07:48 -07003335 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003336}
reedd5fa1a42014-08-09 11:08:05 -07003337
3338///////////////////////////////////////////////////////////////////////////////
3339
3340SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003341 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003342 : fCanvas(canvas)
3343 , fSaveCount(canvas->getSaveCount())
3344{
bsalomon49f085d2014-09-05 13:34:00 -07003345 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003346 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003347 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003348 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003349 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003350 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003351 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003352 canvas->save();
3353 }
mtklein6cfa73a2014-08-13 13:33:49 -07003354
bsalomon49f085d2014-09-05 13:34:00 -07003355 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003356 canvas->concat(*matrix);
3357 }
3358}
3359
3360SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3361 fCanvas->restoreToCount(fSaveCount);
3362}
reede8f30622016-03-23 18:59:25 -07003363
reed73603f32016-09-20 08:42:38 -07003364/////////////////////////////////
3365
3366const SkCanvas::ClipOp SkCanvas::kDifference_Op;
3367const SkCanvas::ClipOp SkCanvas::kIntersect_Op;
3368const SkCanvas::ClipOp SkCanvas::kUnion_Op;
3369const SkCanvas::ClipOp SkCanvas::kXOR_Op;
3370const SkCanvas::ClipOp SkCanvas::kReverseDifference_Op;
3371const SkCanvas::ClipOp SkCanvas::kReplace_Op;
3372
3373static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3374static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3375static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3376static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3377static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3378static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");