blob: f4896cb12b49c5e41653050b413a55482a4041ca [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
halcanary385fe4d2015-08-26 13:07:48 -0700765 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700766 this->init(device, kDefault_InitFlags);
767}
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
halcanary385fe4d2015-08-26 13:07:48 -0700776 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700777 this->init(device, 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
robertphillips5139e502016-07-19 05:10:40 -07001251 SkAutoTUnref<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
robertphillips5139e502016-07-19 05:10:40 -07001265 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001266
1267 layer->fNext = fMCRec->fTopLayer;
1268 fMCRec->fLayer = layer;
1269 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001270
1271 if (rec.fBackdrop) {
1272 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice,
1273 fMCRec->fMatrix, this->getClipStack());
1274 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001275}
1276
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001277int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001278 if (0xFF == alpha) {
1279 return this->saveLayer(bounds, nullptr);
1280 } else {
1281 SkPaint tmpPaint;
1282 tmpPaint.setAlpha(alpha);
1283 return this->saveLayer(bounds, &tmpPaint);
1284 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001285}
1286
reed@android.com8a1c16f2008-12-17 15:59:43 +00001287void SkCanvas::internalRestore() {
1288 SkASSERT(fMCStack.count() != 0);
1289
1290 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001291
reed687fa1c2015-04-07 08:00:56 -07001292 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001293
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001294 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001295 DeviceCM* layer = fMCRec->fLayer; // may be null
1296 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001297 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001298
1299 // now do the normal restore()
1300 fMCRec->~MCRec(); // balanced in save()
1301 fMCStack.pop_back();
1302 fMCRec = (MCRec*)fMCStack.back();
1303
1304 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1305 since if we're being recorded, we don't want to record this (the
1306 recorder will have already recorded the restore).
1307 */
bsalomon49f085d2014-09-05 13:34:00 -07001308 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001309 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001310 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001311 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001312 // restore what we smashed in internalSaveLayer
1313 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001314 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001315 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001316 delete layer;
reedb679ca82015-04-07 04:40:48 -07001317 } else {
1318 // we're at the root
reeda499f902015-05-01 09:34:31 -07001319 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001320 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001321 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001322 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001323 }
msarettfbfa2582016-08-12 08:29:08 -07001324
1325 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001326 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001327 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1328 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001329}
1330
reede8f30622016-03-23 18:59:25 -07001331sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001332 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001333 props = &fProps;
1334 }
1335 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001336}
1337
reede8f30622016-03-23 18:59:25 -07001338sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001339 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001340 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001341}
1342
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001343SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001344 return this->onImageInfo();
1345}
1346
1347SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001348 SkBaseDevice* dev = this->getDevice();
1349 if (dev) {
1350 return dev->imageInfo();
1351 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001352 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001353 }
1354}
1355
brianosman898235c2016-04-06 07:38:23 -07001356bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001357 return this->onGetProps(props);
1358}
1359
1360bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001361 SkBaseDevice* dev = this->getDevice();
1362 if (dev) {
1363 if (props) {
1364 *props = fProps;
1365 }
1366 return true;
1367 } else {
1368 return false;
1369 }
1370}
1371
reed6ceeebd2016-03-09 14:26:26 -08001372bool SkCanvas::peekPixels(SkPixmap* pmap) {
1373 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001374}
1375
reed884e97c2015-05-26 11:31:54 -07001376bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001377 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001378 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001379}
1380
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001381void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001382 SkPixmap pmap;
1383 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001384 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001385 }
1386 if (info) {
1387 *info = pmap.info();
1388 }
1389 if (rowBytes) {
1390 *rowBytes = pmap.rowBytes();
1391 }
1392 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001393 *origin = this->getTopDevice(false)->getOrigin();
1394 }
reed884e97c2015-05-26 11:31:54 -07001395 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001396}
1397
reed884e97c2015-05-26 11:31:54 -07001398bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001399 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001400 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001401}
1402
reed@android.com8a1c16f2008-12-17 15:59:43 +00001403/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001404
reed7503d602016-07-15 14:23:29 -07001405void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001406 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001407 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001408 paint = &tmp;
1409 }
reed@google.com4b226022011-01-11 18:32:13 +00001410
reed@google.com8926b162012-03-23 15:36:36 +00001411 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001412
reed@android.com8a1c16f2008-12-17 15:59:43 +00001413 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001414 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001415 paint = &looper.paint();
1416 SkImageFilter* filter = paint->getImageFilter();
1417 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001418 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001419 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001420 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001421 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001422 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001423 }
reeda2217ef2016-07-20 06:04:34 -07001424
reed@google.com4e2b3d32011-04-07 14:18:59 +00001425 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001426}
1427
reed32704672015-12-16 08:27:10 -08001428/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001429
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001430void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001431 if (dx || dy) {
1432 this->checkForDeferredSave();
1433 fDeviceCMDirty = true;
1434 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001435
reedfe69b502016-09-12 06:31:48 -07001436 // Translate shouldn't affect the is-scale-translateness of the matrix.
1437 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001438
reedfe69b502016-09-12 06:31:48 -07001439 this->didTranslate(dx,dy);
1440 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001441}
1442
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001443void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001444 SkMatrix m;
1445 m.setScale(sx, sy);
1446 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447}
1448
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001449void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001450 SkMatrix m;
1451 m.setRotate(degrees);
1452 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001453}
1454
bungeman7438bfc2016-07-12 15:01:19 -07001455void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1456 SkMatrix m;
1457 m.setRotate(degrees, px, py);
1458 this->concat(m);
1459}
1460
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001461void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001462 SkMatrix m;
1463 m.setSkew(sx, sy);
1464 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001465}
1466
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001467void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001468 if (matrix.isIdentity()) {
1469 return;
1470 }
1471
reed2ff1fce2014-12-11 07:07:37 -08001472 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001473 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001474 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001475 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001476 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001477}
1478
reed8c30a812016-04-20 16:36:51 -07001479void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001480 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001481 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001482 fIsScaleTranslate = matrix.isScaleTranslate();
reed8c30a812016-04-20 16:36:51 -07001483}
1484
1485void SkCanvas::setMatrix(const SkMatrix& matrix) {
1486 this->checkForDeferredSave();
1487 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001488 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489}
1490
reed@android.com8a1c16f2008-12-17 15:59:43 +00001491void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001492 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001493}
1494
vjiaoblack95302da2016-07-21 10:25:54 -07001495#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001496void SkCanvas::translateZ(SkScalar z) {
1497 this->checkForDeferredSave();
1498 this->fMCRec->fCurDrawDepth += z;
1499 this->didTranslateZ(z);
1500}
1501
1502SkScalar SkCanvas::getZ() const {
1503 return this->fMCRec->fCurDrawDepth;
1504}
1505
vjiaoblack95302da2016-07-21 10:25:54 -07001506void SkCanvas::setLights(sk_sp<SkLights> lights) {
1507 this->fLights = lights;
1508}
1509
1510sk_sp<SkLights> SkCanvas::getLights() const {
1511 return this->fLights;
1512}
1513#endif
1514
reed@android.com8a1c16f2008-12-17 15:59:43 +00001515//////////////////////////////////////////////////////////////////////////////
1516
reed73603f32016-09-20 08:42:38 -07001517void SkCanvas::clipRect(const SkRect& rect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001518 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001519 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1520 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001521}
1522
reed73603f32016-09-20 08:42:38 -07001523void SkCanvas::onClipRect(const SkRect& rect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001524 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reedc64eff52015-11-21 12:39:45 -08001525 AutoValidateClip avc(this);
Brian Salomona3b45d42016-10-03 11:36:16 -04001526 fClipStack->clipRect(rect, fMCRec->fMatrix, op, isAA);
1527 fMCRec->fRasterClip.op(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1528 isAA);
reedc64eff52015-11-21 12:39:45 -08001529 fDeviceCMDirty = true;
msarettfbfa2582016-08-12 08:29:08 -07001530 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531}
1532
reed73603f32016-09-20 08:42:38 -07001533void SkCanvas::clipRRect(const SkRRect& rrect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001534 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001535 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001536 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001537 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1538 } else {
1539 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001540 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001541}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001542
reed73603f32016-09-20 08:42:38 -07001543void SkCanvas::onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001544 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001545
Brian Salomona3b45d42016-10-03 11:36:16 -04001546 fDeviceCMDirty = true;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001547
Brian Salomona3b45d42016-10-03 11:36:16 -04001548 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1549 fClipStack->clipRRect(rrect, fMCRec->fMatrix, op, isAA);
1550 fMCRec->fRasterClip.op(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1551 isAA);
1552 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1553 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001554}
1555
reed73603f32016-09-20 08:42:38 -07001556void SkCanvas::clipPath(const SkPath& path, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001557 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001558 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001559
1560 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1561 SkRect r;
1562 if (path.isRect(&r)) {
1563 this->onClipRect(r, op, edgeStyle);
1564 return;
1565 }
1566 SkRRect rrect;
1567 if (path.isOval(&r)) {
1568 rrect.setOval(r);
1569 this->onClipRRect(rrect, op, edgeStyle);
1570 return;
1571 }
1572 if (path.isRRect(&rrect)) {
1573 this->onClipRRect(rrect, op, edgeStyle);
1574 return;
1575 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001576 }
robertphillips39f05382015-11-24 09:30:12 -08001577
1578 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001579}
1580
reed73603f32016-09-20 08:42:38 -07001581void SkCanvas::onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001582 AutoValidateClip avc(this);
1583
reed@android.com8a1c16f2008-12-17 15:59:43 +00001584 fDeviceCMDirty = true;
Brian Salomona3b45d42016-10-03 11:36:16 -04001585 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001586
Brian Salomona3b45d42016-10-03 11:36:16 -04001587 fClipStack->clipPath(path, fMCRec->fMatrix, op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001588
Brian Salomona3b45d42016-10-03 11:36:16 -04001589 const SkPath* rasterClipPath = &path;
1590 const SkMatrix* matrix = &fMCRec->fMatrix;
1591 SkPath tempPath;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001592 if (fAllowSimplifyClip) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001593 isAA = getClipStack()->asPath(&tempPath);
1594 rasterClipPath = &tempPath;
1595 matrix = &SkMatrix::I();
reed73603f32016-09-20 08:42:38 -07001596 op = kReplace_Op;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001597 }
Brian Salomona3b45d42016-10-03 11:36:16 -04001598 fMCRec->fRasterClip.op(*rasterClipPath, *matrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1599 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001600 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001601}
1602
reed73603f32016-09-20 08:42:38 -07001603void SkCanvas::clipRegion(const SkRegion& rgn, ClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001604 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001605 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001606}
1607
reed73603f32016-09-20 08:42:38 -07001608void SkCanvas::onClipRegion(const SkRegion& rgn, ClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001609 AutoValidateClip avc(this);
1610
reed@android.com8a1c16f2008-12-17 15:59:43 +00001611 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001612
reed@google.com5c3d1472011-02-22 19:12:23 +00001613 // todo: signal fClipStack that we have a region, and therefore (I guess)
1614 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001615 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001616
reed73603f32016-09-20 08:42:38 -07001617 fMCRec->fRasterClip.op(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001618 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001619}
1620
reed@google.com819c9212011-02-23 18:56:55 +00001621#ifdef SK_DEBUG
1622void SkCanvas::validateClip() const {
1623 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001624 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001625 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001626 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001627 return;
1628 }
1629
reed@google.com819c9212011-02-23 18:56:55 +00001630 SkIRect ir;
1631 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001632 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001633
reed687fa1c2015-04-07 08:00:56 -07001634 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001635 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001636 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001637 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001638 case SkClipStack::Element::kRect_Type:
1639 element->getRect().round(&ir);
reed73603f32016-09-20 08:42:38 -07001640 tmpClip.op(ir, (SkRegion::Op)element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001641 break;
1642 case SkClipStack::Element::kEmpty_Type:
1643 tmpClip.setEmpty();
1644 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001645 default: {
1646 SkPath path;
1647 element->asPath(&path);
Brian Salomona3b45d42016-10-03 11:36:16 -04001648 tmpClip.op(path, SkMatrix::I(), this->getTopLayerBounds(),
1649 (SkRegion::Op)element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001650 break;
1651 }
reed@google.com819c9212011-02-23 18:56:55 +00001652 }
1653 }
reed@google.com819c9212011-02-23 18:56:55 +00001654}
1655#endif
1656
reed@google.com90c07ea2012-04-13 13:50:27 +00001657void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001658 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001659 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001660
halcanary96fcdcc2015-08-27 07:41:13 -07001661 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001662 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001663 }
1664}
1665
reed@google.com5c3d1472011-02-22 19:12:23 +00001666///////////////////////////////////////////////////////////////////////////////
1667
reed@google.com754de5f2014-02-24 19:38:20 +00001668bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001669 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001670}
1671
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001672bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001673 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001674}
1675
msarettfbfa2582016-08-12 08:29:08 -07001676static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1677#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1678 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1679 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1680 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1681 return 0xF != _mm_movemask_ps(mask);
1682#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1683 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1684 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1685 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1686 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1687#else
1688 SkRect devRectAsRect;
1689 SkRect devClipAsRect;
1690 devRect.store(&devRectAsRect.fLeft);
1691 devClip.store(&devClipAsRect.fLeft);
1692 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1693#endif
1694}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001695
msarettfbfa2582016-08-12 08:29:08 -07001696// It's important for this function to not be inlined. Otherwise the compiler will share code
1697// between the fast path and the slow path, resulting in two slow paths.
1698static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1699 const SkMatrix& matrix) {
1700 SkRect deviceRect;
1701 matrix.mapRect(&deviceRect, src);
1702 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1703}
1704
1705bool SkCanvas::quickReject(const SkRect& src) const {
1706#ifdef SK_DEBUG
1707 // Verify that fDeviceClipBounds are set properly.
1708 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001709 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001710 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001711 } else {
msarettfbfa2582016-08-12 08:29:08 -07001712 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001713 }
msarettfbfa2582016-08-12 08:29:08 -07001714
msarett9637ea92016-08-18 14:03:30 -07001715 // Verify that fIsScaleTranslate is set properly.
1716 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001717#endif
1718
msarett9637ea92016-08-18 14:03:30 -07001719 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001720 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1721 }
1722
1723 // We inline the implementation of mapScaleTranslate() for the fast path.
1724 float sx = fMCRec->fMatrix.getScaleX();
1725 float sy = fMCRec->fMatrix.getScaleY();
1726 float tx = fMCRec->fMatrix.getTranslateX();
1727 float ty = fMCRec->fMatrix.getTranslateY();
1728 Sk4f scale(sx, sy, sx, sy);
1729 Sk4f trans(tx, ty, tx, ty);
1730
1731 // Apply matrix.
1732 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1733
1734 // Make sure left < right, top < bottom.
1735 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1736 Sk4f min = Sk4f::Min(ltrb, rblt);
1737 Sk4f max = Sk4f::Max(ltrb, rblt);
1738 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1739 // ARM this sequence generates the fastest (a single instruction).
1740 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1741
1742 // Check if the device rect is NaN or outside the clip.
1743 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001744}
1745
reed@google.com3b3e8952012-08-16 20:53:31 +00001746bool SkCanvas::quickReject(const SkPath& path) const {
1747 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001748}
1749
reed@google.com3b3e8952012-08-16 20:53:31 +00001750bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001751 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001752 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001753 return false;
1754 }
1755
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001756 SkMatrix inverse;
1757 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001758 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001759 if (bounds) {
1760 bounds->setEmpty();
1761 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001762 return false;
1763 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764
bsalomon49f085d2014-09-05 13:34:00 -07001765 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001766 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001767 // adjust it outwards in case we are antialiasing
1768 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001769
reed@google.com8f4d2302013-12-17 16:44:46 +00001770 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1771 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001772 inverse.mapRect(bounds, r);
1773 }
1774 return true;
1775}
1776
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001777bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001778 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001779 if (clip.isEmpty()) {
1780 if (bounds) {
1781 bounds->setEmpty();
1782 }
1783 return false;
1784 }
1785
bsalomon49f085d2014-09-05 13:34:00 -07001786 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001787 *bounds = clip.getBounds();
1788 }
1789 return true;
1790}
1791
reed@android.com8a1c16f2008-12-17 15:59:43 +00001792const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001793 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001794}
1795
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001796const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001797 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001798}
1799
Brian Osman11052242016-10-27 14:47:55 -04001800GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001801 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001802 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001803}
1804
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001805GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001806 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001807 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001808}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001809
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001810void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1811 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001812 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001813 if (outer.isEmpty()) {
1814 return;
1815 }
1816 if (inner.isEmpty()) {
1817 this->drawRRect(outer, paint);
1818 return;
1819 }
1820
1821 // We don't have this method (yet), but technically this is what we should
1822 // be able to assert...
1823 // SkASSERT(outer.contains(inner));
1824 //
1825 // For now at least check for containment of bounds
1826 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1827
1828 this->onDrawDRRect(outer, inner, paint);
1829}
1830
reed41af9662015-01-05 07:49:08 -08001831// These need to stop being virtual -- clients need to override the onDraw... versions
1832
1833void SkCanvas::drawPaint(const SkPaint& paint) {
1834 this->onDrawPaint(paint);
1835}
1836
1837void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1838 this->onDrawRect(r, paint);
1839}
1840
msarettdca352e2016-08-26 06:37:45 -07001841void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
1842 if (region.isEmpty()) {
1843 return;
1844 }
1845
1846 if (region.isRect()) {
1847 return this->drawIRect(region.getBounds(), paint);
1848 }
1849
1850 this->onDrawRegion(region, paint);
1851}
1852
reed41af9662015-01-05 07:49:08 -08001853void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1854 this->onDrawOval(r, paint);
1855}
1856
1857void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1858 this->onDrawRRect(rrect, paint);
1859}
1860
1861void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1862 this->onDrawPoints(mode, count, pts, paint);
1863}
1864
1865void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001866 const SkPoint texs[], const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08001867 const uint16_t indices[], int indexCount, const SkPaint& paint) {
Mike Reedfaba3712016-11-03 14:45:31 -04001868 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, bmode,
reed41af9662015-01-05 07:49:08 -08001869 indices, indexCount, paint);
1870}
1871
1872void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1873 this->onDrawPath(path, paint);
1874}
1875
reeda85d4d02015-05-06 12:56:48 -07001876void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001877 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001878 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001879}
1880
reede47829b2015-08-06 10:02:53 -07001881void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1882 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001883 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001884 if (dst.isEmpty() || src.isEmpty()) {
1885 return;
1886 }
1887 this->onDrawImageRect(image, &src, dst, paint, constraint);
1888}
reed41af9662015-01-05 07:49:08 -08001889
reed84984ef2015-07-17 07:09:43 -07001890void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1891 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001892 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001893 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001894}
1895
reede47829b2015-08-06 10:02:53 -07001896void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1897 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001898 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001899 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1900 constraint);
1901}
reede47829b2015-08-06 10:02:53 -07001902
reed4c21dc52015-06-25 12:32:03 -07001903void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1904 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001905 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001906 if (dst.isEmpty()) {
1907 return;
1908 }
msarett552bca92016-08-03 06:53:26 -07001909 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1910 this->onDrawImageNine(image, center, dst, paint);
1911 } else {
reede47829b2015-08-06 10:02:53 -07001912 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001913 }
reed4c21dc52015-06-25 12:32:03 -07001914}
1915
msarett16882062016-08-16 09:31:08 -07001916void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1917 const SkPaint* paint) {
1918 RETURN_ON_NULL(image);
1919 if (dst.isEmpty()) {
1920 return;
1921 }
msarett71df2d72016-09-30 12:41:42 -07001922
1923 SkIRect bounds;
1924 Lattice latticePlusBounds = lattice;
1925 if (!latticePlusBounds.fBounds) {
1926 bounds = SkIRect::MakeWH(image->width(), image->height());
1927 latticePlusBounds.fBounds = &bounds;
1928 }
1929
1930 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
1931 this->onDrawImageLattice(image, latticePlusBounds, dst, paint);
msarett16882062016-08-16 09:31:08 -07001932 } else {
1933 this->drawImageRect(image, dst, paint);
1934 }
1935}
1936
reed41af9662015-01-05 07:49:08 -08001937void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001938 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001939 return;
1940 }
reed41af9662015-01-05 07:49:08 -08001941 this->onDrawBitmap(bitmap, dx, dy, paint);
1942}
1943
reede47829b2015-08-06 10:02:53 -07001944void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001945 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001946 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001947 return;
1948 }
reede47829b2015-08-06 10:02:53 -07001949 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001950}
1951
reed84984ef2015-07-17 07:09:43 -07001952void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1953 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001954 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001955}
1956
reede47829b2015-08-06 10:02:53 -07001957void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1958 SrcRectConstraint constraint) {
1959 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1960 constraint);
1961}
reede47829b2015-08-06 10:02:53 -07001962
reed41af9662015-01-05 07:49:08 -08001963void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1964 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001965 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001966 return;
1967 }
msarett552bca92016-08-03 06:53:26 -07001968 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
1969 this->onDrawBitmapNine(bitmap, center, dst, paint);
1970 } else {
reeda5517e22015-07-14 10:54:12 -07001971 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001972 }
reed41af9662015-01-05 07:49:08 -08001973}
1974
msarettc573a402016-08-02 08:05:56 -07001975void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
1976 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07001977 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07001978 return;
1979 }
msarett71df2d72016-09-30 12:41:42 -07001980
1981 SkIRect bounds;
1982 Lattice latticePlusBounds = lattice;
1983 if (!latticePlusBounds.fBounds) {
1984 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
1985 latticePlusBounds.fBounds = &bounds;
1986 }
1987
1988 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
1989 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, paint);
msarett552bca92016-08-03 06:53:26 -07001990 } else {
msarett16882062016-08-16 09:31:08 -07001991 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07001992 }
msarettc573a402016-08-02 08:05:56 -07001993}
1994
reed71c3c762015-06-24 10:29:17 -07001995void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reed7d954ad2016-10-28 15:42:34 -04001996 const SkColor colors[], int count, SkBlendMode mode,
reed71c3c762015-06-24 10:29:17 -07001997 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001998 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07001999 if (count <= 0) {
2000 return;
2001 }
2002 SkASSERT(atlas);
reed71c3c762015-06-24 10:29:17 -07002003 SkASSERT(tex);
Mike Reedfaba3712016-11-03 14:45:31 -04002004 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
reed71c3c762015-06-24 10:29:17 -07002005}
2006
reedf70b5312016-03-04 16:36:20 -08002007void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2008 if (key) {
2009 this->onDrawAnnotation(rect, key, value);
2010 }
2011}
2012
reede47829b2015-08-06 10:02:53 -07002013void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2014 const SkPaint* paint, SrcRectConstraint constraint) {
2015 if (src) {
2016 this->drawImageRect(image, *src, dst, paint, constraint);
2017 } else {
2018 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2019 dst, paint, constraint);
2020 }
2021}
2022void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2023 const SkPaint* paint, SrcRectConstraint constraint) {
2024 if (src) {
2025 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2026 } else {
2027 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2028 dst, paint, constraint);
2029 }
2030}
2031
tomhudsoncb3bd182016-05-18 07:24:16 -07002032void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2033 SkIRect layer_bounds = this->getTopLayerBounds();
2034 if (matrix) {
2035 *matrix = this->getTotalMatrix();
2036 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2037 }
2038 if (clip_bounds) {
2039 this->getClipDeviceBounds(clip_bounds);
2040 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2041 }
2042}
2043
reed@android.com8a1c16f2008-12-17 15:59:43 +00002044//////////////////////////////////////////////////////////////////////////////
2045// These are the virtual drawing methods
2046//////////////////////////////////////////////////////////////////////////////
2047
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002048void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002049 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002050 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2051 }
2052}
2053
reed41af9662015-01-05 07:49:08 -08002054void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002055 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002056 this->internalDrawPaint(paint);
2057}
2058
2059void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002060 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061
2062 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002063 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002064 }
2065
reed@google.com4e2b3d32011-04-07 14:18:59 +00002066 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002067}
2068
reed41af9662015-01-05 07:49:08 -08002069void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2070 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002071 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002072 if ((long)count <= 0) {
2073 return;
2074 }
2075
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002076 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002077 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002078 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002079 // special-case 2 points (common for drawing a single line)
2080 if (2 == count) {
2081 r.set(pts[0], pts[1]);
2082 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002083 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002084 }
senorblanco87e066e2015-10-28 11:23:36 -07002085 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2086 return;
2087 }
2088 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002089 }
reed@google.coma584aed2012-05-16 14:06:02 +00002090
halcanary96fcdcc2015-08-27 07:41:13 -07002091 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002092
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002093 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002094
reed@android.com8a1c16f2008-12-17 15:59:43 +00002095 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002096 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002097 }
reed@google.com4b226022011-01-11 18:32:13 +00002098
reed@google.com4e2b3d32011-04-07 14:18:59 +00002099 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002100}
2101
reed4a167172016-08-18 17:15:25 -07002102static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
2103 return ((intptr_t)paint.getImageFilter() |
2104#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
2105 (intptr_t)canvas->getDrawFilter() |
2106#endif
2107 (intptr_t)paint.getLooper() ) != 0;
2108}
2109
reed41af9662015-01-05 07:49:08 -08002110void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002111 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002112 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002113 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002115 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2116 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2117 SkRect tmp(r);
2118 tmp.sort();
2119
senorblanco87e066e2015-10-28 11:23:36 -07002120 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2121 return;
2122 }
2123 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002124 }
reed@google.com4b226022011-01-11 18:32:13 +00002125
reed4a167172016-08-18 17:15:25 -07002126 if (needs_autodrawlooper(this, paint)) {
2127 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002128
reed4a167172016-08-18 17:15:25 -07002129 while (iter.next()) {
2130 iter.fDevice->drawRect(iter, r, looper.paint());
2131 }
2132
2133 LOOPER_END
2134 } else {
2135 this->predrawNotify(bounds, &paint, false);
2136 SkDrawIter iter(this);
2137 while (iter.next()) {
2138 iter.fDevice->drawRect(iter, r, paint);
2139 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002140 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141}
2142
msarett44df6512016-08-25 13:54:30 -07002143void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
2144 SkRect storage;
2145 SkRect regionRect = SkRect::Make(region.getBounds());
2146 const SkRect* bounds = nullptr;
2147 if (paint.canComputeFastBounds()) {
2148 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2149 return;
2150 }
2151 bounds = &regionRect;
2152 }
2153
2154 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
2155
2156 while (iter.next()) {
2157 iter.fDevice->drawRegion(iter, region, looper.paint());
2158 }
2159
2160 LOOPER_END
2161}
2162
reed41af9662015-01-05 07:49:08 -08002163void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002164 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002165 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002166 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002167 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002168 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2169 return;
2170 }
2171 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002172 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002173
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002174 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002175
2176 while (iter.next()) {
2177 iter.fDevice->drawOval(iter, oval, looper.paint());
2178 }
2179
2180 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002181}
2182
bsalomonac3aa242016-08-19 11:25:19 -07002183void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2184 SkScalar sweepAngle, bool useCenter,
2185 const SkPaint& paint) {
2186 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
2187 const SkRect* bounds = nullptr;
2188 if (paint.canComputeFastBounds()) {
2189 SkRect storage;
2190 // Note we're using the entire oval as the bounds.
2191 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2192 return;
2193 }
2194 bounds = &oval;
2195 }
2196
2197 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
2198
2199 while (iter.next()) {
2200 iter.fDevice->drawArc(iter, oval, startAngle, sweepAngle, useCenter, looper.paint());
2201 }
2202
2203 LOOPER_END
2204}
2205
reed41af9662015-01-05 07:49:08 -08002206void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002207 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002208 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002209 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002210 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002211 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2212 return;
2213 }
2214 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002215 }
2216
2217 if (rrect.isRect()) {
2218 // call the non-virtual version
2219 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002220 return;
2221 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002222 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002223 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2224 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002225 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002226
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002227 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002228
2229 while (iter.next()) {
2230 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2231 }
2232
2233 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002234}
2235
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002236void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2237 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002238 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002239 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002240 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002241 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2242 return;
2243 }
2244 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002245 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002246
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002247 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002248
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002249 while (iter.next()) {
2250 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2251 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002252
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002253 LOOPER_END
2254}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002255
reed41af9662015-01-05 07:49:08 -08002256void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002257 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002258 if (!path.isFinite()) {
2259 return;
2260 }
2261
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002262 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002263 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002264 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002265 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002266 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2267 return;
2268 }
2269 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002270 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002271
2272 const SkRect& r = path.getBounds();
2273 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002274 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002275 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002276 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002277 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002278 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002279
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002280 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002281
2282 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002283 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002284 }
2285
reed@google.com4e2b3d32011-04-07 14:18:59 +00002286 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287}
2288
reed262a71b2015-12-05 13:07:27 -08002289bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002290 if (!paint.getImageFilter()) {
2291 return false;
2292 }
2293
2294 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002295 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002296 return false;
2297 }
2298
2299 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2300 // Once we can filter and the filter will return a result larger than itself, we should be
2301 // able to remove this constraint.
2302 // skbug.com/4526
2303 //
2304 SkPoint pt;
2305 ctm.mapXY(x, y, &pt);
2306 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2307 return ir.contains(fMCRec->fRasterClip.getBounds());
2308}
2309
reeda85d4d02015-05-06 12:56:48 -07002310void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002311 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002312 SkRect bounds = SkRect::MakeXYWH(x, y,
2313 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002314 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002315 SkRect tmp = bounds;
2316 if (paint) {
2317 paint->computeFastBounds(tmp, &tmp);
2318 }
2319 if (this->quickReject(tmp)) {
2320 return;
2321 }
reeda85d4d02015-05-06 12:56:48 -07002322 }
halcanary9d524f22016-03-29 09:03:52 -07002323
reeda85d4d02015-05-06 12:56:48 -07002324 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002325 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002326 paint = lazy.init();
2327 }
reed262a71b2015-12-05 13:07:27 -08002328
reeda2217ef2016-07-20 06:04:34 -07002329 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002330 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2331 *paint);
2332 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002333 special = this->getDevice()->makeSpecial(image);
2334 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002335 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002336 }
2337 }
2338
reed262a71b2015-12-05 13:07:27 -08002339 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2340
reeda85d4d02015-05-06 12:56:48 -07002341 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002342 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002343 if (special) {
2344 SkPoint pt;
2345 iter.fMatrix->mapXY(x, y, &pt);
2346 iter.fDevice->drawSpecial(iter, special.get(),
2347 SkScalarRoundToInt(pt.fX),
2348 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002349 } else {
2350 iter.fDevice->drawImage(iter, image, x, y, pnt);
2351 }
reeda85d4d02015-05-06 12:56:48 -07002352 }
halcanary9d524f22016-03-29 09:03:52 -07002353
reeda85d4d02015-05-06 12:56:48 -07002354 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002355}
2356
reed41af9662015-01-05 07:49:08 -08002357void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002358 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002359 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002360 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002361 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002362 if (paint) {
2363 paint->computeFastBounds(dst, &storage);
2364 }
2365 if (this->quickReject(storage)) {
2366 return;
2367 }
reeda85d4d02015-05-06 12:56:48 -07002368 }
2369 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002370 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002371 paint = lazy.init();
2372 }
halcanary9d524f22016-03-29 09:03:52 -07002373
senorblancoc41e7e12015-12-07 12:51:30 -08002374 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002375 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002376
reeda85d4d02015-05-06 12:56:48 -07002377 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002378 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002379 }
halcanary9d524f22016-03-29 09:03:52 -07002380
reeda85d4d02015-05-06 12:56:48 -07002381 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002382}
2383
reed41af9662015-01-05 07:49:08 -08002384void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002385 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002386 SkDEBUGCODE(bitmap.validate();)
2387
reed33366972015-10-08 09:22:02 -07002388 if (bitmap.drawsNothing()) {
2389 return;
2390 }
2391
2392 SkLazyPaint lazy;
2393 if (nullptr == paint) {
2394 paint = lazy.init();
2395 }
2396
2397 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2398
2399 SkRect storage;
2400 const SkRect* bounds = nullptr;
2401 if (paint->canComputeFastBounds()) {
2402 bitmap.getBounds(&storage);
2403 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002404 SkRect tmp = storage;
2405 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2406 return;
2407 }
2408 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002409 }
reed@google.com4b226022011-01-11 18:32:13 +00002410
reeda2217ef2016-07-20 06:04:34 -07002411 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002412 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2413 *paint);
2414 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002415 special = this->getDevice()->makeSpecial(bitmap);
2416 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002417 drawAsSprite = false;
2418 }
2419 }
2420
reed262a71b2015-12-05 13:07:27 -08002421 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002422
2423 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002424 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002425 if (special) {
reed262a71b2015-12-05 13:07:27 -08002426 SkPoint pt;
2427 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002428 iter.fDevice->drawSpecial(iter, special.get(),
2429 SkScalarRoundToInt(pt.fX),
2430 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002431 } else {
2432 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2433 }
reed33366972015-10-08 09:22:02 -07002434 }
msarettfbfa2582016-08-12 08:29:08 -07002435
reed33366972015-10-08 09:22:02 -07002436 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002437}
2438
reed@google.com9987ec32011-09-07 11:57:52 +00002439// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002440void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002441 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002442 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002443 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002444 return;
2445 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002446
halcanary96fcdcc2015-08-27 07:41:13 -07002447 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002448 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002449 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2450 return;
2451 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002452 }
reed@google.com3d608122011-11-21 15:16:16 +00002453
reed@google.com33535f32012-09-25 15:37:50 +00002454 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002455 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002456 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002457 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002458
senorblancoc41e7e12015-12-07 12:51:30 -08002459 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002460 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002461
reed@google.com33535f32012-09-25 15:37:50 +00002462 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002463 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002464 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002465
reed@google.com33535f32012-09-25 15:37:50 +00002466 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002467}
2468
reed41af9662015-01-05 07:49:08 -08002469void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002470 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002471 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002472 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002473 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002474}
2475
reed4c21dc52015-06-25 12:32:03 -07002476void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2477 const SkPaint* paint) {
2478 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002479
halcanary96fcdcc2015-08-27 07:41:13 -07002480 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002481 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002482 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2483 return;
2484 }
reed@google.com3d608122011-11-21 15:16:16 +00002485 }
halcanary9d524f22016-03-29 09:03:52 -07002486
reed4c21dc52015-06-25 12:32:03 -07002487 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002488 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002489 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002490 }
halcanary9d524f22016-03-29 09:03:52 -07002491
senorblancoc41e7e12015-12-07 12:51:30 -08002492 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002493
reed4c21dc52015-06-25 12:32:03 -07002494 while (iter.next()) {
2495 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002496 }
halcanary9d524f22016-03-29 09:03:52 -07002497
reed4c21dc52015-06-25 12:32:03 -07002498 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002499}
2500
reed41af9662015-01-05 07:49:08 -08002501void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2502 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002503 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002504 SkDEBUGCODE(bitmap.validate();)
2505
halcanary96fcdcc2015-08-27 07:41:13 -07002506 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002507 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002508 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2509 return;
2510 }
reed4c21dc52015-06-25 12:32:03 -07002511 }
halcanary9d524f22016-03-29 09:03:52 -07002512
reed4c21dc52015-06-25 12:32:03 -07002513 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002514 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002515 paint = lazy.init();
2516 }
halcanary9d524f22016-03-29 09:03:52 -07002517
senorblancoc41e7e12015-12-07 12:51:30 -08002518 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002519
reed4c21dc52015-06-25 12:32:03 -07002520 while (iter.next()) {
2521 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2522 }
halcanary9d524f22016-03-29 09:03:52 -07002523
reed4c21dc52015-06-25 12:32:03 -07002524 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002525}
2526
msarett16882062016-08-16 09:31:08 -07002527void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2528 const SkPaint* paint) {
2529 if (nullptr == paint || paint->canComputeFastBounds()) {
2530 SkRect storage;
2531 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2532 return;
2533 }
2534 }
2535
2536 SkLazyPaint lazy;
2537 if (nullptr == paint) {
2538 paint = lazy.init();
2539 }
2540
2541 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2542
2543 while (iter.next()) {
2544 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2545 }
2546
2547 LOOPER_END
2548}
2549
2550void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2551 const SkRect& dst, const SkPaint* paint) {
2552 if (nullptr == paint || paint->canComputeFastBounds()) {
2553 SkRect storage;
2554 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2555 return;
2556 }
2557 }
2558
2559 SkLazyPaint lazy;
2560 if (nullptr == paint) {
2561 paint = lazy.init();
2562 }
2563
2564 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2565
2566 while (iter.next()) {
2567 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2568 }
2569
2570 LOOPER_END
2571}
2572
reed@google.comf67e4cf2011-03-15 20:56:58 +00002573class SkDeviceFilteredPaint {
2574public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002575 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002576 uint32_t filteredFlags = device->filterTextFlags(paint);
2577 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002578 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002579 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002580 fPaint = newPaint;
2581 } else {
2582 fPaint = &paint;
2583 }
2584 }
2585
reed@google.comf67e4cf2011-03-15 20:56:58 +00002586 const SkPaint& paint() const { return *fPaint; }
2587
2588private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002589 const SkPaint* fPaint;
2590 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002591};
2592
bungeman@google.com52c748b2011-08-22 21:30:43 +00002593void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2594 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002595 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002596 draw.fDevice->drawRect(draw, r, paint);
2597 } else {
2598 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002599 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002600 draw.fDevice->drawRect(draw, r, p);
2601 }
2602}
2603
2604void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2605 const char text[], size_t byteLength,
2606 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002607 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002608
2609 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002610 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002611 draw.fRC->isEmpty() ||
reed374772b2016-10-05 17:33:02 -07002612 (paint.getAlpha() == 0 && paint.isSrcOver())) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002613 return;
2614 }
2615
2616 SkScalar width = 0;
2617 SkPoint start;
2618
2619 start.set(0, 0); // to avoid warning
2620 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2621 SkPaint::kStrikeThruText_Flag)) {
2622 width = paint.measureText(text, byteLength);
2623
2624 SkScalar offsetX = 0;
2625 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2626 offsetX = SkScalarHalf(width);
2627 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2628 offsetX = width;
2629 }
2630 start.set(x - offsetX, y);
2631 }
2632
2633 if (0 == width) {
2634 return;
2635 }
2636
2637 uint32_t flags = paint.getFlags();
2638
2639 if (flags & (SkPaint::kUnderlineText_Flag |
2640 SkPaint::kStrikeThruText_Flag)) {
2641 SkScalar textSize = paint.getTextSize();
2642 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2643 SkRect r;
2644
2645 r.fLeft = start.fX;
2646 r.fRight = start.fX + width;
2647
2648 if (flags & SkPaint::kUnderlineText_Flag) {
2649 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2650 start.fY);
2651 r.fTop = offset;
2652 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002653 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002654 }
2655 if (flags & SkPaint::kStrikeThruText_Flag) {
2656 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2657 start.fY);
2658 r.fTop = offset;
2659 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002660 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002661 }
2662 }
2663}
2664
reed@google.come0d9ce82014-04-23 04:00:17 +00002665void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2666 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002667 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002668
2669 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002670 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002671 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002672 DrawTextDecorations(iter, dfp.paint(),
2673 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002674 }
2675
reed@google.com4e2b3d32011-04-07 14:18:59 +00002676 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002677}
2678
reed@google.come0d9ce82014-04-23 04:00:17 +00002679void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2680 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002681 SkPoint textOffset = SkPoint::Make(0, 0);
2682
halcanary96fcdcc2015-08-27 07:41:13 -07002683 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002684
reed@android.com8a1c16f2008-12-17 15:59:43 +00002685 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002686 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002687 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002688 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002689 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002690
reed@google.com4e2b3d32011-04-07 14:18:59 +00002691 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002692}
2693
reed@google.come0d9ce82014-04-23 04:00:17 +00002694void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2695 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002696
2697 SkPoint textOffset = SkPoint::Make(0, constY);
2698
halcanary96fcdcc2015-08-27 07:41:13 -07002699 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002700
reed@android.com8a1c16f2008-12-17 15:59:43 +00002701 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002702 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002703 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002704 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002705 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002706
reed@google.com4e2b3d32011-04-07 14:18:59 +00002707 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002708}
2709
reed@google.come0d9ce82014-04-23 04:00:17 +00002710void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2711 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002712 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002713
reed@android.com8a1c16f2008-12-17 15:59:43 +00002714 while (iter.next()) {
2715 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002716 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002717 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002718
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002719 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002720}
2721
reed45561a02016-07-07 12:47:17 -07002722void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2723 const SkRect* cullRect, const SkPaint& paint) {
2724 if (cullRect && this->quickReject(*cullRect)) {
2725 return;
2726 }
2727
2728 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2729
2730 while (iter.next()) {
2731 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2732 }
2733
2734 LOOPER_END
2735}
2736
fmalita00d5c2c2014-08-21 08:53:26 -07002737void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2738 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002739
fmalita85d5eb92015-03-04 11:20:12 -08002740 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002741 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002742 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002743 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002744 SkRect tmp;
2745 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2746 return;
2747 }
2748 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002749 }
2750
fmalita024f9962015-03-03 19:08:17 -08002751 // We cannot filter in the looper as we normally do, because the paint is
2752 // incomplete at this point (text-related attributes are embedded within blob run paints).
2753 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002754 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002755
fmalita85d5eb92015-03-04 11:20:12 -08002756 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002757
fmalitaaa1b9122014-08-28 14:32:24 -07002758 while (iter.next()) {
2759 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002760 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002761 }
2762
fmalitaaa1b9122014-08-28 14:32:24 -07002763 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002764
2765 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002766}
2767
reed@google.come0d9ce82014-04-23 04:00:17 +00002768// These will become non-virtual, so they always call the (virtual) onDraw... method
2769void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2770 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002771 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002772 if (byteLength) {
2773 this->onDrawText(text, byteLength, x, y, paint);
2774 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002775}
2776void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2777 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002778 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002779 if (byteLength) {
2780 this->onDrawPosText(text, byteLength, pos, paint);
2781 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002782}
2783void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2784 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002785 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002786 if (byteLength) {
2787 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2788 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002789}
2790void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2791 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002792 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002793 if (byteLength) {
2794 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2795 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002796}
reed45561a02016-07-07 12:47:17 -07002797void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2798 const SkRect* cullRect, const SkPaint& paint) {
2799 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2800 if (byteLength) {
2801 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2802 }
2803}
fmalita00d5c2c2014-08-21 08:53:26 -07002804void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2805 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002806 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002807 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002808 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002809}
reed@google.come0d9ce82014-04-23 04:00:17 +00002810
reed41af9662015-01-05 07:49:08 -08002811void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2812 const SkPoint verts[], const SkPoint texs[],
Mike Reedfaba3712016-11-03 14:45:31 -04002813 const SkColor colors[], SkBlendMode bmode,
reed41af9662015-01-05 07:49:08 -08002814 const uint16_t indices[], int indexCount,
2815 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002816 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002817 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002818
reed@android.com8a1c16f2008-12-17 15:59:43 +00002819 while (iter.next()) {
2820 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
Mike Reedfaba3712016-11-03 14:45:31 -04002821 colors, bmode, indices, indexCount,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002822 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002823 }
reed@google.com4b226022011-01-11 18:32:13 +00002824
reed@google.com4e2b3d32011-04-07 14:18:59 +00002825 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002826}
2827
dandovb3c9d1c2014-08-12 08:34:29 -07002828void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reed7d954ad2016-10-28 15:42:34 -04002829 const SkPoint texCoords[4], SkBlendMode bmode,
2830 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002831 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002832 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002833 return;
2834 }
mtklein6cfa73a2014-08-13 13:33:49 -07002835
Mike Reedfaba3712016-11-03 14:45:31 -04002836 this->onDrawPatch(cubics, colors, texCoords, bmode, paint);
msarett9340c262016-09-22 05:20:21 -07002837}
2838
2839void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
Mike Reedfaba3712016-11-03 14:45:31 -04002840 const SkPoint texCoords[4], SkBlendMode bmode,
Mike Reed7d954ad2016-10-28 15:42:34 -04002841 const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002842 // Since a patch is always within the convex hull of the control points, we discard it when its
2843 // bounding rectangle is completely outside the current clip.
2844 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002845 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002846 if (this->quickReject(bounds)) {
2847 return;
2848 }
mtklein6cfa73a2014-08-13 13:33:49 -07002849
halcanary96fcdcc2015-08-27 07:41:13 -07002850 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002851
dandovecfff212014-08-04 10:02:00 -07002852 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002853 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, bmode, paint);
dandovecfff212014-08-04 10:02:00 -07002854 }
mtklein6cfa73a2014-08-13 13:33:49 -07002855
dandovecfff212014-08-04 10:02:00 -07002856 LOOPER_END
2857}
2858
reeda8db7282015-07-07 10:22:31 -07002859void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002860 RETURN_ON_NULL(dr);
2861 if (x || y) {
2862 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2863 this->onDrawDrawable(dr, &matrix);
2864 } else {
2865 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002866 }
2867}
2868
reeda8db7282015-07-07 10:22:31 -07002869void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002870 RETURN_ON_NULL(dr);
2871 if (matrix && matrix->isIdentity()) {
2872 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002873 }
reede3b38ce2016-01-08 09:18:44 -08002874 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002875}
2876
2877void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002878 // drawable bounds are no longer reliable (e.g. android displaylist)
2879 // so don't use them for quick-reject
reeda8db7282015-07-07 10:22:31 -07002880 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002881}
2882
reed71c3c762015-06-24 10:29:17 -07002883void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
Mike Reedfaba3712016-11-03 14:45:31 -04002884 const SkColor colors[], int count, SkBlendMode bmode,
reed71c3c762015-06-24 10:29:17 -07002885 const SkRect* cull, const SkPaint* paint) {
2886 if (cull && this->quickReject(*cull)) {
2887 return;
2888 }
2889
2890 SkPaint pnt;
2891 if (paint) {
2892 pnt = *paint;
2893 }
halcanary9d524f22016-03-29 09:03:52 -07002894
halcanary96fcdcc2015-08-27 07:41:13 -07002895 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002896 while (iter.next()) {
Mike Reedfaba3712016-11-03 14:45:31 -04002897 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, bmode, pnt);
reed71c3c762015-06-24 10:29:17 -07002898 }
2899 LOOPER_END
2900}
2901
reedf70b5312016-03-04 16:36:20 -08002902void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2903 SkASSERT(key);
2904
2905 SkPaint paint;
2906 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2907 while (iter.next()) {
2908 iter.fDevice->drawAnnotation(iter, rect, key, value);
2909 }
2910 LOOPER_END
2911}
2912
reed@android.com8a1c16f2008-12-17 15:59:43 +00002913//////////////////////////////////////////////////////////////////////////////
2914// These methods are NOT virtual, and therefore must call back into virtual
2915// methods, rather than actually drawing themselves.
2916//////////////////////////////////////////////////////////////////////////////
2917
reed374772b2016-10-05 17:33:02 -07002918void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002919 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002920 SkPaint paint;
2921
2922 paint.setARGB(a, r, g, b);
reed374772b2016-10-05 17:33:02 -07002923 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002924 this->drawPaint(paint);
2925}
2926
reed374772b2016-10-05 17:33:02 -07002927void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002928 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002929 SkPaint paint;
2930
2931 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002932 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002933 this->drawPaint(paint);
2934}
2935
2936void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002937 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002938 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002939
reed@android.com8a1c16f2008-12-17 15:59:43 +00002940 pt.set(x, y);
2941 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2942}
2943
2944void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002945 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002946 SkPoint pt;
2947 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002948
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949 pt.set(x, y);
2950 paint.setColor(color);
2951 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2952}
2953
2954void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2955 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002956 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002957 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002958
reed@android.com8a1c16f2008-12-17 15:59:43 +00002959 pts[0].set(x0, y0);
2960 pts[1].set(x1, y1);
2961 this->drawPoints(kLines_PointMode, 2, pts, paint);
2962}
2963
2964void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2965 SkScalar right, SkScalar bottom,
2966 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002967 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002968 SkRect r;
2969
2970 r.set(left, top, right, bottom);
2971 this->drawRect(r, paint);
2972}
2973
2974void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2975 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002976 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002977 if (radius < 0) {
2978 radius = 0;
2979 }
2980
2981 SkRect r;
2982 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002983 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002984}
2985
2986void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
2987 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002988 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002989 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002990 SkRRect rrect;
2991 rrect.setRectXY(r, rx, ry);
2992 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002993 } else {
2994 this->drawRect(r, paint);
2995 }
2996}
2997
reed@android.com8a1c16f2008-12-17 15:59:43 +00002998void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
2999 SkScalar sweepAngle, bool useCenter,
3000 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003001 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
bsalomon21af9ca2016-08-25 12:29:23 -07003002 if (oval.isEmpty() || !sweepAngle) {
3003 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003004 }
bsalomon21af9ca2016-08-25 12:29:23 -07003005 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003006}
3007
3008void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3009 const SkPath& path, SkScalar hOffset,
3010 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003011 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003012 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003013
reed@android.com8a1c16f2008-12-17 15:59:43 +00003014 matrix.setTranslate(hOffset, vOffset);
3015 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3016}
3017
reed@android.comf76bacf2009-05-13 14:00:33 +00003018///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003019
3020/**
3021 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3022 * against the playback cost of recursing into the subpicture to get at its actual ops.
3023 *
3024 * For now we pick a conservatively small value, though measurement (and other heuristics like
3025 * the type of ops contained) may justify changing this value.
3026 */
3027#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003028
reedd5fa1a42014-08-09 11:08:05 -07003029void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003030 RETURN_ON_NULL(picture);
3031
reed1c2c4412015-04-30 13:09:24 -07003032 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003033 if (matrix && matrix->isIdentity()) {
3034 matrix = nullptr;
3035 }
3036 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3037 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3038 picture->playback(this);
3039 } else {
3040 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003041 }
3042}
robertphillips9b14f262014-06-04 05:40:44 -07003043
reedd5fa1a42014-08-09 11:08:05 -07003044void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3045 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003046 if (!paint || paint->canComputeFastBounds()) {
3047 SkRect bounds = picture->cullRect();
3048 if (paint) {
3049 paint->computeFastBounds(bounds, &bounds);
3050 }
3051 if (matrix) {
3052 matrix->mapRect(&bounds);
3053 }
3054 if (this->quickReject(bounds)) {
3055 return;
3056 }
3057 }
3058
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003059 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003060 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003061}
3062
vjiaoblack95302da2016-07-21 10:25:54 -07003063#ifdef SK_EXPERIMENTAL_SHADOWING
3064void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3065 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003066 const SkPaint* paint,
3067 const SkShadowParams& params) {
vjiaoblack95302da2016-07-21 10:25:54 -07003068 RETURN_ON_NULL(picture);
3069
3070 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3071
vjiaoblacke6f5d562016-08-25 06:30:23 -07003072 this->onDrawShadowedPicture(picture, matrix, paint, params);
vjiaoblack95302da2016-07-21 10:25:54 -07003073}
3074
3075void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3076 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003077 const SkPaint* paint,
3078 const SkShadowParams& params) {
vjiaoblack904527d2016-08-09 09:32:09 -07003079 if (!paint || paint->canComputeFastBounds()) {
3080 SkRect bounds = picture->cullRect();
3081 if (paint) {
3082 paint->computeFastBounds(bounds, &bounds);
3083 }
3084 if (matrix) {
3085 matrix->mapRect(&bounds);
3086 }
3087 if (this->quickReject(bounds)) {
3088 return;
3089 }
3090 }
3091
3092 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3093
vjiaoblacke6f5d562016-08-25 06:30:23 -07003094 sk_sp<SkImage> povDepthMap;
3095 sk_sp<SkImage> diffuseMap;
3096
vjiaoblack904527d2016-08-09 09:32:09 -07003097 // povDepthMap
3098 {
3099 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003100 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3101 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003102 sk_sp<SkLights> povLight = builder.finish();
3103
3104 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3105 picture->cullRect().height(),
3106 kBGRA_8888_SkColorType,
3107 kOpaque_SkAlphaType);
3108
3109 // Create a new surface (that matches the backend of canvas)
3110 // to create the povDepthMap
3111 sk_sp<SkSurface> surf(this->makeSurface(info));
3112
3113 // Wrap another SPFCanvas around the surface
3114 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3115 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3116
3117 // set the depth map canvas to have the light as the user's POV
3118 depthMapCanvas->setLights(std::move(povLight));
3119
3120 depthMapCanvas->drawPicture(picture);
vjiaoblack904527d2016-08-09 09:32:09 -07003121 povDepthMap = surf->makeImageSnapshot();
3122 }
3123
3124 // diffuseMap
3125 {
3126 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3127 picture->cullRect().height(),
3128 kBGRA_8888_SkColorType,
3129 kOpaque_SkAlphaType);
3130
3131 sk_sp<SkSurface> surf(this->makeSurface(info));
3132 surf->getCanvas()->drawPicture(picture);
3133
3134 diffuseMap = surf->makeImageSnapshot();
3135 }
vjiaoblack904527d2016-08-09 09:32:09 -07003136
3137 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3138 SkShader::kClamp_TileMode);
vjiaoblack904527d2016-08-09 09:32:09 -07003139 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3140 SkShader::kClamp_TileMode);
vjiaoblackb2796fd2016-09-09 09:22:39 -07003141
3142 // TODO: pass the depth to the shader in vertices, or uniforms
3143 // so we don't have to render depth and color separately
3144 for (int i = 0; i < fLights->numLights(); ++i) {
3145 // skip over ambient lights; they don't cast shadows
3146 // lights that have shadow maps do not need updating (because lights are immutable)
3147 sk_sp<SkImage> depthMap;
3148 SkISize shMapSize;
3149
3150 if (fLights->light(i).getShadowMap() != nullptr) {
3151 continue;
3152 }
3153
3154 if (fLights->light(i).isRadial()) {
3155 shMapSize.fHeight = 1;
3156 shMapSize.fWidth = (int) picture->cullRect().width();
3157
3158 SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1,
3159 kBGRA_8888_SkColorType,
3160 kOpaque_SkAlphaType);
3161
3162 // Create new surface (that matches the backend of canvas)
3163 // for each shadow map
3164 sk_sp<SkSurface> surf(this->makeSurface(info));
3165
3166 // Wrap another SPFCanvas around the surface
3167 SkCanvas* depthMapCanvas = surf->getCanvas();
3168
3169 SkLights::Builder builder;
3170 builder.add(fLights->light(i));
3171 sk_sp<SkLights> curLight = builder.finish();
3172
3173 sk_sp<SkShader> shadowMapShader;
3174 shadowMapShader = SkRadialShadowMapShader::Make(
3175 povDepthShader, curLight,
3176 (int) picture->cullRect().width(),
3177 (int) picture->cullRect().height());
3178
3179 SkPaint shadowMapPaint;
3180 shadowMapPaint.setShader(std::move(shadowMapShader));
3181
3182 depthMapCanvas->setLights(curLight);
3183
3184 depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(),
3185 diffuseMap->height()),
3186 shadowMapPaint);
3187
3188 depthMap = surf->makeImageSnapshot();
3189
3190 } else {
3191 // TODO: compute the correct size of the depth map from the light properties
3192 // TODO: maybe add a kDepth_8_SkColorType
3193 // TODO: find actual max depth of picture
3194 shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3195 fLights->light(i), 255,
3196 (int) picture->cullRect().width(),
3197 (int) picture->cullRect().height());
3198
3199 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3200 kBGRA_8888_SkColorType,
3201 kOpaque_SkAlphaType);
3202
3203 // Create a new surface (that matches the backend of canvas)
3204 // for each shadow map
3205 sk_sp<SkSurface> surf(this->makeSurface(info));
3206
3207 // Wrap another SPFCanvas around the surface
3208 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3209 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3210 depthMapCanvas->setShadowParams(params);
3211
3212 // set the depth map canvas to have the light we're drawing.
3213 SkLights::Builder builder;
3214 builder.add(fLights->light(i));
3215 sk_sp<SkLights> curLight = builder.finish();
3216 depthMapCanvas->setLights(std::move(curLight));
3217
3218 depthMapCanvas->drawPicture(picture);
3219 depthMap = surf->makeImageSnapshot();
3220 }
3221
3222 if (params.fType == SkShadowParams::kNoBlur_ShadowType) {
3223 fLights->light(i).setShadowMap(std::move(depthMap));
3224 } else if (params.fType == SkShadowParams::kVariance_ShadowType) {
3225 // we blur the variance map
3226 SkPaint blurPaint;
3227 blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius,
3228 params.fShadowRadius, nullptr));
3229
3230 SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3231 kBGRA_8888_SkColorType,
3232 kOpaque_SkAlphaType);
3233
3234 sk_sp<SkSurface> blurSurf(this->makeSurface(blurInfo));
3235
3236 blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
3237
3238 fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
3239 }
3240 }
3241
3242 SkPaint shadowPaint;
vjiaoblack904527d2016-08-09 09:32:09 -07003243 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3244 std::move(diffuseShader),
vjiaoblackb2796fd2016-09-09 09:22:39 -07003245 fLights,
vjiaoblack904527d2016-08-09 09:32:09 -07003246 diffuseMap->width(),
vjiaoblacke6f5d562016-08-25 06:30:23 -07003247 diffuseMap->height(),
3248 params);
vjiaoblack904527d2016-08-09 09:32:09 -07003249
3250 shadowPaint.setShader(shadowShader);
3251
3252 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003253}
3254#endif
3255
reed@android.com8a1c16f2008-12-17 15:59:43 +00003256///////////////////////////////////////////////////////////////////////////////
3257///////////////////////////////////////////////////////////////////////////////
3258
reed3aafe112016-08-18 12:45:34 -07003259SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003260 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003261
3262 SkASSERT(canvas);
3263
reed3aafe112016-08-18 12:45:34 -07003264 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003265 fDone = !fImpl->next();
3266}
3267
3268SkCanvas::LayerIter::~LayerIter() {
3269 fImpl->~SkDrawIter();
3270}
3271
3272void SkCanvas::LayerIter::next() {
3273 fDone = !fImpl->next();
3274}
3275
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003276SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003277 return fImpl->getDevice();
3278}
3279
3280const SkMatrix& SkCanvas::LayerIter::matrix() const {
3281 return fImpl->getMatrix();
3282}
3283
3284const SkPaint& SkCanvas::LayerIter::paint() const {
3285 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003286 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003287 paint = &fDefaultPaint;
3288 }
3289 return *paint;
3290}
3291
reed1e7f5e72016-04-27 07:49:17 -07003292const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003293int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3294int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003295
3296///////////////////////////////////////////////////////////////////////////////
3297
fmalitac3b589a2014-06-05 12:40:07 -07003298SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003299
3300///////////////////////////////////////////////////////////////////////////////
3301
3302static bool supported_for_raster_canvas(const SkImageInfo& info) {
3303 switch (info.alphaType()) {
3304 case kPremul_SkAlphaType:
3305 case kOpaque_SkAlphaType:
3306 break;
3307 default:
3308 return false;
3309 }
3310
3311 switch (info.colorType()) {
3312 case kAlpha_8_SkColorType:
3313 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003314 case kN32_SkColorType:
junov9e3dbdf2016-10-13 13:14:27 -07003315 case kRGBA_F16_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003316 break;
3317 default:
3318 return false;
3319 }
3320
3321 return true;
3322}
3323
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003324SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3325 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003326 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003327 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003328
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003329 SkBitmap bitmap;
3330 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003331 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003332 }
halcanary385fe4d2015-08-26 13:07:48 -07003333 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003334}
reedd5fa1a42014-08-09 11:08:05 -07003335
3336///////////////////////////////////////////////////////////////////////////////
3337
3338SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003339 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003340 : fCanvas(canvas)
3341 , fSaveCount(canvas->getSaveCount())
3342{
bsalomon49f085d2014-09-05 13:34:00 -07003343 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003344 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003345 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003346 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003347 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003348 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003349 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003350 canvas->save();
3351 }
mtklein6cfa73a2014-08-13 13:33:49 -07003352
bsalomon49f085d2014-09-05 13:34:00 -07003353 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003354 canvas->concat(*matrix);
3355 }
3356}
3357
3358SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3359 fCanvas->restoreToCount(fSaveCount);
3360}
reede8f30622016-03-23 18:59:25 -07003361
reed73603f32016-09-20 08:42:38 -07003362/////////////////////////////////
3363
3364const SkCanvas::ClipOp SkCanvas::kDifference_Op;
3365const SkCanvas::ClipOp SkCanvas::kIntersect_Op;
3366const SkCanvas::ClipOp SkCanvas::kUnion_Op;
3367const SkCanvas::ClipOp SkCanvas::kXOR_Op;
3368const SkCanvas::ClipOp SkCanvas::kReverseDifference_Op;
3369const SkCanvas::ClipOp SkCanvas::kReplace_Op;
3370
3371static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3372static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3373static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3374static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3375static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3376static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");