blob: 4e74a77992a80eba01f126ddce9a9450f3d797af [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;
reeddbc3cef2015-04-29 12:18:57 -0700484 tmp.setImageFilter(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 -08001372#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001373const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001374 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001375 if (this->peekPixels(&pmap)) {
1376 if (info) {
1377 *info = pmap.info();
1378 }
1379 if (rowBytes) {
1380 *rowBytes = pmap.rowBytes();
1381 }
1382 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001383 }
reed6ceeebd2016-03-09 14:26:26 -08001384 return nullptr;
1385}
1386#endif
1387
1388bool SkCanvas::peekPixels(SkPixmap* pmap) {
1389 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001390}
1391
reed884e97c2015-05-26 11:31:54 -07001392bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001393 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001394 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001395}
1396
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001397void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001398 SkPixmap pmap;
1399 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001400 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001401 }
1402 if (info) {
1403 *info = pmap.info();
1404 }
1405 if (rowBytes) {
1406 *rowBytes = pmap.rowBytes();
1407 }
1408 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001409 *origin = this->getTopDevice(false)->getOrigin();
1410 }
reed884e97c2015-05-26 11:31:54 -07001411 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001412}
1413
reed884e97c2015-05-26 11:31:54 -07001414bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001415 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001416 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001417}
1418
reed@android.com8a1c16f2008-12-17 15:59:43 +00001419/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001420
reed7503d602016-07-15 14:23:29 -07001421void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001422 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001423 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424 paint = &tmp;
1425 }
reed@google.com4b226022011-01-11 18:32:13 +00001426
reed@google.com8926b162012-03-23 15:36:36 +00001427 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001428
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001430 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001431 paint = &looper.paint();
1432 SkImageFilter* filter = paint->getImageFilter();
1433 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001434 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001435 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001436 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001437 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001438 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001439 }
reeda2217ef2016-07-20 06:04:34 -07001440
reed@google.com4e2b3d32011-04-07 14:18:59 +00001441 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001442}
1443
reed32704672015-12-16 08:27:10 -08001444/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001445
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001446void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001447 if (dx || dy) {
1448 this->checkForDeferredSave();
1449 fDeviceCMDirty = true;
1450 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001451
reedfe69b502016-09-12 06:31:48 -07001452 // Translate shouldn't affect the is-scale-translateness of the matrix.
1453 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001454
reedfe69b502016-09-12 06:31:48 -07001455 this->didTranslate(dx,dy);
1456 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001457}
1458
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001459void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001460 SkMatrix m;
1461 m.setScale(sx, sy);
1462 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001463}
1464
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001465void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001466 SkMatrix m;
1467 m.setRotate(degrees);
1468 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001469}
1470
bungeman7438bfc2016-07-12 15:01:19 -07001471void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1472 SkMatrix m;
1473 m.setRotate(degrees, px, py);
1474 this->concat(m);
1475}
1476
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001477void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001478 SkMatrix m;
1479 m.setSkew(sx, sy);
1480 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001481}
1482
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001483void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001484 if (matrix.isIdentity()) {
1485 return;
1486 }
1487
reed2ff1fce2014-12-11 07:07:37 -08001488 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001489 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001490 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001491 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001492 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001493}
1494
reed8c30a812016-04-20 16:36:51 -07001495void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001496 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001497 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001498 fIsScaleTranslate = matrix.isScaleTranslate();
reed8c30a812016-04-20 16:36:51 -07001499}
1500
1501void SkCanvas::setMatrix(const SkMatrix& matrix) {
1502 this->checkForDeferredSave();
1503 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001504 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001505}
1506
reed@android.com8a1c16f2008-12-17 15:59:43 +00001507void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001508 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001509}
1510
vjiaoblack95302da2016-07-21 10:25:54 -07001511#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001512void SkCanvas::translateZ(SkScalar z) {
1513 this->checkForDeferredSave();
1514 this->fMCRec->fCurDrawDepth += z;
1515 this->didTranslateZ(z);
1516}
1517
1518SkScalar SkCanvas::getZ() const {
1519 return this->fMCRec->fCurDrawDepth;
1520}
1521
vjiaoblack95302da2016-07-21 10:25:54 -07001522void SkCanvas::setLights(sk_sp<SkLights> lights) {
1523 this->fLights = lights;
1524}
1525
1526sk_sp<SkLights> SkCanvas::getLights() const {
1527 return this->fLights;
1528}
1529#endif
1530
reed@android.com8a1c16f2008-12-17 15:59:43 +00001531//////////////////////////////////////////////////////////////////////////////
1532
reed73603f32016-09-20 08:42:38 -07001533void SkCanvas::clipRect(const SkRect& rect, 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;
1536 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001537}
1538
reed73603f32016-09-20 08:42:38 -07001539void SkCanvas::onClipRect(const SkRect& rect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001540 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reedc64eff52015-11-21 12:39:45 -08001541 AutoValidateClip avc(this);
Brian Salomona3b45d42016-10-03 11:36:16 -04001542 fClipStack->clipRect(rect, fMCRec->fMatrix, op, isAA);
1543 fMCRec->fRasterClip.op(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1544 isAA);
reedc64eff52015-11-21 12:39:45 -08001545 fDeviceCMDirty = true;
msarettfbfa2582016-08-12 08:29:08 -07001546 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001547}
1548
reed73603f32016-09-20 08:42:38 -07001549void SkCanvas::clipRRect(const SkRRect& rrect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001550 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001551 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001552 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001553 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1554 } else {
1555 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001556 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001557}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001558
reed73603f32016-09-20 08:42:38 -07001559void SkCanvas::onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001560 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001561
Brian Salomona3b45d42016-10-03 11:36:16 -04001562 fDeviceCMDirty = true;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001563
Brian Salomona3b45d42016-10-03 11:36:16 -04001564 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1565 fClipStack->clipRRect(rrect, fMCRec->fMatrix, op, isAA);
1566 fMCRec->fRasterClip.op(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1567 isAA);
1568 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1569 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001570}
1571
reed73603f32016-09-20 08:42:38 -07001572void SkCanvas::clipPath(const SkPath& path, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001573 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001574 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001575
1576 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1577 SkRect r;
1578 if (path.isRect(&r)) {
1579 this->onClipRect(r, op, edgeStyle);
1580 return;
1581 }
1582 SkRRect rrect;
1583 if (path.isOval(&r)) {
1584 rrect.setOval(r);
1585 this->onClipRRect(rrect, op, edgeStyle);
1586 return;
1587 }
1588 if (path.isRRect(&rrect)) {
1589 this->onClipRRect(rrect, op, edgeStyle);
1590 return;
1591 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001592 }
robertphillips39f05382015-11-24 09:30:12 -08001593
1594 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001595}
1596
reed73603f32016-09-20 08:42:38 -07001597void SkCanvas::onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001598 AutoValidateClip avc(this);
1599
reed@android.com8a1c16f2008-12-17 15:59:43 +00001600 fDeviceCMDirty = true;
Brian Salomona3b45d42016-10-03 11:36:16 -04001601 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001602
Brian Salomona3b45d42016-10-03 11:36:16 -04001603 fClipStack->clipPath(path, fMCRec->fMatrix, op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001604
Brian Salomona3b45d42016-10-03 11:36:16 -04001605 const SkPath* rasterClipPath = &path;
1606 const SkMatrix* matrix = &fMCRec->fMatrix;
1607 SkPath tempPath;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001608 if (fAllowSimplifyClip) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001609 isAA = getClipStack()->asPath(&tempPath);
1610 rasterClipPath = &tempPath;
1611 matrix = &SkMatrix::I();
reed73603f32016-09-20 08:42:38 -07001612 op = kReplace_Op;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001613 }
Brian Salomona3b45d42016-10-03 11:36:16 -04001614 fMCRec->fRasterClip.op(*rasterClipPath, *matrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1615 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001616 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001617}
1618
reed73603f32016-09-20 08:42:38 -07001619void SkCanvas::clipRegion(const SkRegion& rgn, ClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001620 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001621 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001622}
1623
reed73603f32016-09-20 08:42:38 -07001624void SkCanvas::onClipRegion(const SkRegion& rgn, ClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001625 AutoValidateClip avc(this);
1626
reed@android.com8a1c16f2008-12-17 15:59:43 +00001627 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001628
reed@google.com5c3d1472011-02-22 19:12:23 +00001629 // todo: signal fClipStack that we have a region, and therefore (I guess)
1630 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001631 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001632
reed73603f32016-09-20 08:42:38 -07001633 fMCRec->fRasterClip.op(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001634 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001635}
1636
reed@google.com819c9212011-02-23 18:56:55 +00001637#ifdef SK_DEBUG
1638void SkCanvas::validateClip() const {
1639 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001640 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001641 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001642 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001643 return;
1644 }
1645
reed@google.com819c9212011-02-23 18:56:55 +00001646 SkIRect ir;
1647 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001648 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001649
reed687fa1c2015-04-07 08:00:56 -07001650 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001651 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001652 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001653 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001654 case SkClipStack::Element::kRect_Type:
1655 element->getRect().round(&ir);
reed73603f32016-09-20 08:42:38 -07001656 tmpClip.op(ir, (SkRegion::Op)element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001657 break;
1658 case SkClipStack::Element::kEmpty_Type:
1659 tmpClip.setEmpty();
1660 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001661 default: {
1662 SkPath path;
1663 element->asPath(&path);
Brian Salomona3b45d42016-10-03 11:36:16 -04001664 tmpClip.op(path, SkMatrix::I(), this->getTopLayerBounds(),
1665 (SkRegion::Op)element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001666 break;
1667 }
reed@google.com819c9212011-02-23 18:56:55 +00001668 }
1669 }
reed@google.com819c9212011-02-23 18:56:55 +00001670}
1671#endif
1672
reed@google.com90c07ea2012-04-13 13:50:27 +00001673void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001674 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001675 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001676
halcanary96fcdcc2015-08-27 07:41:13 -07001677 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001678 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001679 }
1680}
1681
reed@google.com5c3d1472011-02-22 19:12:23 +00001682///////////////////////////////////////////////////////////////////////////////
1683
reed@google.com754de5f2014-02-24 19:38:20 +00001684bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001685 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001686}
1687
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001688bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001689 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001690}
1691
msarettfbfa2582016-08-12 08:29:08 -07001692static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1693#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1694 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1695 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1696 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1697 return 0xF != _mm_movemask_ps(mask);
1698#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1699 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1700 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1701 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1702 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1703#else
1704 SkRect devRectAsRect;
1705 SkRect devClipAsRect;
1706 devRect.store(&devRectAsRect.fLeft);
1707 devClip.store(&devClipAsRect.fLeft);
1708 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1709#endif
1710}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001711
msarettfbfa2582016-08-12 08:29:08 -07001712// It's important for this function to not be inlined. Otherwise the compiler will share code
1713// between the fast path and the slow path, resulting in two slow paths.
1714static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1715 const SkMatrix& matrix) {
1716 SkRect deviceRect;
1717 matrix.mapRect(&deviceRect, src);
1718 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1719}
1720
1721bool SkCanvas::quickReject(const SkRect& src) const {
1722#ifdef SK_DEBUG
1723 // Verify that fDeviceClipBounds are set properly.
1724 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001725 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001726 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001727 } else {
msarettfbfa2582016-08-12 08:29:08 -07001728 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001729 }
msarettfbfa2582016-08-12 08:29:08 -07001730
msarett9637ea92016-08-18 14:03:30 -07001731 // Verify that fIsScaleTranslate is set properly.
1732 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001733#endif
1734
msarett9637ea92016-08-18 14:03:30 -07001735 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001736 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1737 }
1738
1739 // We inline the implementation of mapScaleTranslate() for the fast path.
1740 float sx = fMCRec->fMatrix.getScaleX();
1741 float sy = fMCRec->fMatrix.getScaleY();
1742 float tx = fMCRec->fMatrix.getTranslateX();
1743 float ty = fMCRec->fMatrix.getTranslateY();
1744 Sk4f scale(sx, sy, sx, sy);
1745 Sk4f trans(tx, ty, tx, ty);
1746
1747 // Apply matrix.
1748 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1749
1750 // Make sure left < right, top < bottom.
1751 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1752 Sk4f min = Sk4f::Min(ltrb, rblt);
1753 Sk4f max = Sk4f::Max(ltrb, rblt);
1754 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1755 // ARM this sequence generates the fastest (a single instruction).
1756 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1757
1758 // Check if the device rect is NaN or outside the clip.
1759 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001760}
1761
reed@google.com3b3e8952012-08-16 20:53:31 +00001762bool SkCanvas::quickReject(const SkPath& path) const {
1763 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001764}
1765
reed@google.com3b3e8952012-08-16 20:53:31 +00001766bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001767 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001768 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769 return false;
1770 }
1771
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001772 SkMatrix inverse;
1773 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001774 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001775 if (bounds) {
1776 bounds->setEmpty();
1777 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001778 return false;
1779 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001780
bsalomon49f085d2014-09-05 13:34:00 -07001781 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001782 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001783 // adjust it outwards in case we are antialiasing
1784 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001785
reed@google.com8f4d2302013-12-17 16:44:46 +00001786 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1787 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001788 inverse.mapRect(bounds, r);
1789 }
1790 return true;
1791}
1792
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001793bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001794 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001795 if (clip.isEmpty()) {
1796 if (bounds) {
1797 bounds->setEmpty();
1798 }
1799 return false;
1800 }
1801
bsalomon49f085d2014-09-05 13:34:00 -07001802 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001803 *bounds = clip.getBounds();
1804 }
1805 return true;
1806}
1807
reed@android.com8a1c16f2008-12-17 15:59:43 +00001808const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001809 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001810}
1811
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001812const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001813 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001814}
1815
Brian Osman11052242016-10-27 14:47:55 -04001816GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001817 SkBaseDevice* dev = this->getTopDevice();
Brian Osman11052242016-10-27 14:47:55 -04001818 return dev ? dev->accessRenderTargetContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001819}
1820
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001821GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001822 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001823 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001824}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001825
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001826void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1827 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001828 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001829 if (outer.isEmpty()) {
1830 return;
1831 }
1832 if (inner.isEmpty()) {
1833 this->drawRRect(outer, paint);
1834 return;
1835 }
1836
1837 // We don't have this method (yet), but technically this is what we should
1838 // be able to assert...
1839 // SkASSERT(outer.contains(inner));
1840 //
1841 // For now at least check for containment of bounds
1842 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1843
1844 this->onDrawDRRect(outer, inner, paint);
1845}
1846
reed41af9662015-01-05 07:49:08 -08001847// These need to stop being virtual -- clients need to override the onDraw... versions
1848
1849void SkCanvas::drawPaint(const SkPaint& paint) {
1850 this->onDrawPaint(paint);
1851}
1852
1853void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1854 this->onDrawRect(r, paint);
1855}
1856
msarettdca352e2016-08-26 06:37:45 -07001857void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
1858 if (region.isEmpty()) {
1859 return;
1860 }
1861
1862 if (region.isRect()) {
1863 return this->drawIRect(region.getBounds(), paint);
1864 }
1865
1866 this->onDrawRegion(region, paint);
1867}
1868
reed41af9662015-01-05 07:49:08 -08001869void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1870 this->onDrawOval(r, paint);
1871}
1872
1873void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1874 this->onDrawRRect(rrect, paint);
1875}
1876
1877void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1878 this->onDrawPoints(mode, count, pts, paint);
1879}
1880
1881void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1882 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1883 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1884 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1885 indices, indexCount, paint);
1886}
1887
1888void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1889 this->onDrawPath(path, paint);
1890}
1891
reeda85d4d02015-05-06 12:56:48 -07001892void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001893 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001894 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001895}
1896
reede47829b2015-08-06 10:02:53 -07001897void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1898 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001899 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001900 if (dst.isEmpty() || src.isEmpty()) {
1901 return;
1902 }
1903 this->onDrawImageRect(image, &src, dst, paint, constraint);
1904}
reed41af9662015-01-05 07:49:08 -08001905
reed84984ef2015-07-17 07:09:43 -07001906void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1907 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001908 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001909 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001910}
1911
reede47829b2015-08-06 10:02:53 -07001912void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1913 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001914 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001915 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1916 constraint);
1917}
reede47829b2015-08-06 10:02:53 -07001918
reed4c21dc52015-06-25 12:32:03 -07001919void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1920 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001921 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001922 if (dst.isEmpty()) {
1923 return;
1924 }
msarett552bca92016-08-03 06:53:26 -07001925 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1926 this->onDrawImageNine(image, center, dst, paint);
1927 } else {
reede47829b2015-08-06 10:02:53 -07001928 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001929 }
reed4c21dc52015-06-25 12:32:03 -07001930}
1931
msarett16882062016-08-16 09:31:08 -07001932void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1933 const SkPaint* paint) {
1934 RETURN_ON_NULL(image);
1935 if (dst.isEmpty()) {
1936 return;
1937 }
msarett71df2d72016-09-30 12:41:42 -07001938
1939 SkIRect bounds;
1940 Lattice latticePlusBounds = lattice;
1941 if (!latticePlusBounds.fBounds) {
1942 bounds = SkIRect::MakeWH(image->width(), image->height());
1943 latticePlusBounds.fBounds = &bounds;
1944 }
1945
1946 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
1947 this->onDrawImageLattice(image, latticePlusBounds, dst, paint);
msarett16882062016-08-16 09:31:08 -07001948 } else {
1949 this->drawImageRect(image, dst, paint);
1950 }
1951}
1952
reed41af9662015-01-05 07:49:08 -08001953void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001954 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001955 return;
1956 }
reed41af9662015-01-05 07:49:08 -08001957 this->onDrawBitmap(bitmap, dx, dy, paint);
1958}
1959
reede47829b2015-08-06 10:02:53 -07001960void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001961 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001962 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001963 return;
1964 }
reede47829b2015-08-06 10:02:53 -07001965 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001966}
1967
reed84984ef2015-07-17 07:09:43 -07001968void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1969 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001970 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001971}
1972
reede47829b2015-08-06 10:02:53 -07001973void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1974 SrcRectConstraint constraint) {
1975 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1976 constraint);
1977}
reede47829b2015-08-06 10:02:53 -07001978
reed41af9662015-01-05 07:49:08 -08001979void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1980 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001981 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001982 return;
1983 }
msarett552bca92016-08-03 06:53:26 -07001984 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
1985 this->onDrawBitmapNine(bitmap, center, dst, paint);
1986 } else {
reeda5517e22015-07-14 10:54:12 -07001987 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001988 }
reed41af9662015-01-05 07:49:08 -08001989}
1990
msarettc573a402016-08-02 08:05:56 -07001991void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
1992 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07001993 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07001994 return;
1995 }
msarett71df2d72016-09-30 12:41:42 -07001996
1997 SkIRect bounds;
1998 Lattice latticePlusBounds = lattice;
1999 if (!latticePlusBounds.fBounds) {
2000 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2001 latticePlusBounds.fBounds = &bounds;
2002 }
2003
2004 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
2005 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, paint);
msarett552bca92016-08-03 06:53:26 -07002006 } else {
msarett16882062016-08-16 09:31:08 -07002007 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002008 }
msarettc573a402016-08-02 08:05:56 -07002009}
2010
reed71c3c762015-06-24 10:29:17 -07002011void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2012 const SkColor colors[], int count, SkXfermode::Mode mode,
2013 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002014 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002015 if (count <= 0) {
2016 return;
2017 }
2018 SkASSERT(atlas);
2019 SkASSERT(xform);
2020 SkASSERT(tex);
2021 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2022}
2023
reedf70b5312016-03-04 16:36:20 -08002024void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2025 if (key) {
2026 this->onDrawAnnotation(rect, key, value);
2027 }
2028}
2029
reede47829b2015-08-06 10:02:53 -07002030void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2031 const SkPaint* paint, SrcRectConstraint constraint) {
2032 if (src) {
2033 this->drawImageRect(image, *src, dst, paint, constraint);
2034 } else {
2035 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2036 dst, paint, constraint);
2037 }
2038}
2039void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2040 const SkPaint* paint, SrcRectConstraint constraint) {
2041 if (src) {
2042 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2043 } else {
2044 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2045 dst, paint, constraint);
2046 }
2047}
2048
tomhudsoncb3bd182016-05-18 07:24:16 -07002049void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2050 SkIRect layer_bounds = this->getTopLayerBounds();
2051 if (matrix) {
2052 *matrix = this->getTotalMatrix();
2053 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2054 }
2055 if (clip_bounds) {
2056 this->getClipDeviceBounds(clip_bounds);
2057 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2058 }
2059}
2060
reed@android.com8a1c16f2008-12-17 15:59:43 +00002061//////////////////////////////////////////////////////////////////////////////
2062// These are the virtual drawing methods
2063//////////////////////////////////////////////////////////////////////////////
2064
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002065void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002066 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002067 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2068 }
2069}
2070
reed41af9662015-01-05 07:49:08 -08002071void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002072 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002073 this->internalDrawPaint(paint);
2074}
2075
2076void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002077 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002078
2079 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002080 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002081 }
2082
reed@google.com4e2b3d32011-04-07 14:18:59 +00002083 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002084}
2085
reed41af9662015-01-05 07:49:08 -08002086void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2087 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002088 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089 if ((long)count <= 0) {
2090 return;
2091 }
2092
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002093 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002094 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002095 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002096 // special-case 2 points (common for drawing a single line)
2097 if (2 == count) {
2098 r.set(pts[0], pts[1]);
2099 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002100 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002101 }
senorblanco87e066e2015-10-28 11:23:36 -07002102 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2103 return;
2104 }
2105 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002106 }
reed@google.coma584aed2012-05-16 14:06:02 +00002107
halcanary96fcdcc2015-08-27 07:41:13 -07002108 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002109
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002110 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002111
reed@android.com8a1c16f2008-12-17 15:59:43 +00002112 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002113 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114 }
reed@google.com4b226022011-01-11 18:32:13 +00002115
reed@google.com4e2b3d32011-04-07 14:18:59 +00002116 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117}
2118
reed4a167172016-08-18 17:15:25 -07002119static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
2120 return ((intptr_t)paint.getImageFilter() |
2121#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
2122 (intptr_t)canvas->getDrawFilter() |
2123#endif
2124 (intptr_t)paint.getLooper() ) != 0;
2125}
2126
reed41af9662015-01-05 07:49:08 -08002127void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002128 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002129 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002130 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002131 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002132 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2133 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2134 SkRect tmp(r);
2135 tmp.sort();
2136
senorblanco87e066e2015-10-28 11:23:36 -07002137 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2138 return;
2139 }
2140 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002141 }
reed@google.com4b226022011-01-11 18:32:13 +00002142
reed4a167172016-08-18 17:15:25 -07002143 if (needs_autodrawlooper(this, paint)) {
2144 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002145
reed4a167172016-08-18 17:15:25 -07002146 while (iter.next()) {
2147 iter.fDevice->drawRect(iter, r, looper.paint());
2148 }
2149
2150 LOOPER_END
2151 } else {
2152 this->predrawNotify(bounds, &paint, false);
2153 SkDrawIter iter(this);
2154 while (iter.next()) {
2155 iter.fDevice->drawRect(iter, r, paint);
2156 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002157 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002158}
2159
msarett44df6512016-08-25 13:54:30 -07002160void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
2161 SkRect storage;
2162 SkRect regionRect = SkRect::Make(region.getBounds());
2163 const SkRect* bounds = nullptr;
2164 if (paint.canComputeFastBounds()) {
2165 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2166 return;
2167 }
2168 bounds = &regionRect;
2169 }
2170
2171 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
2172
2173 while (iter.next()) {
2174 iter.fDevice->drawRegion(iter, region, looper.paint());
2175 }
2176
2177 LOOPER_END
2178}
2179
reed41af9662015-01-05 07:49:08 -08002180void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002181 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002182 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002183 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002184 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002185 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2186 return;
2187 }
2188 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002189 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002190
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002191 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002192
2193 while (iter.next()) {
2194 iter.fDevice->drawOval(iter, oval, looper.paint());
2195 }
2196
2197 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002198}
2199
bsalomonac3aa242016-08-19 11:25:19 -07002200void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2201 SkScalar sweepAngle, bool useCenter,
2202 const SkPaint& paint) {
2203 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
2204 const SkRect* bounds = nullptr;
2205 if (paint.canComputeFastBounds()) {
2206 SkRect storage;
2207 // Note we're using the entire oval as the bounds.
2208 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2209 return;
2210 }
2211 bounds = &oval;
2212 }
2213
2214 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
2215
2216 while (iter.next()) {
2217 iter.fDevice->drawArc(iter, oval, startAngle, sweepAngle, useCenter, looper.paint());
2218 }
2219
2220 LOOPER_END
2221}
2222
reed41af9662015-01-05 07:49:08 -08002223void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002224 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002225 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002226 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002227 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002228 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2229 return;
2230 }
2231 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002232 }
2233
2234 if (rrect.isRect()) {
2235 // call the non-virtual version
2236 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002237 return;
2238 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002239 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002240 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2241 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002242 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002243
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002244 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002245
2246 while (iter.next()) {
2247 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2248 }
2249
2250 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002251}
2252
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002253void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2254 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002255 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002256 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002257 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002258 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2259 return;
2260 }
2261 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002262 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002263
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002264 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002265
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002266 while (iter.next()) {
2267 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2268 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002269
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002270 LOOPER_END
2271}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002272
reed41af9662015-01-05 07:49:08 -08002273void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002274 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002275 if (!path.isFinite()) {
2276 return;
2277 }
2278
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002279 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002280 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002281 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002282 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002283 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2284 return;
2285 }
2286 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002287 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002288
2289 const SkRect& r = path.getBounds();
2290 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002291 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002292 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002293 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002294 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002295 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002296
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002297 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002298
2299 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002300 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002301 }
2302
reed@google.com4e2b3d32011-04-07 14:18:59 +00002303 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002304}
2305
reed262a71b2015-12-05 13:07:27 -08002306bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002307 if (!paint.getImageFilter()) {
2308 return false;
2309 }
2310
2311 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002312 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002313 return false;
2314 }
2315
2316 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2317 // Once we can filter and the filter will return a result larger than itself, we should be
2318 // able to remove this constraint.
2319 // skbug.com/4526
2320 //
2321 SkPoint pt;
2322 ctm.mapXY(x, y, &pt);
2323 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2324 return ir.contains(fMCRec->fRasterClip.getBounds());
2325}
2326
reeda85d4d02015-05-06 12:56:48 -07002327void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002328 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002329 SkRect bounds = SkRect::MakeXYWH(x, y,
2330 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002331 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002332 SkRect tmp = bounds;
2333 if (paint) {
2334 paint->computeFastBounds(tmp, &tmp);
2335 }
2336 if (this->quickReject(tmp)) {
2337 return;
2338 }
reeda85d4d02015-05-06 12:56:48 -07002339 }
halcanary9d524f22016-03-29 09:03:52 -07002340
reeda85d4d02015-05-06 12:56:48 -07002341 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002342 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002343 paint = lazy.init();
2344 }
reed262a71b2015-12-05 13:07:27 -08002345
reeda2217ef2016-07-20 06:04:34 -07002346 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002347 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2348 *paint);
2349 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002350 special = this->getDevice()->makeSpecial(image);
2351 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002352 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002353 }
2354 }
2355
reed262a71b2015-12-05 13:07:27 -08002356 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2357
reeda85d4d02015-05-06 12:56:48 -07002358 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002359 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002360 if (special) {
2361 SkPoint pt;
2362 iter.fMatrix->mapXY(x, y, &pt);
2363 iter.fDevice->drawSpecial(iter, special.get(),
2364 SkScalarRoundToInt(pt.fX),
2365 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002366 } else {
2367 iter.fDevice->drawImage(iter, image, x, y, pnt);
2368 }
reeda85d4d02015-05-06 12:56:48 -07002369 }
halcanary9d524f22016-03-29 09:03:52 -07002370
reeda85d4d02015-05-06 12:56:48 -07002371 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002372}
2373
reed41af9662015-01-05 07:49:08 -08002374void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002375 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002376 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002377 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002378 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002379 if (paint) {
2380 paint->computeFastBounds(dst, &storage);
2381 }
2382 if (this->quickReject(storage)) {
2383 return;
2384 }
reeda85d4d02015-05-06 12:56:48 -07002385 }
2386 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002387 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002388 paint = lazy.init();
2389 }
halcanary9d524f22016-03-29 09:03:52 -07002390
senorblancoc41e7e12015-12-07 12:51:30 -08002391 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002392 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002393
reeda85d4d02015-05-06 12:56:48 -07002394 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002395 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002396 }
halcanary9d524f22016-03-29 09:03:52 -07002397
reeda85d4d02015-05-06 12:56:48 -07002398 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002399}
2400
reed41af9662015-01-05 07:49:08 -08002401void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002402 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002403 SkDEBUGCODE(bitmap.validate();)
2404
reed33366972015-10-08 09:22:02 -07002405 if (bitmap.drawsNothing()) {
2406 return;
2407 }
2408
2409 SkLazyPaint lazy;
2410 if (nullptr == paint) {
2411 paint = lazy.init();
2412 }
2413
2414 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2415
2416 SkRect storage;
2417 const SkRect* bounds = nullptr;
2418 if (paint->canComputeFastBounds()) {
2419 bitmap.getBounds(&storage);
2420 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002421 SkRect tmp = storage;
2422 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2423 return;
2424 }
2425 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002426 }
reed@google.com4b226022011-01-11 18:32:13 +00002427
reeda2217ef2016-07-20 06:04:34 -07002428 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002429 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2430 *paint);
2431 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002432 special = this->getDevice()->makeSpecial(bitmap);
2433 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002434 drawAsSprite = false;
2435 }
2436 }
2437
reed262a71b2015-12-05 13:07:27 -08002438 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002439
2440 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002441 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002442 if (special) {
reed262a71b2015-12-05 13:07:27 -08002443 SkPoint pt;
2444 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002445 iter.fDevice->drawSpecial(iter, special.get(),
2446 SkScalarRoundToInt(pt.fX),
2447 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002448 } else {
2449 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2450 }
reed33366972015-10-08 09:22:02 -07002451 }
msarettfbfa2582016-08-12 08:29:08 -07002452
reed33366972015-10-08 09:22:02 -07002453 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002454}
2455
reed@google.com9987ec32011-09-07 11:57:52 +00002456// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002457void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002458 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002459 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002460 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002461 return;
2462 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002463
halcanary96fcdcc2015-08-27 07:41:13 -07002464 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002465 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002466 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2467 return;
2468 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002469 }
reed@google.com3d608122011-11-21 15:16:16 +00002470
reed@google.com33535f32012-09-25 15:37:50 +00002471 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002472 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002473 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002474 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002475
senorblancoc41e7e12015-12-07 12:51:30 -08002476 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002477 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002478
reed@google.com33535f32012-09-25 15:37:50 +00002479 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002480 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002481 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002482
reed@google.com33535f32012-09-25 15:37:50 +00002483 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002484}
2485
reed41af9662015-01-05 07:49:08 -08002486void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002487 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002488 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002489 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002490 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002491}
2492
reed4c21dc52015-06-25 12:32:03 -07002493void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2494 const SkPaint* paint) {
2495 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002496
halcanary96fcdcc2015-08-27 07:41:13 -07002497 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002498 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002499 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2500 return;
2501 }
reed@google.com3d608122011-11-21 15:16:16 +00002502 }
halcanary9d524f22016-03-29 09:03:52 -07002503
reed4c21dc52015-06-25 12:32:03 -07002504 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002505 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002506 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002507 }
halcanary9d524f22016-03-29 09:03:52 -07002508
senorblancoc41e7e12015-12-07 12:51:30 -08002509 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002510
reed4c21dc52015-06-25 12:32:03 -07002511 while (iter.next()) {
2512 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002513 }
halcanary9d524f22016-03-29 09:03:52 -07002514
reed4c21dc52015-06-25 12:32:03 -07002515 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002516}
2517
reed41af9662015-01-05 07:49:08 -08002518void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2519 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002520 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002521 SkDEBUGCODE(bitmap.validate();)
2522
halcanary96fcdcc2015-08-27 07:41:13 -07002523 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002524 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002525 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2526 return;
2527 }
reed4c21dc52015-06-25 12:32:03 -07002528 }
halcanary9d524f22016-03-29 09:03:52 -07002529
reed4c21dc52015-06-25 12:32:03 -07002530 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002531 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002532 paint = lazy.init();
2533 }
halcanary9d524f22016-03-29 09:03:52 -07002534
senorblancoc41e7e12015-12-07 12:51:30 -08002535 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002536
reed4c21dc52015-06-25 12:32:03 -07002537 while (iter.next()) {
2538 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2539 }
halcanary9d524f22016-03-29 09:03:52 -07002540
reed4c21dc52015-06-25 12:32:03 -07002541 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002542}
2543
msarett16882062016-08-16 09:31:08 -07002544void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2545 const SkPaint* paint) {
2546 if (nullptr == paint || paint->canComputeFastBounds()) {
2547 SkRect storage;
2548 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2549 return;
2550 }
2551 }
2552
2553 SkLazyPaint lazy;
2554 if (nullptr == paint) {
2555 paint = lazy.init();
2556 }
2557
2558 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2559
2560 while (iter.next()) {
2561 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2562 }
2563
2564 LOOPER_END
2565}
2566
2567void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2568 const SkRect& dst, const SkPaint* paint) {
2569 if (nullptr == paint || paint->canComputeFastBounds()) {
2570 SkRect storage;
2571 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2572 return;
2573 }
2574 }
2575
2576 SkLazyPaint lazy;
2577 if (nullptr == paint) {
2578 paint = lazy.init();
2579 }
2580
2581 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2582
2583 while (iter.next()) {
2584 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2585 }
2586
2587 LOOPER_END
2588}
2589
reed@google.comf67e4cf2011-03-15 20:56:58 +00002590class SkDeviceFilteredPaint {
2591public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002592 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002593 uint32_t filteredFlags = device->filterTextFlags(paint);
2594 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002595 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002596 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002597 fPaint = newPaint;
2598 } else {
2599 fPaint = &paint;
2600 }
2601 }
2602
reed@google.comf67e4cf2011-03-15 20:56:58 +00002603 const SkPaint& paint() const { return *fPaint; }
2604
2605private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002606 const SkPaint* fPaint;
2607 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002608};
2609
bungeman@google.com52c748b2011-08-22 21:30:43 +00002610void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2611 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002612 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002613 draw.fDevice->drawRect(draw, r, paint);
2614 } else {
2615 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002616 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002617 draw.fDevice->drawRect(draw, r, p);
2618 }
2619}
2620
2621void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2622 const char text[], size_t byteLength,
2623 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002624 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002625
2626 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002627 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002628 draw.fRC->isEmpty() ||
reed374772b2016-10-05 17:33:02 -07002629 (paint.getAlpha() == 0 && paint.isSrcOver())) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002630 return;
2631 }
2632
2633 SkScalar width = 0;
2634 SkPoint start;
2635
2636 start.set(0, 0); // to avoid warning
2637 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2638 SkPaint::kStrikeThruText_Flag)) {
2639 width = paint.measureText(text, byteLength);
2640
2641 SkScalar offsetX = 0;
2642 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2643 offsetX = SkScalarHalf(width);
2644 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2645 offsetX = width;
2646 }
2647 start.set(x - offsetX, y);
2648 }
2649
2650 if (0 == width) {
2651 return;
2652 }
2653
2654 uint32_t flags = paint.getFlags();
2655
2656 if (flags & (SkPaint::kUnderlineText_Flag |
2657 SkPaint::kStrikeThruText_Flag)) {
2658 SkScalar textSize = paint.getTextSize();
2659 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2660 SkRect r;
2661
2662 r.fLeft = start.fX;
2663 r.fRight = start.fX + width;
2664
2665 if (flags & SkPaint::kUnderlineText_Flag) {
2666 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2667 start.fY);
2668 r.fTop = offset;
2669 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002670 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002671 }
2672 if (flags & SkPaint::kStrikeThruText_Flag) {
2673 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2674 start.fY);
2675 r.fTop = offset;
2676 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002677 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002678 }
2679 }
2680}
2681
reed@google.come0d9ce82014-04-23 04:00:17 +00002682void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2683 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002684 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002685
2686 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002687 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002688 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002689 DrawTextDecorations(iter, dfp.paint(),
2690 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002691 }
2692
reed@google.com4e2b3d32011-04-07 14:18:59 +00002693 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002694}
2695
reed@google.come0d9ce82014-04-23 04:00:17 +00002696void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2697 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002698 SkPoint textOffset = SkPoint::Make(0, 0);
2699
halcanary96fcdcc2015-08-27 07:41:13 -07002700 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002701
reed@android.com8a1c16f2008-12-17 15:59:43 +00002702 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002703 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002704 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002705 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002706 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002707
reed@google.com4e2b3d32011-04-07 14:18:59 +00002708 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002709}
2710
reed@google.come0d9ce82014-04-23 04:00:17 +00002711void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2712 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002713
2714 SkPoint textOffset = SkPoint::Make(0, constY);
2715
halcanary96fcdcc2015-08-27 07:41:13 -07002716 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002717
reed@android.com8a1c16f2008-12-17 15:59:43 +00002718 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002719 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002720 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002721 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002722 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002723
reed@google.com4e2b3d32011-04-07 14:18:59 +00002724 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002725}
2726
reed@google.come0d9ce82014-04-23 04:00:17 +00002727void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2728 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002729 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002730
reed@android.com8a1c16f2008-12-17 15:59:43 +00002731 while (iter.next()) {
2732 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002733 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002734 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002735
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002736 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002737}
2738
reed45561a02016-07-07 12:47:17 -07002739void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2740 const SkRect* cullRect, const SkPaint& paint) {
2741 if (cullRect && this->quickReject(*cullRect)) {
2742 return;
2743 }
2744
2745 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2746
2747 while (iter.next()) {
2748 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2749 }
2750
2751 LOOPER_END
2752}
2753
fmalita00d5c2c2014-08-21 08:53:26 -07002754void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2755 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002756
fmalita85d5eb92015-03-04 11:20:12 -08002757 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002758 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002759 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002760 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002761 SkRect tmp;
2762 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2763 return;
2764 }
2765 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002766 }
2767
fmalita024f9962015-03-03 19:08:17 -08002768 // We cannot filter in the looper as we normally do, because the paint is
2769 // incomplete at this point (text-related attributes are embedded within blob run paints).
2770 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002771 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002772
fmalita85d5eb92015-03-04 11:20:12 -08002773 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002774
fmalitaaa1b9122014-08-28 14:32:24 -07002775 while (iter.next()) {
2776 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002777 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002778 }
2779
fmalitaaa1b9122014-08-28 14:32:24 -07002780 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002781
2782 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002783}
2784
reed@google.come0d9ce82014-04-23 04:00:17 +00002785// These will become non-virtual, so they always call the (virtual) onDraw... method
2786void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2787 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002788 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002789 if (byteLength) {
2790 this->onDrawText(text, byteLength, x, y, paint);
2791 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002792}
2793void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2794 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002795 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002796 if (byteLength) {
2797 this->onDrawPosText(text, byteLength, pos, paint);
2798 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002799}
2800void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2801 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002802 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002803 if (byteLength) {
2804 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2805 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002806}
2807void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2808 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002809 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002810 if (byteLength) {
2811 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2812 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002813}
reed45561a02016-07-07 12:47:17 -07002814void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2815 const SkRect* cullRect, const SkPaint& paint) {
2816 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2817 if (byteLength) {
2818 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2819 }
2820}
fmalita00d5c2c2014-08-21 08:53:26 -07002821void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2822 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002823 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002824 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002825 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002826}
reed@google.come0d9ce82014-04-23 04:00:17 +00002827
reed41af9662015-01-05 07:49:08 -08002828void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2829 const SkPoint verts[], const SkPoint texs[],
2830 const SkColor colors[], SkXfermode* xmode,
2831 const uint16_t indices[], int indexCount,
2832 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002833 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002834 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002835
reed@android.com8a1c16f2008-12-17 15:59:43 +00002836 while (iter.next()) {
2837 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002838 colors, xmode, indices, indexCount,
2839 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002840 }
reed@google.com4b226022011-01-11 18:32:13 +00002841
reed@google.com4e2b3d32011-04-07 14:18:59 +00002842 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002843}
2844
dandovb3c9d1c2014-08-12 08:34:29 -07002845void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2846 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002847 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002848 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002849 return;
2850 }
mtklein6cfa73a2014-08-13 13:33:49 -07002851
msarett9340c262016-09-22 05:20:21 -07002852 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2853}
2854
2855void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2856 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002857 // Since a patch is always within the convex hull of the control points, we discard it when its
2858 // bounding rectangle is completely outside the current clip.
2859 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002860 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002861 if (this->quickReject(bounds)) {
2862 return;
2863 }
mtklein6cfa73a2014-08-13 13:33:49 -07002864
halcanary96fcdcc2015-08-27 07:41:13 -07002865 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002866
dandovecfff212014-08-04 10:02:00 -07002867 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002868 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002869 }
mtklein6cfa73a2014-08-13 13:33:49 -07002870
dandovecfff212014-08-04 10:02:00 -07002871 LOOPER_END
2872}
2873
reeda8db7282015-07-07 10:22:31 -07002874void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002875 RETURN_ON_NULL(dr);
2876 if (x || y) {
2877 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2878 this->onDrawDrawable(dr, &matrix);
2879 } else {
2880 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002881 }
2882}
2883
reeda8db7282015-07-07 10:22:31 -07002884void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002885 RETURN_ON_NULL(dr);
2886 if (matrix && matrix->isIdentity()) {
2887 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002888 }
reede3b38ce2016-01-08 09:18:44 -08002889 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002890}
2891
2892void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002893 // drawable bounds are no longer reliable (e.g. android displaylist)
2894 // so don't use them for quick-reject
reeda8db7282015-07-07 10:22:31 -07002895 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002896}
2897
reed71c3c762015-06-24 10:29:17 -07002898void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2899 const SkColor colors[], int count, SkXfermode::Mode mode,
2900 const SkRect* cull, const SkPaint* paint) {
2901 if (cull && this->quickReject(*cull)) {
2902 return;
2903 }
2904
2905 SkPaint pnt;
2906 if (paint) {
2907 pnt = *paint;
2908 }
halcanary9d524f22016-03-29 09:03:52 -07002909
halcanary96fcdcc2015-08-27 07:41:13 -07002910 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002911 while (iter.next()) {
2912 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2913 }
2914 LOOPER_END
2915}
2916
reedf70b5312016-03-04 16:36:20 -08002917void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2918 SkASSERT(key);
2919
2920 SkPaint paint;
2921 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2922 while (iter.next()) {
2923 iter.fDevice->drawAnnotation(iter, rect, key, value);
2924 }
2925 LOOPER_END
2926}
2927
reed@android.com8a1c16f2008-12-17 15:59:43 +00002928//////////////////////////////////////////////////////////////////////////////
2929// These methods are NOT virtual, and therefore must call back into virtual
2930// methods, rather than actually drawing themselves.
2931//////////////////////////////////////////////////////////////////////////////
2932
reed374772b2016-10-05 17:33:02 -07002933void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002934 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002935 SkPaint paint;
2936
2937 paint.setARGB(a, r, g, b);
reed374772b2016-10-05 17:33:02 -07002938 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002939 this->drawPaint(paint);
2940}
2941
reed374772b2016-10-05 17:33:02 -07002942void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002943 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002944 SkPaint paint;
2945
2946 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002947 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002948 this->drawPaint(paint);
2949}
2950
2951void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002952 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002953 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002954
reed@android.com8a1c16f2008-12-17 15:59:43 +00002955 pt.set(x, y);
2956 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2957}
2958
2959void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002960 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002961 SkPoint pt;
2962 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002963
reed@android.com8a1c16f2008-12-17 15:59:43 +00002964 pt.set(x, y);
2965 paint.setColor(color);
2966 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2967}
2968
2969void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2970 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002971 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002972 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002973
reed@android.com8a1c16f2008-12-17 15:59:43 +00002974 pts[0].set(x0, y0);
2975 pts[1].set(x1, y1);
2976 this->drawPoints(kLines_PointMode, 2, pts, paint);
2977}
2978
2979void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2980 SkScalar right, SkScalar bottom,
2981 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002982 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002983 SkRect r;
2984
2985 r.set(left, top, right, bottom);
2986 this->drawRect(r, paint);
2987}
2988
2989void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2990 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002991 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002992 if (radius < 0) {
2993 radius = 0;
2994 }
2995
2996 SkRect r;
2997 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00002998 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002999}
3000
3001void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3002 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003003 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003004 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003005 SkRRect rrect;
3006 rrect.setRectXY(r, rx, ry);
3007 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003008 } else {
3009 this->drawRect(r, paint);
3010 }
3011}
3012
reed@android.com8a1c16f2008-12-17 15:59:43 +00003013void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3014 SkScalar sweepAngle, bool useCenter,
3015 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003016 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
bsalomon21af9ca2016-08-25 12:29:23 -07003017 if (oval.isEmpty() || !sweepAngle) {
3018 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003019 }
bsalomon21af9ca2016-08-25 12:29:23 -07003020 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003021}
3022
3023void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3024 const SkPath& path, SkScalar hOffset,
3025 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003026 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003027 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003028
reed@android.com8a1c16f2008-12-17 15:59:43 +00003029 matrix.setTranslate(hOffset, vOffset);
3030 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3031}
3032
reed@android.comf76bacf2009-05-13 14:00:33 +00003033///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003034
3035/**
3036 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3037 * against the playback cost of recursing into the subpicture to get at its actual ops.
3038 *
3039 * For now we pick a conservatively small value, though measurement (and other heuristics like
3040 * the type of ops contained) may justify changing this value.
3041 */
3042#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003043
reedd5fa1a42014-08-09 11:08:05 -07003044void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003045 RETURN_ON_NULL(picture);
3046
reed1c2c4412015-04-30 13:09:24 -07003047 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003048 if (matrix && matrix->isIdentity()) {
3049 matrix = nullptr;
3050 }
3051 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3052 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3053 picture->playback(this);
3054 } else {
3055 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003056 }
3057}
robertphillips9b14f262014-06-04 05:40:44 -07003058
reedd5fa1a42014-08-09 11:08:05 -07003059void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3060 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003061 if (!paint || paint->canComputeFastBounds()) {
3062 SkRect bounds = picture->cullRect();
3063 if (paint) {
3064 paint->computeFastBounds(bounds, &bounds);
3065 }
3066 if (matrix) {
3067 matrix->mapRect(&bounds);
3068 }
3069 if (this->quickReject(bounds)) {
3070 return;
3071 }
3072 }
3073
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003074 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003075 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003076}
3077
vjiaoblack95302da2016-07-21 10:25:54 -07003078#ifdef SK_EXPERIMENTAL_SHADOWING
3079void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3080 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003081 const SkPaint* paint,
3082 const SkShadowParams& params) {
vjiaoblack95302da2016-07-21 10:25:54 -07003083 RETURN_ON_NULL(picture);
3084
3085 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3086
vjiaoblacke6f5d562016-08-25 06:30:23 -07003087 this->onDrawShadowedPicture(picture, matrix, paint, params);
vjiaoblack95302da2016-07-21 10:25:54 -07003088}
3089
3090void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3091 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003092 const SkPaint* paint,
3093 const SkShadowParams& params) {
vjiaoblack904527d2016-08-09 09:32:09 -07003094 if (!paint || paint->canComputeFastBounds()) {
3095 SkRect bounds = picture->cullRect();
3096 if (paint) {
3097 paint->computeFastBounds(bounds, &bounds);
3098 }
3099 if (matrix) {
3100 matrix->mapRect(&bounds);
3101 }
3102 if (this->quickReject(bounds)) {
3103 return;
3104 }
3105 }
3106
3107 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3108
vjiaoblacke6f5d562016-08-25 06:30:23 -07003109 sk_sp<SkImage> povDepthMap;
3110 sk_sp<SkImage> diffuseMap;
3111
vjiaoblack904527d2016-08-09 09:32:09 -07003112 // povDepthMap
3113 {
3114 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003115 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3116 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003117 sk_sp<SkLights> povLight = builder.finish();
3118
3119 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3120 picture->cullRect().height(),
3121 kBGRA_8888_SkColorType,
3122 kOpaque_SkAlphaType);
3123
3124 // Create a new surface (that matches the backend of canvas)
3125 // to create the povDepthMap
3126 sk_sp<SkSurface> surf(this->makeSurface(info));
3127
3128 // Wrap another SPFCanvas around the surface
3129 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3130 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3131
3132 // set the depth map canvas to have the light as the user's POV
3133 depthMapCanvas->setLights(std::move(povLight));
3134
3135 depthMapCanvas->drawPicture(picture);
vjiaoblack904527d2016-08-09 09:32:09 -07003136 povDepthMap = surf->makeImageSnapshot();
3137 }
3138
3139 // diffuseMap
3140 {
3141 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3142 picture->cullRect().height(),
3143 kBGRA_8888_SkColorType,
3144 kOpaque_SkAlphaType);
3145
3146 sk_sp<SkSurface> surf(this->makeSurface(info));
3147 surf->getCanvas()->drawPicture(picture);
3148
3149 diffuseMap = surf->makeImageSnapshot();
3150 }
vjiaoblack904527d2016-08-09 09:32:09 -07003151
3152 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3153 SkShader::kClamp_TileMode);
vjiaoblack904527d2016-08-09 09:32:09 -07003154 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3155 SkShader::kClamp_TileMode);
vjiaoblackb2796fd2016-09-09 09:22:39 -07003156
3157 // TODO: pass the depth to the shader in vertices, or uniforms
3158 // so we don't have to render depth and color separately
3159 for (int i = 0; i < fLights->numLights(); ++i) {
3160 // skip over ambient lights; they don't cast shadows
3161 // lights that have shadow maps do not need updating (because lights are immutable)
3162 sk_sp<SkImage> depthMap;
3163 SkISize shMapSize;
3164
3165 if (fLights->light(i).getShadowMap() != nullptr) {
3166 continue;
3167 }
3168
3169 if (fLights->light(i).isRadial()) {
3170 shMapSize.fHeight = 1;
3171 shMapSize.fWidth = (int) picture->cullRect().width();
3172
3173 SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1,
3174 kBGRA_8888_SkColorType,
3175 kOpaque_SkAlphaType);
3176
3177 // Create new surface (that matches the backend of canvas)
3178 // for each shadow map
3179 sk_sp<SkSurface> surf(this->makeSurface(info));
3180
3181 // Wrap another SPFCanvas around the surface
3182 SkCanvas* depthMapCanvas = surf->getCanvas();
3183
3184 SkLights::Builder builder;
3185 builder.add(fLights->light(i));
3186 sk_sp<SkLights> curLight = builder.finish();
3187
3188 sk_sp<SkShader> shadowMapShader;
3189 shadowMapShader = SkRadialShadowMapShader::Make(
3190 povDepthShader, curLight,
3191 (int) picture->cullRect().width(),
3192 (int) picture->cullRect().height());
3193
3194 SkPaint shadowMapPaint;
3195 shadowMapPaint.setShader(std::move(shadowMapShader));
3196
3197 depthMapCanvas->setLights(curLight);
3198
3199 depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(),
3200 diffuseMap->height()),
3201 shadowMapPaint);
3202
3203 depthMap = surf->makeImageSnapshot();
3204
3205 } else {
3206 // TODO: compute the correct size of the depth map from the light properties
3207 // TODO: maybe add a kDepth_8_SkColorType
3208 // TODO: find actual max depth of picture
3209 shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3210 fLights->light(i), 255,
3211 (int) picture->cullRect().width(),
3212 (int) picture->cullRect().height());
3213
3214 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3215 kBGRA_8888_SkColorType,
3216 kOpaque_SkAlphaType);
3217
3218 // Create a new surface (that matches the backend of canvas)
3219 // for each shadow map
3220 sk_sp<SkSurface> surf(this->makeSurface(info));
3221
3222 // Wrap another SPFCanvas around the surface
3223 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3224 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3225 depthMapCanvas->setShadowParams(params);
3226
3227 // set the depth map canvas to have the light we're drawing.
3228 SkLights::Builder builder;
3229 builder.add(fLights->light(i));
3230 sk_sp<SkLights> curLight = builder.finish();
3231 depthMapCanvas->setLights(std::move(curLight));
3232
3233 depthMapCanvas->drawPicture(picture);
3234 depthMap = surf->makeImageSnapshot();
3235 }
3236
3237 if (params.fType == SkShadowParams::kNoBlur_ShadowType) {
3238 fLights->light(i).setShadowMap(std::move(depthMap));
3239 } else if (params.fType == SkShadowParams::kVariance_ShadowType) {
3240 // we blur the variance map
3241 SkPaint blurPaint;
3242 blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius,
3243 params.fShadowRadius, nullptr));
3244
3245 SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3246 kBGRA_8888_SkColorType,
3247 kOpaque_SkAlphaType);
3248
3249 sk_sp<SkSurface> blurSurf(this->makeSurface(blurInfo));
3250
3251 blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
3252
3253 fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
3254 }
3255 }
3256
3257 SkPaint shadowPaint;
vjiaoblack904527d2016-08-09 09:32:09 -07003258 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3259 std::move(diffuseShader),
vjiaoblackb2796fd2016-09-09 09:22:39 -07003260 fLights,
vjiaoblack904527d2016-08-09 09:32:09 -07003261 diffuseMap->width(),
vjiaoblacke6f5d562016-08-25 06:30:23 -07003262 diffuseMap->height(),
3263 params);
vjiaoblack904527d2016-08-09 09:32:09 -07003264
3265 shadowPaint.setShader(shadowShader);
3266
3267 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003268}
3269#endif
3270
reed@android.com8a1c16f2008-12-17 15:59:43 +00003271///////////////////////////////////////////////////////////////////////////////
3272///////////////////////////////////////////////////////////////////////////////
3273
reed3aafe112016-08-18 12:45:34 -07003274SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003275 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003276
3277 SkASSERT(canvas);
3278
reed3aafe112016-08-18 12:45:34 -07003279 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003280 fDone = !fImpl->next();
3281}
3282
3283SkCanvas::LayerIter::~LayerIter() {
3284 fImpl->~SkDrawIter();
3285}
3286
3287void SkCanvas::LayerIter::next() {
3288 fDone = !fImpl->next();
3289}
3290
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003291SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003292 return fImpl->getDevice();
3293}
3294
3295const SkMatrix& SkCanvas::LayerIter::matrix() const {
3296 return fImpl->getMatrix();
3297}
3298
3299const SkPaint& SkCanvas::LayerIter::paint() const {
3300 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003301 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003302 paint = &fDefaultPaint;
3303 }
3304 return *paint;
3305}
3306
reed1e7f5e72016-04-27 07:49:17 -07003307const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003308int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3309int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003310
3311///////////////////////////////////////////////////////////////////////////////
3312
fmalitac3b589a2014-06-05 12:40:07 -07003313SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003314
3315///////////////////////////////////////////////////////////////////////////////
3316
3317static bool supported_for_raster_canvas(const SkImageInfo& info) {
3318 switch (info.alphaType()) {
3319 case kPremul_SkAlphaType:
3320 case kOpaque_SkAlphaType:
3321 break;
3322 default:
3323 return false;
3324 }
3325
3326 switch (info.colorType()) {
3327 case kAlpha_8_SkColorType:
3328 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003329 case kN32_SkColorType:
junov9e3dbdf2016-10-13 13:14:27 -07003330 case kRGBA_F16_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003331 break;
3332 default:
3333 return false;
3334 }
3335
3336 return true;
3337}
3338
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003339SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3340 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003341 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003342 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003343
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003344 SkBitmap bitmap;
3345 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003346 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003347 }
halcanary385fe4d2015-08-26 13:07:48 -07003348 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003349}
reedd5fa1a42014-08-09 11:08:05 -07003350
3351///////////////////////////////////////////////////////////////////////////////
3352
3353SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003354 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003355 : fCanvas(canvas)
3356 , fSaveCount(canvas->getSaveCount())
3357{
bsalomon49f085d2014-09-05 13:34:00 -07003358 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003359 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003360 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003361 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003362 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003363 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003364 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003365 canvas->save();
3366 }
mtklein6cfa73a2014-08-13 13:33:49 -07003367
bsalomon49f085d2014-09-05 13:34:00 -07003368 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003369 canvas->concat(*matrix);
3370 }
3371}
3372
3373SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3374 fCanvas->restoreToCount(fSaveCount);
3375}
reede8f30622016-03-23 18:59:25 -07003376
3377#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3378SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3379 return this->makeSurface(info, props).release();
3380}
3381#endif
reed73603f32016-09-20 08:42:38 -07003382
3383/////////////////////////////////
3384
3385const SkCanvas::ClipOp SkCanvas::kDifference_Op;
3386const SkCanvas::ClipOp SkCanvas::kIntersect_Op;
3387const SkCanvas::ClipOp SkCanvas::kUnion_Op;
3388const SkCanvas::ClipOp SkCanvas::kXOR_Op;
3389const SkCanvas::ClipOp SkCanvas::kReverseDifference_Op;
3390const SkCanvas::ClipOp SkCanvas::kReplace_Op;
3391
3392static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3393static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3394static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3395static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3396static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3397static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");