blob: bcda0f2526ca712a95eb4525da0dc8172f1ee707 [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) {
junov@google.com4370aed2012-01-18 16:21:08 +0000322 canvas = canvas->canvasForDrawIter();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000323 canvas->updateDeviceCMCache();
324
reed687fa1c2015-04-07 08:00:56 -0700325 fClipStack = canvas->fClipStack;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000326 fCurrLayer = canvas->fMCRec->fTopLayer;
reed02f9ed72016-09-06 09:06:18 -0700327
328 fMultiDeviceCS = nullptr;
329 if (fCurrLayer->fNext) {
330 fMultiDeviceCS = canvas->fClipStack;
331 fMultiDeviceCS->save();
332 }
333 }
334
335 ~SkDrawIter() {
336 if (fMultiDeviceCS) {
337 fMultiDeviceCS->restore();
338 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000339 }
reed@google.com4b226022011-01-11 18:32:13 +0000340
reed@android.com8a1c16f2008-12-17 15:59:43 +0000341 bool next() {
reed02f9ed72016-09-06 09:06:18 -0700342 if (fMultiDeviceCS && fDevice) {
343 // remove the previous device's bounds
reed73603f32016-09-20 08:42:38 -0700344 fMultiDeviceCS->clipDevRect(compute_device_bounds(fDevice), SkCanvas::kDifference_Op);
reed02f9ed72016-09-06 09:06:18 -0700345 }
346
reed@android.com8a1c16f2008-12-17 15:59:43 +0000347 // skip over recs with empty clips
reed3aafe112016-08-18 12:45:34 -0700348 while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
349 fCurrLayer = fCurrLayer->fNext;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000350 }
351
reed@google.comf68c5e22012-02-24 16:38:58 +0000352 const DeviceCM* rec = fCurrLayer;
353 if (rec && rec->fDevice) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000354
355 fMatrix = rec->fMatrix;
reed@google.com045e62d2011-10-24 12:19:46 +0000356 fRC = &rec->fClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000357 fDevice = rec->fDevice;
reed41e010c2015-06-09 12:16:53 -0700358 if (!fDevice->accessPixels(&fDst)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700359 fDst.reset(fDevice->imageInfo(), nullptr, 0);
reed41e010c2015-06-09 12:16:53 -0700360 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000361 fPaint = rec->fPaint;
reed@android.comf2b98d62010-12-20 18:26:13 +0000362 SkDEBUGCODE(this->validate();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000363
364 fCurrLayer = rec->fNext;
halcanary96fcdcc2015-08-27 07:41:13 -0700365 // fCurrLayer may be nullptr now
reed@android.com199f1082009-06-10 02:12:47 +0000366
reed@android.com8a1c16f2008-12-17 15:59:43 +0000367 return true;
368 }
369 return false;
370 }
reed@google.com4b226022011-01-11 18:32:13 +0000371
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000372 SkBaseDevice* getDevice() const { return fDevice; }
reed1e7f5e72016-04-27 07:49:17 -0700373 const SkRasterClip& getClip() const { return *fRC; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000374 int getX() const { return fDevice->getOrigin().x(); }
375 int getY() const { return fDevice->getOrigin().y(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000376 const SkMatrix& getMatrix() const { return *fMatrix; }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000377 const SkPaint* getPaint() const { return fPaint; }
reed@google.com6f8f2922011-03-04 22:27:10 +0000378
reed@android.com8a1c16f2008-12-17 15:59:43 +0000379private:
reed@android.com8a1c16f2008-12-17 15:59:43 +0000380 const DeviceCM* fCurrLayer;
381 const SkPaint* fPaint; // May be null.
reed02f9ed72016-09-06 09:06:18 -0700382 SkClipStack* fMultiDeviceCS;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000383
384 typedef SkDraw INHERITED;
385};
386
387/////////////////////////////////////////////////////////////////////////////
388
reeddbc3cef2015-04-29 12:18:57 -0700389static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) {
390 return lazy->isValid() ? lazy->get() : lazy->set(orig);
391}
392
393/**
394 * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that
halcanary96fcdcc2015-08-27 07:41:13 -0700395 * colorfilter, else return nullptr.
reeddbc3cef2015-04-29 12:18:57 -0700396 */
reedd053ce92016-03-22 10:17:23 -0700397static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) {
reeddbc3cef2015-04-29 12:18:57 -0700398 SkImageFilter* imgf = paint.getImageFilter();
399 if (!imgf) {
halcanary96fcdcc2015-08-27 07:41:13 -0700400 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700401 }
402
reedd053ce92016-03-22 10:17:23 -0700403 SkColorFilter* imgCFPtr;
404 if (!imgf->asAColorFilter(&imgCFPtr)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700405 return nullptr;
reeddbc3cef2015-04-29 12:18:57 -0700406 }
reedd053ce92016-03-22 10:17:23 -0700407 sk_sp<SkColorFilter> imgCF(imgCFPtr);
reeddbc3cef2015-04-29 12:18:57 -0700408
409 SkColorFilter* paintCF = paint.getColorFilter();
halcanary96fcdcc2015-08-27 07:41:13 -0700410 if (nullptr == paintCF) {
reeddbc3cef2015-04-29 12:18:57 -0700411 // there is no existing paint colorfilter, so we can just return the imagefilter's
412 return imgCF;
413 }
414
415 // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF)
416 // and we need to combine them into a single colorfilter.
reedd053ce92016-03-22 10:17:23 -0700417 return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF));
reeddbc3cef2015-04-29 12:18:57 -0700418}
419
senorblanco87e066e2015-10-28 11:23:36 -0700420/**
421 * There are many bounds in skia. A circle's bounds is just its center extended by its radius.
422 * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw
423 * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional
424 * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters,
425 * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the
426 * conservative "effective" bounds based on the settings in the paint... with one exception. This
427 * function does *not* look at the imagefilter, which can also modify the effective bounds. It is
428 * deliberately ignored.
429 */
430static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint,
431 const SkRect& rawBounds,
432 SkRect* storage) {
433 SkPaint tmpUnfiltered(paint);
434 tmpUnfiltered.setImageFilter(nullptr);
435 if (tmpUnfiltered.canComputeFastBounds()) {
436 return tmpUnfiltered.computeFastBounds(rawBounds, storage);
437 } else {
438 return rawBounds;
439 }
440}
441
reed@android.com8a1c16f2008-12-17 15:59:43 +0000442class AutoDrawLooper {
443public:
senorblanco87e066e2015-10-28 11:23:36 -0700444 // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the
445 // paint. It's used to determine the size of the offscreen layer for filters.
446 // If null, the clip will be used instead.
reed3aafe112016-08-18 12:45:34 -0700447 AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, bool skipLayerForImageFilter = false,
senorblanco87e066e2015-10-28 11:23:36 -0700448 const SkRect* rawBounds = nullptr) : fOrigPaint(paint) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000449 fCanvas = canvas;
fmalita53d9f1c2016-01-25 06:23:54 -0800450#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000451 fFilter = canvas->getDrawFilter();
fmalita77650002016-01-21 18:47:11 -0800452#else
453 fFilter = nullptr;
454#endif
reed4a8126e2014-09-22 07:29:03 -0700455 fPaint = &fOrigPaint;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000456 fSaveCount = canvas->getSaveCount();
reed5c476fb2015-04-20 08:04:21 -0700457 fTempLayerForImageFilter = false;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000458 fDone = false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000459
reedd053ce92016-03-22 10:17:23 -0700460 auto simplifiedCF = image_to_color_filter(fOrigPaint);
reeddbc3cef2015-04-29 12:18:57 -0700461 if (simplifiedCF) {
462 SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint);
reedd053ce92016-03-22 10:17:23 -0700463 paint->setColorFilter(std::move(simplifiedCF));
halcanary96fcdcc2015-08-27 07:41:13 -0700464 paint->setImageFilter(nullptr);
reeddbc3cef2015-04-29 12:18:57 -0700465 fPaint = paint;
466 }
467
468 if (!skipLayerForImageFilter && fPaint->getImageFilter()) {
reed5c476fb2015-04-20 08:04:21 -0700469 /**
470 * We implement ImageFilters for a given draw by creating a layer, then applying the
471 * imagefilter to the pixels of that layer (its backing surface/image), and then
472 * we call restore() to xfer that layer to the main canvas.
473 *
474 * 1. SaveLayer (with a paint containing the current imagefilter and xfermode)
475 * 2. Generate the src pixels:
476 * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper)
477 * return (fPaint). We then draw the primitive (using srcover) into a cleared
478 * buffer/surface.
479 * 3. Restore the layer created in #1
480 * The imagefilter is passed the buffer/surface from the layer (now filled with the
481 * src pixels of the primitive). It returns a new "filtered" buffer, which we
482 * draw onto the previous layer using the xfermode from the original paint.
483 */
reed@google.com8926b162012-03-23 15:36:36 +0000484 SkPaint tmp;
reeddbc3cef2015-04-29 12:18:57 -0700485 tmp.setImageFilter(fPaint->getImageFilter());
reed374772b2016-10-05 17:33:02 -0700486 tmp.setBlendMode(fPaint->getBlendMode());
senorblanco87e066e2015-10-28 11:23:36 -0700487 SkRect storage;
488 if (rawBounds) {
489 // Make rawBounds include all paint outsets except for those due to image filters.
490 rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage);
491 }
reedbfd5f172016-01-07 11:28:08 -0800492 (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &tmp),
reed76033be2015-03-14 10:54:31 -0700493 SkCanvas::kFullLayer_SaveLayerStrategy);
reed5c476fb2015-04-20 08:04:21 -0700494 fTempLayerForImageFilter = true;
495 // we remove the imagefilter/xfermode inside doNext()
reed@google.com8926b162012-03-23 15:36:36 +0000496 }
497
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000498 if (SkDrawLooper* looper = paint.getLooper()) {
499 void* buffer = fLooperContextAllocator.reserveT<SkDrawLooper::Context>(
500 looper->contextSize());
501 fLooperContext = looper->createContext(canvas, buffer);
reed@google.com129ec222012-05-15 13:24:09 +0000502 fIsSimple = false;
503 } else {
halcanary96fcdcc2015-08-27 07:41:13 -0700504 fLooperContext = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000505 // can we be marked as simple?
reed5c476fb2015-04-20 08:04:21 -0700506 fIsSimple = !fFilter && !fTempLayerForImageFilter;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000507 }
508 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000509
reed@android.com8a1c16f2008-12-17 15:59:43 +0000510 ~AutoDrawLooper() {
reed5c476fb2015-04-20 08:04:21 -0700511 if (fTempLayerForImageFilter) {
reed@google.com8926b162012-03-23 15:36:36 +0000512 fCanvas->internalRestore();
513 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000514 SkASSERT(fCanvas->getSaveCount() == fSaveCount);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000515 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000516
reed@google.com4e2b3d32011-04-07 14:18:59 +0000517 const SkPaint& paint() const {
518 SkASSERT(fPaint);
519 return *fPaint;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000520 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000521
reed@google.com129ec222012-05-15 13:24:09 +0000522 bool next(SkDrawFilter::Type drawType) {
523 if (fDone) {
524 return false;
525 } else if (fIsSimple) {
526 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000527 return !fPaint->nothingToDraw();
528 } else {
529 return this->doNext(drawType);
530 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000531 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +0000532
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533private:
reeddbc3cef2015-04-29 12:18:57 -0700534 SkLazyPaint fLazyPaintInit; // base paint storage in case we need to modify it
535 SkLazyPaint fLazyPaintPerLooper; // per-draw-looper storage, so the looper can modify it
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000536 SkCanvas* fCanvas;
537 const SkPaint& fOrigPaint;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000538 SkDrawFilter* fFilter;
539 const SkPaint* fPaint;
540 int fSaveCount;
reed5c476fb2015-04-20 08:04:21 -0700541 bool fTempLayerForImageFilter;
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +0000542 bool fDone;
reed@google.com129ec222012-05-15 13:24:09 +0000543 bool fIsSimple;
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000544 SkDrawLooper::Context* fLooperContext;
545 SkSmallAllocator<1, 32> fLooperContextAllocator;
reed@google.com129ec222012-05-15 13:24:09 +0000546
547 bool doNext(SkDrawFilter::Type drawType);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548};
549
reed@google.com129ec222012-05-15 13:24:09 +0000550bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
halcanary96fcdcc2015-08-27 07:41:13 -0700551 fPaint = nullptr;
reed@google.com129ec222012-05-15 13:24:09 +0000552 SkASSERT(!fIsSimple);
reed5c476fb2015-04-20 08:04:21 -0700553 SkASSERT(fLooperContext || fFilter || fTempLayerForImageFilter);
reed@google.com129ec222012-05-15 13:24:09 +0000554
reeddbc3cef2015-04-29 12:18:57 -0700555 SkPaint* paint = fLazyPaintPerLooper.set(fLazyPaintInit.isValid() ?
556 *fLazyPaintInit.get() : fOrigPaint);
reed@google.com129ec222012-05-15 13:24:09 +0000557
reed5c476fb2015-04-20 08:04:21 -0700558 if (fTempLayerForImageFilter) {
halcanary96fcdcc2015-08-27 07:41:13 -0700559 paint->setImageFilter(nullptr);
reed374772b2016-10-05 17:33:02 -0700560 paint->setBlendMode(SkBlendMode::kSrcOver);
reed@google.com4e2b3d32011-04-07 14:18:59 +0000561 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000562
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000563 if (fLooperContext && !fLooperContext->next(fCanvas, paint)) {
reed@google.com4e2b3d32011-04-07 14:18:59 +0000564 fDone = true;
reed@google.com129ec222012-05-15 13:24:09 +0000565 return false;
566 }
567 if (fFilter) {
reed@google.com971aca72012-11-26 20:26:54 +0000568 if (!fFilter->filter(paint, drawType)) {
569 fDone = true;
570 return false;
571 }
halcanary96fcdcc2015-08-27 07:41:13 -0700572 if (nullptr == fLooperContext) {
reed@google.com129ec222012-05-15 13:24:09 +0000573 // no looper means we only draw once
574 fDone = true;
575 }
576 }
577 fPaint = paint;
578
579 // if we only came in here for the imagefilter, mark us as done
commit-bot@chromium.org79fbb402014-03-12 09:42:01 +0000580 if (!fLooperContext && !fFilter) {
reed@google.com129ec222012-05-15 13:24:09 +0000581 fDone = true;
reed@google.com632e1a22011-10-06 12:37:00 +0000582 }
583
584 // call this after any possible paint modifiers
585 if (fPaint->nothingToDraw()) {
halcanary96fcdcc2015-08-27 07:41:13 -0700586 fPaint = nullptr;
reed@google.com4e2b3d32011-04-07 14:18:59 +0000587 return false;
588 }
reed@google.com4e2b3d32011-04-07 14:18:59 +0000589 return true;
590}
591
reed@android.com8a1c16f2008-12-17 15:59:43 +0000592////////// macros to place around the internal draw calls //////////////////
593
reed3aafe112016-08-18 12:45:34 -0700594#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \
595 this->predrawNotify(); \
596 AutoDrawLooper looper(this, paint, skipLayerForFilter, bounds); \
597 while (looper.next(SkDrawFilter::kBitmap_Type)) { \
reed262a71b2015-12-05 13:07:27 -0800598 SkDrawIter iter(this);
599
600
reed@google.com8926b162012-03-23 15:36:36 +0000601#define LOOPER_BEGIN_DRAWDEVICE(paint, type) \
reed@google.com97af1a62012-08-28 12:19:02 +0000602 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700603 AutoDrawLooper looper(this, paint, true); \
reed@google.com8926b162012-03-23 15:36:36 +0000604 while (looper.next(type)) { \
reed@google.com8926b162012-03-23 15:36:36 +0000605 SkDrawIter iter(this);
606
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +0000607#define LOOPER_BEGIN(paint, type, bounds) \
reed@google.com97af1a62012-08-28 12:19:02 +0000608 this->predrawNotify(); \
reed3aafe112016-08-18 12:45:34 -0700609 AutoDrawLooper looper(this, paint, false, bounds); \
reed@google.com4e2b3d32011-04-07 14:18:59 +0000610 while (looper.next(type)) { \
reed@android.com8a1c16f2008-12-17 15:59:43 +0000611 SkDrawIter iter(this);
reed@google.com4b226022011-01-11 18:32:13 +0000612
reedc83a2972015-07-16 07:40:45 -0700613#define LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, type, bounds, auxOpaque) \
614 this->predrawNotify(bounds, &paint, auxOpaque); \
reed3aafe112016-08-18 12:45:34 -0700615 AutoDrawLooper looper(this, paint, false, bounds); \
reedc83a2972015-07-16 07:40:45 -0700616 while (looper.next(type)) { \
617 SkDrawIter iter(this);
618
reed@google.com4e2b3d32011-04-07 14:18:59 +0000619#define LOOPER_END }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000620
621////////////////////////////////////////////////////////////////////////////
622
msarettfbfa2582016-08-12 08:29:08 -0700623static inline SkRect qr_clip_bounds(const SkIRect& bounds) {
624 if (bounds.isEmpty()) {
625 return SkRect::MakeEmpty();
626 }
627
628 // Expand bounds out by 1 in case we are anti-aliasing. We store the
629 // bounds as floats to enable a faster quick reject implementation.
630 SkRect dst;
631 SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft);
632 return dst;
633}
634
mtkleinfeaadee2015-04-08 11:25:48 -0700635void SkCanvas::resetForNextPicture(const SkIRect& bounds) {
636 this->restoreToCount(1);
mtkleinfeaadee2015-04-08 11:25:48 -0700637 fClipStack->reset();
638 fMCRec->reset(bounds);
639
640 // We're peering through a lot of structs here. Only at this scope do we
641 // know that the device is an SkBitmapDevice (really an SkNoPixelsBitmapDevice).
642 static_cast<SkBitmapDevice*>(fMCRec->fLayer->fDevice)->setNewSize(bounds.size());
msarettfbfa2582016-08-12 08:29:08 -0700643 fDeviceClipBounds = qr_clip_bounds(bounds);
msarett9637ea92016-08-18 14:03:30 -0700644 fIsScaleTranslate = true;
mtkleinfeaadee2015-04-08 11:25:48 -0700645}
646
reedd9544982014-09-09 18:46:22 -0700647SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
reed42b73eb2015-11-20 13:42:42 -0800648 if (device && device->forceConservativeRasterClip()) {
649 flags = InitFlags(flags | kConservativeRasterClip_InitFlag);
650 }
651 // Since init() is only called once by our constructors, it is safe to perform this
652 // const-cast.
653 *const_cast<bool*>(&fConservativeRasterClip) = SkToBool(flags & kConservativeRasterClip_InitFlag);
654
caryclark@google.com45a75fb2013-04-25 13:34:40 +0000655 fAllowSimplifyClip = false;
reedf92c8662014-08-18 08:02:43 -0700656 fDeviceCMDirty = true;
reed2ff1fce2014-12-11 07:07:37 -0800657 fSaveCount = 1;
halcanary96fcdcc2015-08-27 07:41:13 -0700658 fMetaData = nullptr;
vjiaoblack95302da2016-07-21 10:25:54 -0700659#ifdef SK_EXPERIMENTAL_SHADOWING
660 fLights = nullptr;
661#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000662
halcanary385fe4d2015-08-26 13:07:48 -0700663 fClipStack.reset(new SkClipStack);
reed687fa1c2015-04-07 08:00:56 -0700664
reed@android.com8a1c16f2008-12-17 15:59:43 +0000665 fMCRec = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -0700666 new (fMCRec) MCRec(fConservativeRasterClip);
msarett9637ea92016-08-18 14:03:30 -0700667 fIsScaleTranslate = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000668
reeda499f902015-05-01 09:34:31 -0700669 SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
670 fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
reed7503d602016-07-15 14:23:29 -0700671 new (fDeviceCMStorage) DeviceCM(nullptr, nullptr, nullptr, fConservativeRasterClip,
reed8c30a812016-04-20 16:36:51 -0700672 fMCRec->fMatrix);
reedb679ca82015-04-07 04:40:48 -0700673
reed@android.com8a1c16f2008-12-17 15:59:43 +0000674 fMCRec->fTopLayer = fMCRec->fLayer;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000675
halcanary96fcdcc2015-08-27 07:41:13 -0700676 fSurfaceBase = nullptr;
reed@android.comf2b98d62010-12-20 18:26:13 +0000677
reedf92c8662014-08-18 08:02:43 -0700678 if (device) {
robertphillipsefbffed2015-06-22 12:06:08 -0700679 // The root device and the canvas should always have the same pixel geometry
680 SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry());
reedf92c8662014-08-18 08:02:43 -0700681 fMCRec->fLayer->fDevice = SkRef(device);
reed78e27682014-11-19 08:04:34 -0800682 fMCRec->fRasterClip.setRect(device->getGlobalBounds());
msarettfbfa2582016-08-12 08:29:08 -0700683 fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds());
reedf92c8662014-08-18 08:02:43 -0700684 }
msarettfbfa2582016-08-12 08:29:08 -0700685
reedf92c8662014-08-18 08:02:43 -0700686 return device;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687}
688
reed@google.comcde92112011-07-06 20:00:52 +0000689SkCanvas::SkCanvas()
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000690 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700691 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800692 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000693{
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000694 inc_canvas();
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000695
halcanary96fcdcc2015-08-27 07:41:13 -0700696 this->init(nullptr, kDefault_InitFlags);
vandebo@chromium.org8d84fac2010-10-13 22:13:05 +0000697}
698
reedd9544982014-09-09 18:46:22 -0700699static SkBitmap make_nopixels(int width, int height) {
700 SkBitmap bitmap;
701 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height));
702 return bitmap;
703}
704
705class SkNoPixelsBitmapDevice : public SkBitmapDevice {
706public:
robertphillipsfcf78292015-06-19 11:49:52 -0700707 SkNoPixelsBitmapDevice(const SkIRect& bounds, const SkSurfaceProps& surfaceProps)
708 : INHERITED(make_nopixels(bounds.width(), bounds.height()), surfaceProps)
reed78e27682014-11-19 08:04:34 -0800709 {
710 this->setOrigin(bounds.x(), bounds.y());
711 }
reedd9544982014-09-09 18:46:22 -0700712
713private:
piotaixrb5fae932014-09-24 13:03:30 -0700714
reedd9544982014-09-09 18:46:22 -0700715 typedef SkBitmapDevice INHERITED;
716};
717
reed96a857e2015-01-25 10:33:58 -0800718SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000719 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed96a857e2015-01-25 10:33:58 -0800720 , fProps(SkSurfacePropsCopyOrDefault(props))
reed42b73eb2015-11-20 13:42:42 -0800721 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000722{
723 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700724
halcanary385fe4d2015-08-26 13:07:48 -0700725 this->init(new SkNoPixelsBitmapDevice(SkIRect::MakeWH(width, height), fProps),
726 kDefault_InitFlags)->unref();
reedd9544982014-09-09 18:46:22 -0700727}
skia.committer@gmail.comba124482014-02-01 03:01:57 +0000728
reed78e27682014-11-19 08:04:34 -0800729SkCanvas::SkCanvas(const SkIRect& bounds, InitFlags flags)
reedd9544982014-09-09 18:46:22 -0700730 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700731 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800732 , fConservativeRasterClip(false)
reedd9544982014-09-09 18:46:22 -0700733{
734 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700735
halcanary385fe4d2015-08-26 13:07:48 -0700736 this->init(new SkNoPixelsBitmapDevice(bounds, fProps), flags)->unref();
reedd9544982014-09-09 18:46:22 -0700737}
738
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000739SkCanvas::SkCanvas(SkBaseDevice* device)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000740 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700741 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800742 , fConservativeRasterClip(false)
commit-bot@chromium.orge2543102014-01-31 19:42:58 +0000743{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000744 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700745
reedd9544982014-09-09 18:46:22 -0700746 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000747}
748
robertphillipsfcf78292015-06-19 11:49:52 -0700749SkCanvas::SkCanvas(SkBaseDevice* device, InitFlags flags)
750 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
robertphillips7b05ff12015-06-19 14:14:54 -0700751 , fProps(device->surfaceProps())
reed42b73eb2015-11-20 13:42:42 -0800752 , fConservativeRasterClip(false)
robertphillipsfcf78292015-06-19 11:49:52 -0700753{
754 inc_canvas();
755
756 this->init(device, flags);
757}
758
reed4a8126e2014-09-22 07:29:03 -0700759SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props)
reed3716fd02014-09-21 09:39:55 -0700760 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
reed4a8126e2014-09-22 07:29:03 -0700761 , fProps(props)
reed42b73eb2015-11-20 13:42:42 -0800762 , fConservativeRasterClip(false)
reed3716fd02014-09-21 09:39:55 -0700763{
764 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700765
halcanary385fe4d2015-08-26 13:07:48 -0700766 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700767 this->init(device, kDefault_InitFlags);
768}
reed29c857d2014-09-21 10:25:07 -0700769
reed4a8126e2014-09-22 07:29:03 -0700770SkCanvas::SkCanvas(const SkBitmap& bitmap)
771 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
772 , fProps(SkSurfaceProps::kLegacyFontHost_InitType)
reed42b73eb2015-11-20 13:42:42 -0800773 , fConservativeRasterClip(false)
reed4a8126e2014-09-22 07:29:03 -0700774{
775 inc_canvas();
piotaixrb5fae932014-09-24 13:03:30 -0700776
halcanary385fe4d2015-08-26 13:07:48 -0700777 SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
reed4a8126e2014-09-22 07:29:03 -0700778 this->init(device, kDefault_InitFlags);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000779}
780
781SkCanvas::~SkCanvas() {
782 // free up the contents of our deque
783 this->restoreToCount(1); // restore everything but the last
reed@google.com7c202932011-12-14 18:48:05 +0000784
reed@android.com8a1c16f2008-12-17 15:59:43 +0000785 this->internalRestore(); // restore the last, since we're going away
786
halcanary385fe4d2015-08-26 13:07:48 -0700787 delete fMetaData;
vandebo@chromium.orgb70ae312010-10-15 18:58:19 +0000788
reed@android.com8a1c16f2008-12-17 15:59:43 +0000789 dec_canvas();
790}
791
fmalita53d9f1c2016-01-25 06:23:54 -0800792#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000793SkDrawFilter* SkCanvas::getDrawFilter() const {
794 return fMCRec->fFilter;
795}
796
797SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
reed51985e32015-04-11 08:04:56 -0700798 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000799 SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
800 return filter;
801}
fmalita77650002016-01-21 18:47:11 -0800802#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000803
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000804SkMetaData& SkCanvas::getMetaData() {
805 // metadata users are rare, so we lazily allocate it. If that changes we
806 // can decide to just make it a field in the device (rather than a ptr)
halcanary96fcdcc2015-08-27 07:41:13 -0700807 if (nullptr == fMetaData) {
mike@reedtribe.org74bb77e2012-09-26 02:24:45 +0000808 fMetaData = new SkMetaData;
809 }
810 return *fMetaData;
811}
812
reed@android.com8a1c16f2008-12-17 15:59:43 +0000813///////////////////////////////////////////////////////////////////////////////
814
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000815void SkCanvas::flush() {
reedea5a6512016-07-07 16:44:27 -0700816 this->onFlush();
817}
818
819void SkCanvas::onFlush() {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000820 SkBaseDevice* device = this->getDevice();
junov@chromium.orgbf6c1e42012-01-30 14:53:22 +0000821 if (device) {
822 device->flush();
823 }
824}
825
bsalomon@google.com4ebe3822014-02-26 20:22:32 +0000826SkISize SkCanvas::getBaseLayerSize() const {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000827 SkBaseDevice* d = this->getDevice();
reed@google.com210ce002011-11-01 14:24:23 +0000828 return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0);
829}
830
senorblancoafc7cce2016-02-02 18:44:15 -0800831SkIRect SkCanvas::getTopLayerBounds() const {
832 SkBaseDevice* d = this->getTopDevice();
833 if (!d) {
834 return SkIRect::MakeEmpty();
835 }
836 return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height());
837}
838
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000839SkBaseDevice* SkCanvas::getDevice() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000840 // return root device
robertphillips@google.comc0290622012-07-16 21:20:03 +0000841 MCRec* rec = (MCRec*) fMCStack.front();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000842 SkASSERT(rec && rec->fLayer);
843 return rec->fLayer->fDevice;
844}
845
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000846SkBaseDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
reed@google.com0b53d592012-03-19 18:26:34 +0000847 if (updateMatrixClip) {
848 const_cast<SkCanvas*>(this)->updateDeviceCMCache();
849 }
reed@google.com9266fed2011-03-30 00:18:03 +0000850 return fMCRec->fTopLayer->fDevice;
851}
852
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000853bool SkCanvas::readPixels(SkBitmap* bitmap, int x, int y) {
reedc7ec7c92016-07-25 08:29:10 -0700854 if (kUnknown_SkColorType == bitmap->colorType()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000855 return false;
856 }
857
858 bool weAllocated = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700859 if (nullptr == bitmap->pixelRef()) {
reed84825042014-09-02 12:50:45 -0700860 if (!bitmap->tryAllocPixels()) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000861 return false;
862 }
863 weAllocated = true;
864 }
865
reedcf01e312015-05-23 19:14:51 -0700866 SkAutoPixmapUnlock unlocker;
867 if (bitmap->requestLock(&unlocker)) {
868 const SkPixmap& pm = unlocker.pixmap();
869 if (this->readPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), x, y)) {
870 return true;
871 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000872 }
873
874 if (weAllocated) {
halcanary96fcdcc2015-08-27 07:41:13 -0700875 bitmap->setPixelRef(nullptr);
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000876 }
877 return false;
878}
reed@google.com51df9e32010-12-23 19:29:18 +0000879
bsalomon@google.comc6980972011-11-02 19:57:21 +0000880bool SkCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000881 SkIRect r = srcRect;
882 const SkISize size = this->getBaseLayerSize();
883 if (!r.intersect(0, 0, size.width(), size.height())) {
884 bitmap->reset();
885 return false;
886 }
887
reed84825042014-09-02 12:50:45 -0700888 if (!bitmap->tryAllocN32Pixels(r.width(), r.height())) {
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000889 // bitmap will already be reset.
890 return false;
891 }
892 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), r.x(), r.y())) {
893 bitmap->reset();
894 return false;
895 }
896 return true;
897}
898
reed96472de2014-12-10 09:53:42 -0800899bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +0000900 SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +0000901 if (!device) {
902 return false;
903 }
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000904 const SkISize size = this->getBaseLayerSize();
mtkleinf0f14112014-12-12 08:46:25 -0800905
reed96472de2014-12-10 09:53:42 -0800906 SkReadPixelsRec rec(dstInfo, dstP, rowBytes, x, y);
907 if (!rec.trim(size.width(), size.height())) {
bsalomon@google.comdaba14b2011-11-02 20:10:48 +0000908 return false;
bsalomon@google.comc6980972011-11-02 19:57:21 +0000909 }
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000910
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000911 // The device can assert that the requested area is always contained in its bounds
reed96472de2014-12-10 09:53:42 -0800912 return device->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
reed@google.com51df9e32010-12-23 19:29:18 +0000913}
914
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000915bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) {
reedcf01e312015-05-23 19:14:51 -0700916 SkAutoPixmapUnlock unlocker;
917 if (bitmap.requestLock(&unlocker)) {
918 const SkPixmap& pm = unlocker.pixmap();
919 return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y);
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000920 }
921 return false;
922}
923
924bool SkCanvas::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes,
925 int x, int y) {
926 switch (origInfo.colorType()) {
927 case kUnknown_SkColorType:
928 case kIndex_8_SkColorType:
929 return false;
930 default:
931 break;
932 }
halcanary96fcdcc2015-08-27 07:41:13 -0700933 if (nullptr == pixels || rowBytes < origInfo.minRowBytes()) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000934 return false;
935 }
936
937 const SkISize size = this->getBaseLayerSize();
938 SkIRect target = SkIRect::MakeXYWH(x, y, origInfo.width(), origInfo.height());
939 if (!target.intersect(0, 0, size.width(), size.height())) {
940 return false;
941 }
942
943 SkBaseDevice* device = this->getDevice();
944 if (!device) {
945 return false;
946 }
947
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000948 // the intersect may have shrunk info's logical size
reede5ea5002014-09-03 11:54:58 -0700949 const SkImageInfo info = origInfo.makeWH(target.width(), target.height());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000950
951 // if x or y are negative, then we have to adjust pixels
952 if (x > 0) {
953 x = 0;
954 }
955 if (y > 0) {
956 y = 0;
957 }
958 // here x,y are either 0 or negative
959 pixels = ((const char*)pixels - y * rowBytes - x * info.bytesPerPixel());
960
reed4af35f32014-06-27 17:47:49 -0700961 // Tell our owning surface to bump its generation ID
reedc83a2972015-07-16 07:40:45 -0700962 const bool completeOverwrite = info.dimensions() == size;
963 this->predrawNotify(completeOverwrite);
reed4af35f32014-06-27 17:47:49 -0700964
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000965 // The device can assert that the requested area is always contained in its bounds
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000966 return device->writePixels(info, pixels, rowBytes, target.x(), target.y());
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000967}
reed@google.com51df9e32010-12-23 19:29:18 +0000968
junov@google.com4370aed2012-01-18 16:21:08 +0000969SkCanvas* SkCanvas::canvasForDrawIter() {
970 return this;
971}
972
reed@android.com8a1c16f2008-12-17 15:59:43 +0000973//////////////////////////////////////////////////////////////////////////////
974
reed@android.com8a1c16f2008-12-17 15:59:43 +0000975void SkCanvas::updateDeviceCMCache() {
976 if (fDeviceCMDirty) {
977 const SkMatrix& totalMatrix = this->getTotalMatrix();
reed1f836ee2014-07-07 07:49:34 -0700978 const SkRasterClip& totalClip = fMCRec->fRasterClip;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000979 DeviceCM* layer = fMCRec->fTopLayer;
reed@google.com4b226022011-01-11 18:32:13 +0000980
halcanary96fcdcc2015-08-27 07:41:13 -0700981 if (nullptr == layer->fNext) { // only one layer
reedde6c5312016-09-02 12:10:07 -0700982 layer->updateMC(totalMatrix, totalClip, nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000983 } else {
reed@google.com045e62d2011-10-24 12:19:46 +0000984 SkRasterClip clip(totalClip);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000985 do {
reedde6c5312016-09-02 12:10:07 -0700986 layer->updateMC(totalMatrix, clip, &clip);
halcanary96fcdcc2015-08-27 07:41:13 -0700987 } while ((layer = layer->fNext) != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000988 }
989 fDeviceCMDirty = false;
990 }
991}
992
reed@android.com8a1c16f2008-12-17 15:59:43 +0000993///////////////////////////////////////////////////////////////////////////////
994
reed2ff1fce2014-12-11 07:07:37 -0800995void SkCanvas::checkForDeferredSave() {
996 if (fMCRec->fDeferredSaveCount > 0) {
reed2ff1fce2014-12-11 07:07:37 -0800997 this->doSave();
998 }
999}
1000
reedf0090cb2014-11-26 08:55:51 -08001001int SkCanvas::getSaveCount() const {
reed2ff1fce2014-12-11 07:07:37 -08001002#ifdef SK_DEBUG
1003 int count = 0;
1004 SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart);
1005 for (;;) {
1006 const MCRec* rec = (const MCRec*)iter.next();
1007 if (!rec) {
1008 break;
1009 }
1010 count += 1 + rec->fDeferredSaveCount;
1011 }
1012 SkASSERT(count == fSaveCount);
1013#endif
1014 return fSaveCount;
reedf0090cb2014-11-26 08:55:51 -08001015}
1016
1017int SkCanvas::save() {
reed2ff1fce2014-12-11 07:07:37 -08001018 fSaveCount += 1;
1019 fMCRec->fDeferredSaveCount += 1;
1020 return this->getSaveCount() - 1; // return our prev value
1021}
1022
1023void SkCanvas::doSave() {
reedf0090cb2014-11-26 08:55:51 -08001024 this->willSave();
fmalitaa62d32d2015-04-28 08:08:57 -07001025
1026 SkASSERT(fMCRec->fDeferredSaveCount > 0);
1027 fMCRec->fDeferredSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001028 this->internalSave();
reedf0090cb2014-11-26 08:55:51 -08001029}
1030
1031void SkCanvas::restore() {
reed2ff1fce2014-12-11 07:07:37 -08001032 if (fMCRec->fDeferredSaveCount > 0) {
1033 SkASSERT(fSaveCount > 1);
1034 fSaveCount -= 1;
1035 fMCRec->fDeferredSaveCount -= 1;
1036 } else {
1037 // check for underflow
1038 if (fMCStack.count() > 1) {
1039 this->willRestore();
1040 SkASSERT(fSaveCount > 1);
reeda6441162015-03-26 13:40:09 -07001041 fSaveCount -= 1;
reed2ff1fce2014-12-11 07:07:37 -08001042 this->internalRestore();
1043 this->didRestore();
1044 }
reedf0090cb2014-11-26 08:55:51 -08001045 }
1046}
1047
1048void SkCanvas::restoreToCount(int count) {
1049 // sanity check
1050 if (count < 1) {
1051 count = 1;
1052 }
mtkleinf0f14112014-12-12 08:46:25 -08001053
reedf0090cb2014-11-26 08:55:51 -08001054 int n = this->getSaveCount() - count;
1055 for (int i = 0; i < n; ++i) {
1056 this->restore();
1057 }
1058}
1059
reed2ff1fce2014-12-11 07:07:37 -08001060void SkCanvas::internalSave() {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001061 MCRec* newTop = (MCRec*)fMCStack.push_back();
reedd9544982014-09-09 18:46:22 -07001062 new (newTop) MCRec(*fMCRec); // balanced in restore()
reed@android.com8a1c16f2008-12-17 15:59:43 +00001063 fMCRec = newTop;
reed@google.com4b226022011-01-11 18:32:13 +00001064
reed687fa1c2015-04-07 08:00:56 -07001065 fClipStack->save();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001066}
1067
reed4960eee2015-12-18 07:09:18 -08001068bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) {
reed4960eee2015-12-18 07:09:18 -08001069 return !(saveLayerFlags & SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001070}
1071
reed4960eee2015-12-18 07:09:18 -08001072bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags,
reed9b3aa542015-03-11 08:47:12 -07001073 SkIRect* intersection, const SkImageFilter* imageFilter) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001074 SkIRect clipBounds;
1075 if (!this->getClipDeviceBounds(&clipBounds)) {
junov@chromium.orga907ac32012-02-24 21:54:07 +00001076 return false;
reed@android.comf2b98d62010-12-20 18:26:13 +00001077 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001078
reed96e657d2015-03-10 17:30:07 -07001079 const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix()
1080
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001081 if (imageFilter) {
senorblancoe5e79842016-03-21 14:51:59 -07001082 clipBounds = imageFilter->filterBounds(clipBounds, ctm);
senorblancodb64af32015-12-09 10:11:43 -08001083 if (bounds && !imageFilter->canComputeFastBounds()) {
1084 bounds = nullptr;
1085 }
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +00001086 }
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001087 SkIRect ir;
bsalomon49f085d2014-09-05 13:34:00 -07001088 if (bounds) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001089 SkRect r;
reed@google.com4b226022011-01-11 18:32:13 +00001090
reed96e657d2015-03-10 17:30:07 -07001091 ctm.mapRect(&r, *bounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001092 r.roundOut(&ir);
1093 // early exit if the layer's bounds are clipped out
1094 if (!ir.intersect(clipBounds)) {
reed4960eee2015-12-18 07:09:18 -08001095 if (BoundsAffectsClip(saveLayerFlags)) {
reed1f836ee2014-07-07 07:49:34 -07001096 fMCRec->fRasterClip.setEmpty();
msarettfbfa2582016-08-12 08:29:08 -07001097 fDeviceClipBounds.setEmpty();
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001098 }
junov@chromium.orga907ac32012-02-24 21:54:07 +00001099 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001100 }
1101 } else { // no user bounds, so just use the clip
1102 ir = clipBounds;
1103 }
reed180aec42015-03-11 10:39:04 -07001104 SkASSERT(!ir.isEmpty());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001105
reed4960eee2015-12-18 07:09:18 -08001106 if (BoundsAffectsClip(saveLayerFlags)) {
reed180aec42015-03-11 10:39:04 -07001107 // Simplify the current clips since they will be applied properly during restore()
reed73603f32016-09-20 08:42:38 -07001108 fClipStack->clipDevRect(ir, kReplace_Op);
reed180aec42015-03-11 10:39:04 -07001109 fMCRec->fRasterClip.setRect(ir);
msarettfbfa2582016-08-12 08:29:08 -07001110 fDeviceClipBounds = qr_clip_bounds(ir);
junov@chromium.orga907ac32012-02-24 21:54:07 +00001111 }
1112
1113 if (intersection) {
1114 *intersection = ir;
1115 }
1116 return true;
1117}
1118
reed4960eee2015-12-18 07:09:18 -08001119
reed4960eee2015-12-18 07:09:18 -08001120int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) {
1121 return this->saveLayer(SaveLayerRec(bounds, paint, 0));
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001122}
1123
reed70ee31b2015-12-10 13:44:45 -08001124int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) {
reed4960eee2015-12-18 07:09:18 -08001125 return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag));
1126}
1127
1128int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
1129 SaveLayerRec rec(origRec);
1130 if (gIgnoreSaveLayerBounds) {
1131 rec.fBounds = nullptr;
1132 }
1133 SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
1134 fSaveCount += 1;
1135 this->internalSaveLayer(rec, strategy);
1136 return this->getSaveCount() - 1;
reed70ee31b2015-12-10 13:44:45 -08001137}
1138
reeda2217ef2016-07-20 06:04:34 -07001139void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter,
1140 SkBaseDevice* dst, const SkMatrix& ctm,
1141 const SkClipStack* clipStack) {
1142 SkDraw draw;
1143 SkRasterClip rc;
1144 rc.setRect(SkIRect::MakeWH(dst->width(), dst->height()));
1145 if (!dst->accessPixels(&draw.fDst)) {
1146 draw.fDst.reset(dst->imageInfo(), nullptr, 0);
robertphillips7354a4b2015-12-16 05:08:27 -08001147 }
reeda2217ef2016-07-20 06:04:34 -07001148 draw.fMatrix = &SkMatrix::I();
1149 draw.fRC = &rc;
1150 draw.fClipStack = clipStack;
1151 draw.fDevice = dst;
robertphillips7354a4b2015-12-16 05:08:27 -08001152
1153 SkPaint p;
robertphillips372177e2016-03-30 07:32:28 -07001154 p.setImageFilter(filter->makeWithLocalMatrix(ctm));
reeda2217ef2016-07-20 06:04:34 -07001155
1156 int x = src->getOrigin().x() - dst->getOrigin().x();
1157 int y = src->getOrigin().y() - dst->getOrigin().y();
1158 auto special = src->snapSpecial();
1159 if (special) {
1160 dst->drawSpecial(draw, special.get(), x, y, p);
1161 }
robertphillips7354a4b2015-12-16 05:08:27 -08001162}
reed70ee31b2015-12-10 13:44:45 -08001163
reed129ed1c2016-02-22 06:42:31 -08001164static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque,
1165 const SkPaint* paint) {
1166 // need to force L32 for now if we have an image filter. Once filters support other colortypes
1167 // e.g. sRGB or F16, we can remove this check
brianosman52ede1d2016-06-20 08:25:02 -07001168 // SRGBTODO: Can we remove this check now?
reed129ed1c2016-02-22 06:42:31 -08001169 const bool hasImageFilter = paint && paint->getImageFilter();
1170
1171 SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1172 if ((prev.bytesPerPixel() < 4) || hasImageFilter) {
1173 // force to L32
1174 return SkImageInfo::MakeN32(w, h, alphaType);
1175 } else {
1176 // keep the same characteristics as the prev
brianosman52ede1d2016-06-20 08:25:02 -07001177 return SkImageInfo::Make(w, h, prev.colorType(), alphaType, sk_ref_sp(prev.colorSpace()));
reed129ed1c2016-02-22 06:42:31 -08001178 }
1179}
1180
reed4960eee2015-12-18 07:09:18 -08001181void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) {
1182 const SkRect* bounds = rec.fBounds;
1183 const SkPaint* paint = rec.fPaint;
1184 SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags;
1185
reed8c30a812016-04-20 16:36:51 -07001186 SkLazyPaint lazyP;
1187 SkImageFilter* imageFilter = paint ? paint->getImageFilter() : NULL;
1188 SkMatrix stashedMatrix = fMCRec->fMatrix;
reed8c30a812016-04-20 16:36:51 -07001189 SkMatrix remainder;
1190 SkSize scale;
1191 /*
1192 * ImageFilters (so far) do not correctly handle matrices (CTM) that contain rotation/skew/etc.
1193 * but they do handle scaling. To accommodate this, we do the following:
1194 *
1195 * 1. Stash off the current CTM
1196 * 2. Decompose the CTM into SCALE and REMAINDER
1197 * 3. Wack the CTM to be just SCALE, and wrap the imagefilter with a MatrixImageFilter that
1198 * contains the REMAINDER
1199 * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM)
1200 * 5. During restore, we process the MatrixImageFilter, which applies REMAINDER to the output
1201 * of the original imagefilter, and draw that (via drawSprite)
1202 * 6. Unwack the CTM to its original state (i.e. stashedMatrix)
1203 *
1204 * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer
1205 * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter.
1206 */
reed96a04f32016-04-25 09:25:15 -07001207 if (imageFilter && !stashedMatrix.isScaleTranslate() && !imageFilter->canHandleComplexCTM() &&
reed8c30a812016-04-20 16:36:51 -07001208 stashedMatrix.decomposeScale(&scale, &remainder))
1209 {
1210 // We will restore the matrix (which we are overwriting here) in restore via fStashedMatrix
1211 this->internalSetMatrix(SkMatrix::MakeScale(scale.width(), scale.height()));
1212 SkPaint* p = lazyP.set(*paint);
1213 p->setImageFilter(SkImageFilter::MakeMatrixFilter(remainder,
1214 SkFilterQuality::kLow_SkFilterQuality,
1215 sk_ref_sp(imageFilter)));
1216 imageFilter = p->getImageFilter();
1217 paint = p;
1218 }
reed8c30a812016-04-20 16:36:51 -07001219
junov@chromium.orga907ac32012-02-24 21:54:07 +00001220 // do this before we create the layer. We don't call the public save() since
1221 // that would invoke a possibly overridden virtual
reed2ff1fce2014-12-11 07:07:37 -08001222 this->internalSave();
junov@chromium.orga907ac32012-02-24 21:54:07 +00001223
1224 fDeviceCMDirty = true;
1225
1226 SkIRect ir;
reed8c30a812016-04-20 16:36:51 -07001227 if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) {
reed2ff1fce2014-12-11 07:07:37 -08001228 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001229 }
1230
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001231 // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about
1232 // the clipRectBounds() call above?
1233 if (kNoLayer_SaveLayerStrategy == strategy) {
reed2ff1fce2014-12-11 07:07:37 -08001234 return;
commit-bot@chromium.orge54a23f2014-03-12 20:21:48 +00001235 }
1236
reed4960eee2015-12-18 07:09:18 -08001237 bool isOpaque = SkToBool(saveLayerFlags & kIsOpaque_SaveLayerFlag);
reed8dc0ccb2015-03-20 06:32:52 -07001238 SkPixelGeometry geo = fProps.pixelGeometry();
1239 if (paint) {
reed76033be2015-03-14 10:54:31 -07001240 // TODO: perhaps add a query to filters so we might preserve opaqueness...
reeddaa57bf2015-05-15 10:39:17 -07001241 if (paint->getImageFilter() || paint->getColorFilter()) {
reed76033be2015-03-14 10:54:31 -07001242 isOpaque = false;
reed8dc0ccb2015-03-20 06:32:52 -07001243 geo = kUnknown_SkPixelGeometry;
reed@google.comb55deeb2012-01-06 14:43:09 +00001244 }
1245 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001246
robertphillips5139e502016-07-19 05:10:40 -07001247 SkBaseDevice* priorDevice = this->getTopDevice();
reeda2217ef2016-07-20 06:04:34 -07001248 if (nullptr == priorDevice) {
reedb2db8982014-11-13 12:41:02 -08001249 SkDebugf("Unable to find device for layer.");
reed2ff1fce2014-12-11 07:07:37 -08001250 return;
reed@google.com76dd2772012-01-05 21:15:07 +00001251 }
reedb2db8982014-11-13 12:41:02 -08001252
robertphillips5139e502016-07-19 05:10:40 -07001253 SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), isOpaque,
reed129ed1c2016-02-22 06:42:31 -08001254 paint);
1255
robertphillips5139e502016-07-19 05:10:40 -07001256 SkAutoTUnref<SkBaseDevice> newDevice;
reed61f501f2015-04-29 08:34:00 -07001257 {
reed70ee31b2015-12-10 13:44:45 -08001258 const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() ||
reed4960eee2015-12-18 07:09:18 -08001259 (saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
reeddaa57bf2015-05-15 10:39:17 -07001260 const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
reed70ee31b2015-12-10 13:44:45 -08001261 const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
reedcd4051e2016-07-15 09:41:26 -07001262 preserveLCDText);
robertphillips5139e502016-07-19 05:10:40 -07001263 newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
1264 if (!newDevice) {
reed7503d602016-07-15 14:23:29 -07001265 return;
reed61f501f2015-04-29 08:34:00 -07001266 }
bungeman@google.come25c6842011-08-17 14:53:54 +00001267 }
robertphillips5139e502016-07-19 05:10:40 -07001268 newDevice->setOrigin(ir.fLeft, ir.fTop);
robertphillips7354a4b2015-12-16 05:08:27 -08001269
robertphillips5139e502016-07-19 05:10:40 -07001270 DeviceCM* layer = new DeviceCM(newDevice, paint, this, fConservativeRasterClip, stashedMatrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001271
1272 layer->fNext = fMCRec->fTopLayer;
1273 fMCRec->fLayer = layer;
1274 fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
reeda2217ef2016-07-20 06:04:34 -07001275
1276 if (rec.fBackdrop) {
1277 DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice,
1278 fMCRec->fMatrix, this->getClipStack());
1279 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001280}
1281
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001282int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
reedbada1882015-12-21 13:09:44 -08001283 if (0xFF == alpha) {
1284 return this->saveLayer(bounds, nullptr);
1285 } else {
1286 SkPaint tmpPaint;
1287 tmpPaint.setAlpha(alpha);
1288 return this->saveLayer(bounds, &tmpPaint);
1289 }
commit-bot@chromium.orgd70fa202014-04-24 21:51:58 +00001290}
1291
reed@android.com8a1c16f2008-12-17 15:59:43 +00001292void SkCanvas::internalRestore() {
1293 SkASSERT(fMCStack.count() != 0);
1294
1295 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001296
reed687fa1c2015-04-07 08:00:56 -07001297 fClipStack->restore();
commit-bot@chromium.org6c157642013-08-16 00:53:34 +00001298
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001299 // reserve our layer (if any)
reed@android.com8a1c16f2008-12-17 15:59:43 +00001300 DeviceCM* layer = fMCRec->fLayer; // may be null
1301 // now detach it from fMCRec so we can pop(). Gets freed after its drawn
halcanary96fcdcc2015-08-27 07:41:13 -07001302 fMCRec->fLayer = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001303
1304 // now do the normal restore()
1305 fMCRec->~MCRec(); // balanced in save()
1306 fMCStack.pop_back();
1307 fMCRec = (MCRec*)fMCStack.back();
1308
1309 /* Time to draw the layer's offscreen. We can't call the public drawSprite,
1310 since if we're being recorded, we don't want to record this (the
1311 recorder will have already recorded the restore).
1312 */
bsalomon49f085d2014-09-05 13:34:00 -07001313 if (layer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001314 if (layer->fNext) {
reed@google.com6f8f2922011-03-04 22:27:10 +00001315 const SkIPoint& origin = layer->fDevice->getOrigin();
reed7503d602016-07-15 14:23:29 -07001316 this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), layer->fPaint);
reed8c30a812016-04-20 16:36:51 -07001317 // restore what we smashed in internalSaveLayer
1318 fMCRec->fMatrix = layer->fStashedMatrix;
reed@google.com8926b162012-03-23 15:36:36 +00001319 // reset this, since internalDrawDevice will have set it to true
reed@android.com8a1c16f2008-12-17 15:59:43 +00001320 fDeviceCMDirty = true;
halcanary385fe4d2015-08-26 13:07:48 -07001321 delete layer;
reedb679ca82015-04-07 04:40:48 -07001322 } else {
1323 // we're at the root
reeda499f902015-05-01 09:34:31 -07001324 SkASSERT(layer == (void*)fDeviceCMStorage);
reedb679ca82015-04-07 04:40:48 -07001325 layer->~DeviceCM();
reed8c30a812016-04-20 16:36:51 -07001326 // no need to update fMCRec, 'cause we're killing the canvas
reed@android.com8a1c16f2008-12-17 15:59:43 +00001327 }
bungeman@google.com88edf1e2011-08-08 19:41:56 +00001328 }
msarettfbfa2582016-08-12 08:29:08 -07001329
1330 if (fMCRec) {
msarett9637ea92016-08-18 14:03:30 -07001331 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
msarettfbfa2582016-08-12 08:29:08 -07001332 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1333 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001334}
1335
reede8f30622016-03-23 18:59:25 -07001336sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
halcanary96fcdcc2015-08-27 07:41:13 -07001337 if (nullptr == props) {
reed4a8126e2014-09-22 07:29:03 -07001338 props = &fProps;
1339 }
1340 return this->onNewSurface(info, *props);
reed@google.com76f10a32014-02-05 15:32:21 +00001341}
1342
reede8f30622016-03-23 18:59:25 -07001343sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
reed@google.com76f10a32014-02-05 15:32:21 +00001344 SkBaseDevice* dev = this->getDevice();
reede8f30622016-03-23 18:59:25 -07001345 return dev ? dev->makeSurface(info, props) : nullptr;
reed@google.com76f10a32014-02-05 15:32:21 +00001346}
1347
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001348SkImageInfo SkCanvas::imageInfo() const {
reedea5a6512016-07-07 16:44:27 -07001349 return this->onImageInfo();
1350}
1351
1352SkImageInfo SkCanvas::onImageInfo() const {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001353 SkBaseDevice* dev = this->getDevice();
1354 if (dev) {
1355 return dev->imageInfo();
1356 } else {
reed@google.com900ecf22014-02-20 20:55:37 +00001357 return SkImageInfo::MakeUnknown(0, 0);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001358 }
1359}
1360
brianosman898235c2016-04-06 07:38:23 -07001361bool SkCanvas::getProps(SkSurfaceProps* props) const {
reedea5a6512016-07-07 16:44:27 -07001362 return this->onGetProps(props);
1363}
1364
1365bool SkCanvas::onGetProps(SkSurfaceProps* props) const {
brianosman898235c2016-04-06 07:38:23 -07001366 SkBaseDevice* dev = this->getDevice();
1367 if (dev) {
1368 if (props) {
1369 *props = fProps;
1370 }
1371 return true;
1372 } else {
1373 return false;
1374 }
1375}
1376
reed6ceeebd2016-03-09 14:26:26 -08001377#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001378const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) {
reed884e97c2015-05-26 11:31:54 -07001379 SkPixmap pmap;
reed6ceeebd2016-03-09 14:26:26 -08001380 if (this->peekPixels(&pmap)) {
1381 if (info) {
1382 *info = pmap.info();
1383 }
1384 if (rowBytes) {
1385 *rowBytes = pmap.rowBytes();
1386 }
1387 return pmap.addr();
reed884e97c2015-05-26 11:31:54 -07001388 }
reed6ceeebd2016-03-09 14:26:26 -08001389 return nullptr;
1390}
1391#endif
1392
1393bool SkCanvas::peekPixels(SkPixmap* pmap) {
1394 return this->onPeekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001395}
1396
reed884e97c2015-05-26 11:31:54 -07001397bool SkCanvas::onPeekPixels(SkPixmap* pmap) {
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001398 SkBaseDevice* dev = this->getDevice();
reed884e97c2015-05-26 11:31:54 -07001399 return dev && dev->peekPixels(pmap);
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +00001400}
1401
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001402void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) {
reed884e97c2015-05-26 11:31:54 -07001403 SkPixmap pmap;
1404 if (!this->onAccessTopLayerPixels(&pmap)) {
halcanary96fcdcc2015-08-27 07:41:13 -07001405 return nullptr;
reed884e97c2015-05-26 11:31:54 -07001406 }
1407 if (info) {
1408 *info = pmap.info();
1409 }
1410 if (rowBytes) {
1411 *rowBytes = pmap.rowBytes();
1412 }
1413 if (origin) {
commit-bot@chromium.org6b4aaa72014-04-21 21:09:38 +00001414 *origin = this->getTopDevice(false)->getOrigin();
1415 }
reed884e97c2015-05-26 11:31:54 -07001416 return pmap.writable_addr();
reed@google.com9c135db2014-03-12 18:28:35 +00001417}
1418
reed884e97c2015-05-26 11:31:54 -07001419bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
reed@google.com9c135db2014-03-12 18:28:35 +00001420 SkBaseDevice* dev = this->getTopDevice();
reed884e97c2015-05-26 11:31:54 -07001421 return dev && dev->accessPixels(pmap);
reed@google.com9c135db2014-03-12 18:28:35 +00001422}
1423
reed@android.com8a1c16f2008-12-17 15:59:43 +00001424/////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +00001425
reed7503d602016-07-15 14:23:29 -07001426void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001427 SkPaint tmp;
halcanary96fcdcc2015-08-27 07:41:13 -07001428 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001429 paint = &tmp;
1430 }
reed@google.com4b226022011-01-11 18:32:13 +00001431
reed@google.com8926b162012-03-23 15:36:36 +00001432 LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
reeda2217ef2016-07-20 06:04:34 -07001433
reed@android.com8a1c16f2008-12-17 15:59:43 +00001434 while (iter.next()) {
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001435 SkBaseDevice* dstDev = iter.fDevice;
reed@google.com76dd2772012-01-05 21:15:07 +00001436 paint = &looper.paint();
1437 SkImageFilter* filter = paint->getImageFilter();
1438 SkIPoint pos = { x - iter.getX(), y - iter.getY() };
senorblancof35566e2016-04-18 10:32:02 -07001439 if (filter) {
reeda2217ef2016-07-20 06:04:34 -07001440 dstDev->drawSpecial(iter, srcDev->snapSpecial().get(), pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001441 } else {
reed@google.comb55deeb2012-01-06 14:43:09 +00001442 dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
reed@google.com76dd2772012-01-05 21:15:07 +00001443 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001444 }
reeda2217ef2016-07-20 06:04:34 -07001445
reed@google.com4e2b3d32011-04-07 14:18:59 +00001446 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00001447}
1448
reed32704672015-12-16 08:27:10 -08001449/////////////////////////////////////////////////////////////////////////////
reedda420b92015-12-16 08:38:15 -08001450
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001451void SkCanvas::translate(SkScalar dx, SkScalar dy) {
reedfe69b502016-09-12 06:31:48 -07001452 if (dx || dy) {
1453 this->checkForDeferredSave();
1454 fDeviceCMDirty = true;
1455 fMCRec->fMatrix.preTranslate(dx,dy);
mtkleincbdf0072016-08-19 09:05:27 -07001456
reedfe69b502016-09-12 06:31:48 -07001457 // Translate shouldn't affect the is-scale-translateness of the matrix.
1458 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
mtkleincbdf0072016-08-19 09:05:27 -07001459
reedfe69b502016-09-12 06:31:48 -07001460 this->didTranslate(dx,dy);
1461 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001462}
1463
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001464void SkCanvas::scale(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001465 SkMatrix m;
1466 m.setScale(sx, sy);
1467 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001468}
1469
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001470void SkCanvas::rotate(SkScalar degrees) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001471 SkMatrix m;
1472 m.setRotate(degrees);
1473 this->concat(m);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001474}
1475
bungeman7438bfc2016-07-12 15:01:19 -07001476void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) {
1477 SkMatrix m;
1478 m.setRotate(degrees, px, py);
1479 this->concat(m);
1480}
1481
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001482void SkCanvas::skew(SkScalar sx, SkScalar sy) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001483 SkMatrix m;
1484 m.setSkew(sx, sy);
1485 this->concat(m);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001486}
1487
commit-bot@chromium.org92362382014-03-18 12:51:48 +00001488void SkCanvas::concat(const SkMatrix& matrix) {
commit-bot@chromium.orgd9ea09e2014-03-25 17:32:26 +00001489 if (matrix.isIdentity()) {
1490 return;
1491 }
1492
reed2ff1fce2014-12-11 07:07:37 -08001493 this->checkForDeferredSave();
reed@android.com8a1c16f2008-12-17 15:59:43 +00001494 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001495 fMCRec->fMatrix.preConcat(matrix);
msarett9637ea92016-08-18 14:03:30 -07001496 fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate();
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001497 this->didConcat(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001498}
1499
reed8c30a812016-04-20 16:36:51 -07001500void SkCanvas::internalSetMatrix(const SkMatrix& matrix) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001501 fDeviceCMDirty = true;
reed1f836ee2014-07-07 07:49:34 -07001502 fMCRec->fMatrix = matrix;
msarett9da5a5a2016-08-19 08:38:36 -07001503 fIsScaleTranslate = matrix.isScaleTranslate();
reed8c30a812016-04-20 16:36:51 -07001504}
1505
1506void SkCanvas::setMatrix(const SkMatrix& matrix) {
1507 this->checkForDeferredSave();
1508 this->internalSetMatrix(matrix);
commit-bot@chromium.org44c48d02014-03-13 20:03:58 +00001509 this->didSetMatrix(matrix);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001510}
1511
reed@android.com8a1c16f2008-12-17 15:59:43 +00001512void SkCanvas::resetMatrix() {
reed8c30a812016-04-20 16:36:51 -07001513 this->setMatrix(SkMatrix::I());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001514}
1515
vjiaoblack95302da2016-07-21 10:25:54 -07001516#ifdef SK_EXPERIMENTAL_SHADOWING
vjiaoblacke5de1302016-07-13 14:05:28 -07001517void SkCanvas::translateZ(SkScalar z) {
1518 this->checkForDeferredSave();
1519 this->fMCRec->fCurDrawDepth += z;
1520 this->didTranslateZ(z);
1521}
1522
1523SkScalar SkCanvas::getZ() const {
1524 return this->fMCRec->fCurDrawDepth;
1525}
1526
vjiaoblack95302da2016-07-21 10:25:54 -07001527void SkCanvas::setLights(sk_sp<SkLights> lights) {
1528 this->fLights = lights;
1529}
1530
1531sk_sp<SkLights> SkCanvas::getLights() const {
1532 return this->fLights;
1533}
1534#endif
1535
reed@android.com8a1c16f2008-12-17 15:59:43 +00001536//////////////////////////////////////////////////////////////////////////////
1537
reed73603f32016-09-20 08:42:38 -07001538void SkCanvas::clipRect(const SkRect& rect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001539 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001540 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
1541 this->onClipRect(rect, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001542}
1543
reed73603f32016-09-20 08:42:38 -07001544void SkCanvas::onClipRect(const SkRect& rect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001545 const bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reedc64eff52015-11-21 12:39:45 -08001546 AutoValidateClip avc(this);
Brian Salomona3b45d42016-10-03 11:36:16 -04001547 fClipStack->clipRect(rect, fMCRec->fMatrix, op, isAA);
1548 fMCRec->fRasterClip.op(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1549 isAA);
reedc64eff52015-11-21 12:39:45 -08001550 fDeviceCMDirty = true;
msarettfbfa2582016-08-12 08:29:08 -07001551 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001552}
1553
reed73603f32016-09-20 08:42:38 -07001554void SkCanvas::clipRRect(const SkRRect& rrect, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001555 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001556 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001557 if (rrect.isRect()) {
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001558 this->onClipRect(rrect.getBounds(), op, edgeStyle);
1559 } else {
1560 this->onClipRRect(rrect, op, edgeStyle);
reed@google.com4ed0fb72012-12-12 20:48:18 +00001561 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001562}
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001563
reed73603f32016-09-20 08:42:38 -07001564void SkCanvas::onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001565 AutoValidateClip avc(this);
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001566
Brian Salomona3b45d42016-10-03 11:36:16 -04001567 fDeviceCMDirty = true;
commit-bot@chromium.org14e50ae2014-02-16 23:35:31 +00001568
Brian Salomona3b45d42016-10-03 11:36:16 -04001569 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
1570 fClipStack->clipRRect(rrect, fMCRec->fMatrix, op, isAA);
1571 fMCRec->fRasterClip.op(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1572 isAA);
1573 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
1574 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00001575}
1576
reed73603f32016-09-20 08:42:38 -07001577void SkCanvas::clipPath(const SkPath& path, ClipOp op, bool doAA) {
reed2ff1fce2014-12-11 07:07:37 -08001578 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001579 ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle;
robertphillips39f05382015-11-24 09:30:12 -08001580
1581 if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) {
1582 SkRect r;
1583 if (path.isRect(&r)) {
1584 this->onClipRect(r, op, edgeStyle);
1585 return;
1586 }
1587 SkRRect rrect;
1588 if (path.isOval(&r)) {
1589 rrect.setOval(r);
1590 this->onClipRRect(rrect, op, edgeStyle);
1591 return;
1592 }
1593 if (path.isRRect(&rrect)) {
1594 this->onClipRRect(rrect, op, edgeStyle);
1595 return;
1596 }
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001597 }
robertphillips39f05382015-11-24 09:30:12 -08001598
1599 this->onClipPath(path, op, edgeStyle);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001600}
1601
reed73603f32016-09-20 08:42:38 -07001602void SkCanvas::onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001603 AutoValidateClip avc(this);
1604
reed@android.com8a1c16f2008-12-17 15:59:43 +00001605 fDeviceCMDirty = true;
Brian Salomona3b45d42016-10-03 11:36:16 -04001606 bool isAA = kSoft_ClipEdgeStyle == edgeStyle;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001607
Brian Salomona3b45d42016-10-03 11:36:16 -04001608 fClipStack->clipPath(path, fMCRec->fMatrix, op, isAA);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001609
Brian Salomona3b45d42016-10-03 11:36:16 -04001610 const SkPath* rasterClipPath = &path;
1611 const SkMatrix* matrix = &fMCRec->fMatrix;
1612 SkPath tempPath;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001613 if (fAllowSimplifyClip) {
Brian Salomona3b45d42016-10-03 11:36:16 -04001614 isAA = getClipStack()->asPath(&tempPath);
1615 rasterClipPath = &tempPath;
1616 matrix = &SkMatrix::I();
reed73603f32016-09-20 08:42:38 -07001617 op = kReplace_Op;
caryclark@google.com45a75fb2013-04-25 13:34:40 +00001618 }
Brian Salomona3b45d42016-10-03 11:36:16 -04001619 fMCRec->fRasterClip.op(*rasterClipPath, *matrix, this->getTopLayerBounds(), (SkRegion::Op)op,
1620 isAA);
msarettfbfa2582016-08-12 08:29:08 -07001621 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001622}
1623
reed73603f32016-09-20 08:42:38 -07001624void SkCanvas::clipRegion(const SkRegion& rgn, ClipOp op) {
reed2ff1fce2014-12-11 07:07:37 -08001625 this->checkForDeferredSave();
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001626 this->onClipRegion(rgn, op);
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001627}
1628
reed73603f32016-09-20 08:42:38 -07001629void SkCanvas::onClipRegion(const SkRegion& rgn, ClipOp op) {
reed@google.com5c3d1472011-02-22 19:12:23 +00001630 AutoValidateClip avc(this);
1631
reed@android.com8a1c16f2008-12-17 15:59:43 +00001632 fDeviceCMDirty = true;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001633
reed@google.com5c3d1472011-02-22 19:12:23 +00001634 // todo: signal fClipStack that we have a region, and therefore (I guess)
1635 // we have to ignore it, and use the region directly?
reed687fa1c2015-04-07 08:00:56 -07001636 fClipStack->clipDevRect(rgn.getBounds(), op);
reed@google.com5c3d1472011-02-22 19:12:23 +00001637
reed73603f32016-09-20 08:42:38 -07001638 fMCRec->fRasterClip.op(rgn, (SkRegion::Op)op);
msarettfbfa2582016-08-12 08:29:08 -07001639 fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001640}
1641
reed@google.com819c9212011-02-23 18:56:55 +00001642#ifdef SK_DEBUG
1643void SkCanvas::validateClip() const {
1644 // construct clipRgn from the clipstack
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00001645 const SkBaseDevice* device = this->getDevice();
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001646 if (!device) {
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001647 SkASSERT(this->isClipEmpty());
djsollen@google.comccfee2a2012-05-01 16:50:10 +00001648 return;
1649 }
1650
reed@google.com819c9212011-02-23 18:56:55 +00001651 SkIRect ir;
1652 ir.set(0, 0, device->width(), device->height());
reedd9544982014-09-09 18:46:22 -07001653 SkRasterClip tmpClip(ir, fConservativeRasterClip);
reed@google.com819c9212011-02-23 18:56:55 +00001654
reed687fa1c2015-04-07 08:00:56 -07001655 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001656 const SkClipStack::Element* element;
halcanary96fcdcc2015-08-27 07:41:13 -07001657 while ((element = iter.next()) != nullptr) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001658 switch (element->getType()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001659 case SkClipStack::Element::kRect_Type:
1660 element->getRect().round(&ir);
reed73603f32016-09-20 08:42:38 -07001661 tmpClip.op(ir, (SkRegion::Op)element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001662 break;
1663 case SkClipStack::Element::kEmpty_Type:
1664 tmpClip.setEmpty();
1665 break;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001666 default: {
1667 SkPath path;
1668 element->asPath(&path);
Brian Salomona3b45d42016-10-03 11:36:16 -04001669 tmpClip.op(path, SkMatrix::I(), this->getTopLayerBounds(),
1670 (SkRegion::Op)element->getOp(), element->isAA());
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +00001671 break;
1672 }
reed@google.com819c9212011-02-23 18:56:55 +00001673 }
1674 }
reed@google.com819c9212011-02-23 18:56:55 +00001675}
1676#endif
1677
reed@google.com90c07ea2012-04-13 13:50:27 +00001678void SkCanvas::replayClips(ClipVisitor* visitor) const {
reed687fa1c2015-04-07 08:00:56 -07001679 SkClipStack::B2TIter iter(*fClipStack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001680 const SkClipStack::Element* element;
reed@google.com90c07ea2012-04-13 13:50:27 +00001681
halcanary96fcdcc2015-08-27 07:41:13 -07001682 while ((element = iter.next()) != nullptr) {
fmalitac3b589a2014-06-05 12:40:07 -07001683 element->replay(visitor);
reed@google.com90c07ea2012-04-13 13:50:27 +00001684 }
1685}
1686
reed@google.com5c3d1472011-02-22 19:12:23 +00001687///////////////////////////////////////////////////////////////////////////////
1688
reed@google.com754de5f2014-02-24 19:38:20 +00001689bool SkCanvas::isClipEmpty() const {
reed1f836ee2014-07-07 07:49:34 -07001690 return fMCRec->fRasterClip.isEmpty();
reed@google.com754de5f2014-02-24 19:38:20 +00001691}
1692
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001693bool SkCanvas::isClipRect() const {
reed1f836ee2014-07-07 07:49:34 -07001694 return fMCRec->fRasterClip.isRect();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001695}
1696
msarettfbfa2582016-08-12 08:29:08 -07001697static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) {
1698#if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
1699 __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec);
1700 __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec);
1701 __m128 mask = _mm_cmplt_ps(lLtT, RrBb);
1702 return 0xF != _mm_movemask_ps(mask);
1703#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON)
1704 float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0];
1705 float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1];
1706 uint32x4_t mask = vcltq_f32(lLtT, RrBb);
1707 return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask);
1708#else
1709 SkRect devRectAsRect;
1710 SkRect devClipAsRect;
1711 devRect.store(&devRectAsRect.fLeft);
1712 devClip.store(&devClipAsRect.fLeft);
1713 return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect);
1714#endif
1715}
wjmaclean@chromium.org116b2bc2011-02-07 17:48:37 +00001716
msarettfbfa2582016-08-12 08:29:08 -07001717// It's important for this function to not be inlined. Otherwise the compiler will share code
1718// between the fast path and the slow path, resulting in two slow paths.
1719static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip,
1720 const SkMatrix& matrix) {
1721 SkRect deviceRect;
1722 matrix.mapRect(&deviceRect, src);
1723 return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip);
1724}
1725
1726bool SkCanvas::quickReject(const SkRect& src) const {
1727#ifdef SK_DEBUG
1728 // Verify that fDeviceClipBounds are set properly.
1729 SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds());
reed1f836ee2014-07-07 07:49:34 -07001730 if (fMCRec->fRasterClip.isEmpty()) {
msarett0c685ee2016-08-14 13:51:16 -07001731 SkASSERT(fDeviceClipBounds.isEmpty());
reed@android.coma380ae42009-07-21 01:17:02 +00001732 } else {
msarettfbfa2582016-08-12 08:29:08 -07001733 SkASSERT(tmp == fDeviceClipBounds);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001734 }
msarettfbfa2582016-08-12 08:29:08 -07001735
msarett9637ea92016-08-18 14:03:30 -07001736 // Verify that fIsScaleTranslate is set properly.
1737 SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate());
msarettfbfa2582016-08-12 08:29:08 -07001738#endif
1739
msarett9637ea92016-08-18 14:03:30 -07001740 if (!fIsScaleTranslate) {
msarettfbfa2582016-08-12 08:29:08 -07001741 return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix);
1742 }
1743
1744 // We inline the implementation of mapScaleTranslate() for the fast path.
1745 float sx = fMCRec->fMatrix.getScaleX();
1746 float sy = fMCRec->fMatrix.getScaleY();
1747 float tx = fMCRec->fMatrix.getTranslateX();
1748 float ty = fMCRec->fMatrix.getTranslateY();
1749 Sk4f scale(sx, sy, sx, sy);
1750 Sk4f trans(tx, ty, tx, ty);
1751
1752 // Apply matrix.
1753 Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans;
1754
1755 // Make sure left < right, top < bottom.
1756 Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]);
1757 Sk4f min = Sk4f::Min(ltrb, rblt);
1758 Sk4f max = Sk4f::Max(ltrb, rblt);
1759 // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on
1760 // ARM this sequence generates the fastest (a single instruction).
1761 Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]);
1762
1763 // Check if the device rect is NaN or outside the clip.
1764 return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft));
reed@android.com8a1c16f2008-12-17 15:59:43 +00001765}
1766
reed@google.com3b3e8952012-08-16 20:53:31 +00001767bool SkCanvas::quickReject(const SkPath& path) const {
1768 return path.isEmpty() || this->quickReject(path.getBounds());
reed@android.com8a1c16f2008-12-17 15:59:43 +00001769}
1770
reed@google.com3b3e8952012-08-16 20:53:31 +00001771bool SkCanvas::getClipBounds(SkRect* bounds) const {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001772 SkIRect ibounds;
robertphillips@google.com8f90a892014-02-28 18:19:39 +00001773 if (!this->getClipDeviceBounds(&ibounds)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00001774 return false;
1775 }
1776
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001777 SkMatrix inverse;
1778 // if we can't invert the CTM, we can't return local clip bounds
reed1f836ee2014-07-07 07:49:34 -07001779 if (!fMCRec->fMatrix.invert(&inverse)) {
reed@android.com72dcd3a2009-05-18 15:46:29 +00001780 if (bounds) {
1781 bounds->setEmpty();
1782 }
reed@android.comd9c0f0b2009-02-06 22:39:37 +00001783 return false;
1784 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00001785
bsalomon49f085d2014-09-05 13:34:00 -07001786 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001787 SkRect r;
reed@google.com3b3e8952012-08-16 20:53:31 +00001788 // adjust it outwards in case we are antialiasing
1789 const int inset = 1;
reed@google.comfa4d5bd2012-01-30 17:36:27 +00001790
reed@google.com8f4d2302013-12-17 16:44:46 +00001791 r.iset(ibounds.fLeft - inset, ibounds.fTop - inset,
1792 ibounds.fRight + inset, ibounds.fBottom + inset);
reed@android.com8a1c16f2008-12-17 15:59:43 +00001793 inverse.mapRect(bounds, r);
1794 }
1795 return true;
1796}
1797
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001798bool SkCanvas::getClipDeviceBounds(SkIRect* bounds) const {
reed1f836ee2014-07-07 07:49:34 -07001799 const SkRasterClip& clip = fMCRec->fRasterClip;
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001800 if (clip.isEmpty()) {
1801 if (bounds) {
1802 bounds->setEmpty();
1803 }
1804 return false;
1805 }
1806
bsalomon49f085d2014-09-05 13:34:00 -07001807 if (bounds) {
tomhudson@google.combcb671c2011-09-13 15:07:58 +00001808 *bounds = clip.getBounds();
1809 }
1810 return true;
1811}
1812
reed@android.com8a1c16f2008-12-17 15:59:43 +00001813const SkMatrix& SkCanvas::getTotalMatrix() const {
reed1f836ee2014-07-07 07:49:34 -07001814 return fMCRec->fMatrix;
reed@android.com8a1c16f2008-12-17 15:59:43 +00001815}
1816
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001817const SkRegion& SkCanvas::internal_private_getTotalClip() const {
reed1f836ee2014-07-07 07:49:34 -07001818 return fMCRec->fRasterClip.forceGetBW();
commit-bot@chromium.org5c70cdc2014-03-08 03:57:19 +00001819}
1820
robertphillips175dd9b2016-04-28 14:32:04 -07001821GrDrawContext* SkCanvas::internal_private_accessTopLayerDrawContext() {
reed@google.com9c135db2014-03-12 18:28:35 +00001822 SkBaseDevice* dev = this->getTopDevice();
robertphillips175dd9b2016-04-28 14:32:04 -07001823 return dev ? dev->accessDrawContext() : nullptr;
reed@google.com9c135db2014-03-12 18:28:35 +00001824}
1825
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001826GrContext* SkCanvas::getGrContext() {
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001827 SkBaseDevice* device = this->getTopDevice();
reed86ae3d12016-04-26 06:57:31 -07001828 return device ? device->context() : nullptr;
commit-bot@chromium.org644629c2013-11-21 06:21:58 +00001829}
bsalomon@google.come97f0852011-06-17 13:10:25 +00001830
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001831void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
1832 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08001833 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawDRRect()");
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00001834 if (outer.isEmpty()) {
1835 return;
1836 }
1837 if (inner.isEmpty()) {
1838 this->drawRRect(outer, paint);
1839 return;
1840 }
1841
1842 // We don't have this method (yet), but technically this is what we should
1843 // be able to assert...
1844 // SkASSERT(outer.contains(inner));
1845 //
1846 // For now at least check for containment of bounds
1847 SkASSERT(outer.getBounds().contains(inner.getBounds()));
1848
1849 this->onDrawDRRect(outer, inner, paint);
1850}
1851
reed41af9662015-01-05 07:49:08 -08001852// These need to stop being virtual -- clients need to override the onDraw... versions
1853
1854void SkCanvas::drawPaint(const SkPaint& paint) {
1855 this->onDrawPaint(paint);
1856}
1857
1858void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
1859 this->onDrawRect(r, paint);
1860}
1861
msarettdca352e2016-08-26 06:37:45 -07001862void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
1863 if (region.isEmpty()) {
1864 return;
1865 }
1866
1867 if (region.isRect()) {
1868 return this->drawIRect(region.getBounds(), paint);
1869 }
1870
1871 this->onDrawRegion(region, paint);
1872}
1873
reed41af9662015-01-05 07:49:08 -08001874void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) {
1875 this->onDrawOval(r, paint);
1876}
1877
1878void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
1879 this->onDrawRRect(rrect, paint);
1880}
1881
1882void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) {
1883 this->onDrawPoints(mode, count, pts, paint);
1884}
1885
1886void SkCanvas::drawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[],
1887 const SkPoint texs[], const SkColor colors[], SkXfermode* xmode,
1888 const uint16_t indices[], int indexCount, const SkPaint& paint) {
1889 this->onDrawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
1890 indices, indexCount, paint);
1891}
1892
1893void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
1894 this->onDrawPath(path, paint);
1895}
1896
reeda85d4d02015-05-06 12:56:48 -07001897void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001898 RETURN_ON_NULL(image);
reeda85d4d02015-05-06 12:56:48 -07001899 this->onDrawImage(image, x, y, paint);
reed41af9662015-01-05 07:49:08 -08001900}
1901
reede47829b2015-08-06 10:02:53 -07001902void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst,
1903 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001904 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001905 if (dst.isEmpty() || src.isEmpty()) {
1906 return;
1907 }
1908 this->onDrawImageRect(image, &src, dst, paint, constraint);
1909}
reed41af9662015-01-05 07:49:08 -08001910
reed84984ef2015-07-17 07:09:43 -07001911void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst,
1912 const SkPaint* paint, SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001913 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001914 this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001915}
1916
reede47829b2015-08-06 10:02:53 -07001917void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint,
1918 SrcRectConstraint constraint) {
reede3b38ce2016-01-08 09:18:44 -08001919 RETURN_ON_NULL(image);
reede47829b2015-08-06 10:02:53 -07001920 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint,
1921 constraint);
1922}
reede47829b2015-08-06 10:02:53 -07001923
reed4c21dc52015-06-25 12:32:03 -07001924void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
1925 const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08001926 RETURN_ON_NULL(image);
reed4c21dc52015-06-25 12:32:03 -07001927 if (dst.isEmpty()) {
1928 return;
1929 }
msarett552bca92016-08-03 06:53:26 -07001930 if (SkLatticeIter::Valid(image->width(), image->height(), center)) {
1931 this->onDrawImageNine(image, center, dst, paint);
1932 } else {
reede47829b2015-08-06 10:02:53 -07001933 this->drawImageRect(image, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001934 }
reed4c21dc52015-06-25 12:32:03 -07001935}
1936
msarett16882062016-08-16 09:31:08 -07001937void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
1938 const SkPaint* paint) {
1939 RETURN_ON_NULL(image);
1940 if (dst.isEmpty()) {
1941 return;
1942 }
msarett71df2d72016-09-30 12:41:42 -07001943
1944 SkIRect bounds;
1945 Lattice latticePlusBounds = lattice;
1946 if (!latticePlusBounds.fBounds) {
1947 bounds = SkIRect::MakeWH(image->width(), image->height());
1948 latticePlusBounds.fBounds = &bounds;
1949 }
1950
1951 if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
1952 this->onDrawImageLattice(image, latticePlusBounds, dst, paint);
msarett16882062016-08-16 09:31:08 -07001953 } else {
1954 this->drawImageRect(image, dst, paint);
1955 }
1956}
1957
reed41af9662015-01-05 07:49:08 -08001958void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001959 if (bitmap.drawsNothing()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001960 return;
1961 }
reed41af9662015-01-05 07:49:08 -08001962 this->onDrawBitmap(bitmap, dx, dy, paint);
1963}
1964
reede47829b2015-08-06 10:02:53 -07001965void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst,
reeda5517e22015-07-14 10:54:12 -07001966 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001967 if (bitmap.drawsNothing() || dst.isEmpty() || src.isEmpty()) {
reeda5517e22015-07-14 10:54:12 -07001968 return;
1969 }
reede47829b2015-08-06 10:02:53 -07001970 this->onDrawBitmapRect(bitmap, &src, dst, paint, constraint);
reed41af9662015-01-05 07:49:08 -08001971}
1972
reed84984ef2015-07-17 07:09:43 -07001973void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst,
1974 const SkPaint* paint, SrcRectConstraint constraint) {
reede47829b2015-08-06 10:02:53 -07001975 this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint);
reed84984ef2015-07-17 07:09:43 -07001976}
1977
reede47829b2015-08-06 10:02:53 -07001978void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint,
1979 SrcRectConstraint constraint) {
1980 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint,
1981 constraint);
1982}
reede47829b2015-08-06 10:02:53 -07001983
reed41af9662015-01-05 07:49:08 -08001984void SkCanvas::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
1985 const SkPaint* paint) {
reed4c21dc52015-06-25 12:32:03 -07001986 if (bitmap.drawsNothing() || dst.isEmpty()) {
tomhudson2df6fd62015-04-09 09:20:19 -07001987 return;
1988 }
msarett552bca92016-08-03 06:53:26 -07001989 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), center)) {
1990 this->onDrawBitmapNine(bitmap, center, dst, paint);
1991 } else {
reeda5517e22015-07-14 10:54:12 -07001992 this->drawBitmapRect(bitmap, dst, paint);
reed4c21dc52015-06-25 12:32:03 -07001993 }
reed41af9662015-01-05 07:49:08 -08001994}
1995
msarettc573a402016-08-02 08:05:56 -07001996void SkCanvas::drawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice, const SkRect& dst,
1997 const SkPaint* paint) {
msarett16882062016-08-16 09:31:08 -07001998 if (bitmap.drawsNothing() || dst.isEmpty()) {
msarettc573a402016-08-02 08:05:56 -07001999 return;
2000 }
msarett71df2d72016-09-30 12:41:42 -07002001
2002 SkIRect bounds;
2003 Lattice latticePlusBounds = lattice;
2004 if (!latticePlusBounds.fBounds) {
2005 bounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
2006 latticePlusBounds.fBounds = &bounds;
2007 }
2008
2009 if (SkLatticeIter::Valid(bitmap.width(), bitmap.height(), latticePlusBounds)) {
2010 this->onDrawBitmapLattice(bitmap, latticePlusBounds, dst, paint);
msarett552bca92016-08-03 06:53:26 -07002011 } else {
msarett16882062016-08-16 09:31:08 -07002012 this->drawBitmapRect(bitmap, dst, paint);
msarettc573a402016-08-02 08:05:56 -07002013 }
msarettc573a402016-08-02 08:05:56 -07002014}
2015
reed71c3c762015-06-24 10:29:17 -07002016void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2017 const SkColor colors[], int count, SkXfermode::Mode mode,
2018 const SkRect* cull, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08002019 RETURN_ON_NULL(atlas);
reed71c3c762015-06-24 10:29:17 -07002020 if (count <= 0) {
2021 return;
2022 }
2023 SkASSERT(atlas);
2024 SkASSERT(xform);
2025 SkASSERT(tex);
2026 this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint);
2027}
2028
reedf70b5312016-03-04 16:36:20 -08002029void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2030 if (key) {
2031 this->onDrawAnnotation(rect, key, value);
2032 }
2033}
2034
reede47829b2015-08-06 10:02:53 -07002035void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
2036 const SkPaint* paint, SrcRectConstraint constraint) {
2037 if (src) {
2038 this->drawImageRect(image, *src, dst, paint, constraint);
2039 } else {
2040 this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()),
2041 dst, paint, constraint);
2042 }
2043}
2044void SkCanvas::legacy_drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
2045 const SkPaint* paint, SrcRectConstraint constraint) {
2046 if (src) {
2047 this->drawBitmapRect(bitmap, *src, dst, paint, constraint);
2048 } else {
2049 this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()),
2050 dst, paint, constraint);
2051 }
2052}
2053
tomhudsoncb3bd182016-05-18 07:24:16 -07002054void SkCanvas::temporary_internal_describeTopLayer(SkMatrix* matrix, SkIRect* clip_bounds) {
2055 SkIRect layer_bounds = this->getTopLayerBounds();
2056 if (matrix) {
2057 *matrix = this->getTotalMatrix();
2058 matrix->preTranslate(-layer_bounds.left(), -layer_bounds.top());
2059 }
2060 if (clip_bounds) {
2061 this->getClipDeviceBounds(clip_bounds);
2062 clip_bounds->offset(-layer_bounds.left(), -layer_bounds.top());
2063 }
2064}
2065
reed@android.com8a1c16f2008-12-17 15:59:43 +00002066//////////////////////////////////////////////////////////////////////////////
2067// These are the virtual drawing methods
2068//////////////////////////////////////////////////////////////////////////////
2069
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002070void SkCanvas::onDiscard() {
bsalomon49f085d2014-09-05 13:34:00 -07002071 if (fSurfaceBase) {
commit-bot@chromium.org28361fa2014-03-28 16:08:05 +00002072 fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode);
2073 }
2074}
2075
reed41af9662015-01-05 07:49:08 -08002076void SkCanvas::onDrawPaint(const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002077 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPaint()");
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002078 this->internalDrawPaint(paint);
2079}
2080
2081void SkCanvas::internalDrawPaint(const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002082 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kPaint_Type, nullptr, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002083
2084 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002085 iter.fDevice->drawPaint(iter, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002086 }
2087
reed@google.com4e2b3d32011-04-07 14:18:59 +00002088 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002089}
2090
reed41af9662015-01-05 07:49:08 -08002091void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[],
2092 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002093 TRACE_EVENT1("disabled-by-default-skia", "SkCanvas::drawPoints()", "count", static_cast<uint64_t>(count));
reed@android.com8a1c16f2008-12-17 15:59:43 +00002094 if ((long)count <= 0) {
2095 return;
2096 }
2097
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002098 SkRect r, storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002099 const SkRect* bounds = nullptr;
reed@google.coma584aed2012-05-16 14:06:02 +00002100 if (paint.canComputeFastBounds()) {
reed@google.coma584aed2012-05-16 14:06:02 +00002101 // special-case 2 points (common for drawing a single line)
2102 if (2 == count) {
2103 r.set(pts[0], pts[1]);
2104 } else {
commit-bot@chromium.orga8c7f772014-01-24 21:46:29 +00002105 r.set(pts, SkToInt(count));
reed@google.coma584aed2012-05-16 14:06:02 +00002106 }
senorblanco87e066e2015-10-28 11:23:36 -07002107 if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
2108 return;
2109 }
2110 bounds = &r;
rmistry@google.comfbfcd562012-08-23 18:09:54 +00002111 }
reed@google.coma584aed2012-05-16 14:06:02 +00002112
halcanary96fcdcc2015-08-27 07:41:13 -07002113 SkASSERT(pts != nullptr);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002114
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002115 LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type, bounds)
reed@google.com4b226022011-01-11 18:32:13 +00002116
reed@android.com8a1c16f2008-12-17 15:59:43 +00002117 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002118 iter.fDevice->drawPoints(iter, mode, count, pts, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002119 }
reed@google.com4b226022011-01-11 18:32:13 +00002120
reed@google.com4e2b3d32011-04-07 14:18:59 +00002121 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002122}
2123
reed4a167172016-08-18 17:15:25 -07002124static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) {
2125 return ((intptr_t)paint.getImageFilter() |
2126#ifdef SK_SUPPORT_LEGACY_DRAWFILTER
2127 (intptr_t)canvas->getDrawFilter() |
2128#endif
2129 (intptr_t)paint.getLooper() ) != 0;
2130}
2131
reed41af9662015-01-05 07:49:08 -08002132void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002133 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002134 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002135 const SkRect* bounds = nullptr;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002136 if (paint.canComputeFastBounds()) {
reed84328082015-02-10 14:18:09 -08002137 // Skia will draw an inverted rect, because it explicitly "sorts" it downstream.
2138 // To prevent accidental rejecting at this stage, we have to sort it before we check.
2139 SkRect tmp(r);
2140 tmp.sort();
2141
senorblanco87e066e2015-10-28 11:23:36 -07002142 if (this->quickReject(paint.computeFastBounds(tmp, &storage))) {
2143 return;
2144 }
2145 bounds = &r;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002146 }
reed@google.com4b226022011-01-11 18:32:13 +00002147
reed4a167172016-08-18 17:15:25 -07002148 if (needs_autodrawlooper(this, paint)) {
2149 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, SkDrawFilter::kRect_Type, bounds, false)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002150
reed4a167172016-08-18 17:15:25 -07002151 while (iter.next()) {
2152 iter.fDevice->drawRect(iter, r, looper.paint());
2153 }
2154
2155 LOOPER_END
2156 } else {
2157 this->predrawNotify(bounds, &paint, false);
2158 SkDrawIter iter(this);
2159 while (iter.next()) {
2160 iter.fDevice->drawRect(iter, r, paint);
2161 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002162 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002163}
2164
msarett44df6512016-08-25 13:54:30 -07002165void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
2166 SkRect storage;
2167 SkRect regionRect = SkRect::Make(region.getBounds());
2168 const SkRect* bounds = nullptr;
2169 if (paint.canComputeFastBounds()) {
2170 if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) {
2171 return;
2172 }
2173 bounds = &regionRect;
2174 }
2175
2176 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, bounds)
2177
2178 while (iter.next()) {
2179 iter.fDevice->drawRegion(iter, region, looper.paint());
2180 }
2181
2182 LOOPER_END
2183}
2184
reed41af9662015-01-05 07:49:08 -08002185void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002186 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawOval()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002187 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002188 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002189 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002190 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2191 return;
2192 }
2193 bounds = &oval;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002194 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +00002195
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002196 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
jvanverth@google.com46d3d392013-01-22 13:34:01 +00002197
2198 while (iter.next()) {
2199 iter.fDevice->drawOval(iter, oval, looper.paint());
2200 }
2201
2202 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002203}
2204
bsalomonac3aa242016-08-19 11:25:19 -07002205void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle,
2206 SkScalar sweepAngle, bool useCenter,
2207 const SkPaint& paint) {
2208 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
2209 const SkRect* bounds = nullptr;
2210 if (paint.canComputeFastBounds()) {
2211 SkRect storage;
2212 // Note we're using the entire oval as the bounds.
2213 if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
2214 return;
2215 }
2216 bounds = &oval;
2217 }
2218
2219 LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type, bounds)
2220
2221 while (iter.next()) {
2222 iter.fDevice->drawArc(iter, oval, startAngle, sweepAngle, useCenter, looper.paint());
2223 }
2224
2225 LOOPER_END
2226}
2227
reed41af9662015-01-05 07:49:08 -08002228void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002229 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRRect()");
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002230 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002231 const SkRect* bounds = nullptr;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002232 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002233 if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
2234 return;
2235 }
2236 bounds = &rrect.getBounds();
reed@google.com4ed0fb72012-12-12 20:48:18 +00002237 }
2238
2239 if (rrect.isRect()) {
2240 // call the non-virtual version
2241 this->SkCanvas::drawRect(rrect.getBounds(), paint);
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002242 return;
2243 } else if (rrect.isOval()) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00002244 // call the non-virtual version
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002245 this->SkCanvas::drawOval(rrect.getBounds(), paint);
2246 return;
reed@google.com4ed0fb72012-12-12 20:48:18 +00002247 }
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002248
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002249 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
commit-bot@chromium.orgf2bfd542013-04-25 15:27:00 +00002250
2251 while (iter.next()) {
2252 iter.fDevice->drawRRect(iter, rrect, looper.paint());
2253 }
2254
2255 LOOPER_END
reed@google.com4ed0fb72012-12-12 20:48:18 +00002256}
2257
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002258void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
2259 const SkPaint& paint) {
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002260 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002261 const SkRect* bounds = nullptr;
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002262 if (paint.canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002263 if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) {
2264 return;
2265 }
2266 bounds = &outer.getBounds();
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002267 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002268
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002269 LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002270
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002271 while (iter.next()) {
2272 iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
2273 }
skia.committer@gmail.com25c71272014-02-21 03:02:02 +00002274
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +00002275 LOOPER_END
2276}
reed@google.com4ed0fb72012-12-12 20:48:18 +00002277
reed41af9662015-01-05 07:49:08 -08002278void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002279 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPath()");
reed@google.com93645112012-07-26 16:11:47 +00002280 if (!path.isFinite()) {
2281 return;
2282 }
2283
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002284 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002285 const SkRect* bounds = nullptr;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002286 if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002287 const SkRect& pathBounds = path.getBounds();
senorblanco87e066e2015-10-28 11:23:36 -07002288 if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) {
2289 return;
2290 }
2291 bounds = &pathBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002292 }
commit-bot@chromium.org0b45dc42014-02-21 05:42:57 +00002293
2294 const SkRect& r = path.getBounds();
2295 if (r.width() <= 0 && r.height() <= 0) {
commit-bot@chromium.org6803c212014-05-04 18:08:27 +00002296 if (path.isInverseFillType()) {
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002297 this->internalDrawPaint(paint);
caryclark6651a322015-09-09 13:20:49 -07002298 return;
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002299 }
bsalomon@google.comfa6ac932011-10-05 19:57:55 +00002300 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002301
senorblanco@chromium.org78cf1192014-01-28 19:22:35 +00002302 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, bounds)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002303
2304 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002305 iter.fDevice->drawPath(iter, path, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002306 }
2307
reed@google.com4e2b3d32011-04-07 14:18:59 +00002308 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002309}
2310
reed262a71b2015-12-05 13:07:27 -08002311bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
reed262a71b2015-12-05 13:07:27 -08002312 if (!paint.getImageFilter()) {
2313 return false;
2314 }
2315
2316 const SkMatrix& ctm = this->getTotalMatrix();
fmalitac7e211a2016-01-07 10:34:46 -08002317 if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
reed262a71b2015-12-05 13:07:27 -08002318 return false;
2319 }
2320
2321 // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
2322 // Once we can filter and the filter will return a result larger than itself, we should be
2323 // able to remove this constraint.
2324 // skbug.com/4526
2325 //
2326 SkPoint pt;
2327 ctm.mapXY(x, y, &pt);
2328 SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
2329 return ir.contains(fMCRec->fRasterClip.getBounds());
2330}
2331
reeda85d4d02015-05-06 12:56:48 -07002332void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002333 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
reeda85d4d02015-05-06 12:56:48 -07002334 SkRect bounds = SkRect::MakeXYWH(x, y,
2335 SkIntToScalar(image->width()), SkIntToScalar(image->height()));
halcanary96fcdcc2015-08-27 07:41:13 -07002336 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblanco87e066e2015-10-28 11:23:36 -07002337 SkRect tmp = bounds;
2338 if (paint) {
2339 paint->computeFastBounds(tmp, &tmp);
2340 }
2341 if (this->quickReject(tmp)) {
2342 return;
2343 }
reeda85d4d02015-05-06 12:56:48 -07002344 }
halcanary9d524f22016-03-29 09:03:52 -07002345
reeda85d4d02015-05-06 12:56:48 -07002346 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002347 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002348 paint = lazy.init();
2349 }
reed262a71b2015-12-05 13:07:27 -08002350
reeda2217ef2016-07-20 06:04:34 -07002351 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002352 bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
2353 *paint);
2354 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002355 special = this->getDevice()->makeSpecial(image);
2356 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002357 drawAsSprite = false;
reed129ed1c2016-02-22 06:42:31 -08002358 }
2359 }
2360
reed262a71b2015-12-05 13:07:27 -08002361 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
2362
reeda85d4d02015-05-06 12:56:48 -07002363 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002364 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002365 if (special) {
2366 SkPoint pt;
2367 iter.fMatrix->mapXY(x, y, &pt);
2368 iter.fDevice->drawSpecial(iter, special.get(),
2369 SkScalarRoundToInt(pt.fX),
2370 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002371 } else {
2372 iter.fDevice->drawImage(iter, image, x, y, pnt);
2373 }
reeda85d4d02015-05-06 12:56:48 -07002374 }
halcanary9d524f22016-03-29 09:03:52 -07002375
reeda85d4d02015-05-06 12:56:48 -07002376 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002377}
2378
reed41af9662015-01-05 07:49:08 -08002379void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002380 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002381 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
halcanary96fcdcc2015-08-27 07:41:13 -07002382 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002383 SkRect storage = dst;
senorblanco87e066e2015-10-28 11:23:36 -07002384 if (paint) {
2385 paint->computeFastBounds(dst, &storage);
2386 }
2387 if (this->quickReject(storage)) {
2388 return;
2389 }
reeda85d4d02015-05-06 12:56:48 -07002390 }
2391 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002392 if (nullptr == paint) {
reeda85d4d02015-05-06 12:56:48 -07002393 paint = lazy.init();
2394 }
halcanary9d524f22016-03-29 09:03:52 -07002395
senorblancoc41e7e12015-12-07 12:51:30 -08002396 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002397 image->isOpaque())
halcanary9d524f22016-03-29 09:03:52 -07002398
reeda85d4d02015-05-06 12:56:48 -07002399 while (iter.next()) {
reeda5517e22015-07-14 10:54:12 -07002400 iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint);
reeda85d4d02015-05-06 12:56:48 -07002401 }
halcanary9d524f22016-03-29 09:03:52 -07002402
reeda85d4d02015-05-06 12:56:48 -07002403 LOOPER_END
piotaixrb5fae932014-09-24 13:03:30 -07002404}
2405
reed41af9662015-01-05 07:49:08 -08002406void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002407 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmap()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002408 SkDEBUGCODE(bitmap.validate();)
2409
reed33366972015-10-08 09:22:02 -07002410 if (bitmap.drawsNothing()) {
2411 return;
2412 }
2413
2414 SkLazyPaint lazy;
2415 if (nullptr == paint) {
2416 paint = lazy.init();
2417 }
2418
2419 const SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2420
2421 SkRect storage;
2422 const SkRect* bounds = nullptr;
2423 if (paint->canComputeFastBounds()) {
2424 bitmap.getBounds(&storage);
2425 matrix.mapRect(&storage);
senorblanco87e066e2015-10-28 11:23:36 -07002426 SkRect tmp = storage;
2427 if (this->quickReject(paint->computeFastBounds(tmp, &tmp))) {
2428 return;
2429 }
2430 bounds = &storage;
reed@android.com8a1c16f2008-12-17 15:59:43 +00002431 }
reed@google.com4b226022011-01-11 18:32:13 +00002432
reeda2217ef2016-07-20 06:04:34 -07002433 sk_sp<SkSpecialImage> special;
reed129ed1c2016-02-22 06:42:31 -08002434 bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(),
2435 *paint);
2436 if (drawAsSprite && paint->getImageFilter()) {
reeda2217ef2016-07-20 06:04:34 -07002437 special = this->getDevice()->makeSpecial(bitmap);
2438 if (!special) {
reed129ed1c2016-02-22 06:42:31 -08002439 drawAsSprite = false;
2440 }
2441 }
2442
reed262a71b2015-12-05 13:07:27 -08002443 LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
reed33366972015-10-08 09:22:02 -07002444
2445 while (iter.next()) {
reed262a71b2015-12-05 13:07:27 -08002446 const SkPaint& pnt = looper.paint();
reeda2217ef2016-07-20 06:04:34 -07002447 if (special) {
reed262a71b2015-12-05 13:07:27 -08002448 SkPoint pt;
2449 iter.fMatrix->mapXY(x, y, &pt);
reeda2217ef2016-07-20 06:04:34 -07002450 iter.fDevice->drawSpecial(iter, special.get(),
2451 SkScalarRoundToInt(pt.fX),
2452 SkScalarRoundToInt(pt.fY), pnt);
reed262a71b2015-12-05 13:07:27 -08002453 } else {
2454 iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
2455 }
reed33366972015-10-08 09:22:02 -07002456 }
msarettfbfa2582016-08-12 08:29:08 -07002457
reed33366972015-10-08 09:22:02 -07002458 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002459}
2460
reed@google.com9987ec32011-09-07 11:57:52 +00002461// this one is non-virtual, so it can be called safely by other canvas apis
reed@google.com71121732012-09-18 15:14:33 +00002462void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
commit-bot@chromium.orgeed779d2013-08-16 10:24:37 +00002463 const SkRect& dst, const SkPaint* paint,
reeda5517e22015-07-14 10:54:12 -07002464 SrcRectConstraint constraint) {
commit-bot@chromium.org50b393a2014-02-10 18:29:10 +00002465 if (bitmap.drawsNothing() || dst.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00002466 return;
2467 }
vandebo@chromium.org74b46192012-01-28 01:45:11 +00002468
halcanary96fcdcc2015-08-27 07:41:13 -07002469 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002470 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002471 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2472 return;
2473 }
reed@android.com8a1c16f2008-12-17 15:59:43 +00002474 }
reed@google.com3d608122011-11-21 15:16:16 +00002475
reed@google.com33535f32012-09-25 15:37:50 +00002476 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002477 if (nullptr == paint) {
reed@google.com33535f32012-09-25 15:37:50 +00002478 paint = lazy.init();
reed@android.com8a1c16f2008-12-17 15:59:43 +00002479 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002480
senorblancoc41e7e12015-12-07 12:51:30 -08002481 LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst,
reedc83a2972015-07-16 07:40:45 -07002482 bitmap.isOpaque())
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002483
reed@google.com33535f32012-09-25 15:37:50 +00002484 while (iter.next()) {
reed562fe472015-07-28 07:35:14 -07002485 iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint(), constraint);
reed@android.comf2b98d62010-12-20 18:26:13 +00002486 }
skia.committer@gmail.com7064e9a2012-09-26 02:01:18 +00002487
reed@google.com33535f32012-09-25 15:37:50 +00002488 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002489}
2490
reed41af9662015-01-05 07:49:08 -08002491void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
reed562fe472015-07-28 07:35:14 -07002492 const SkPaint* paint, SrcRectConstraint constraint) {
danakj9881d632014-11-26 12:41:06 -08002493 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapRectToRect()");
reed@google.com9987ec32011-09-07 11:57:52 +00002494 SkDEBUGCODE(bitmap.validate();)
reed562fe472015-07-28 07:35:14 -07002495 this->internalDrawBitmapRect(bitmap, src, dst, paint, constraint);
reed@google.com9987ec32011-09-07 11:57:52 +00002496}
2497
reed4c21dc52015-06-25 12:32:03 -07002498void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
2499 const SkPaint* paint) {
2500 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()");
halcanary9d524f22016-03-29 09:03:52 -07002501
halcanary96fcdcc2015-08-27 07:41:13 -07002502 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002503 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002504 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2505 return;
2506 }
reed@google.com3d608122011-11-21 15:16:16 +00002507 }
halcanary9d524f22016-03-29 09:03:52 -07002508
reed4c21dc52015-06-25 12:32:03 -07002509 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002510 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002511 paint = lazy.init();
reed@google.com9987ec32011-09-07 11:57:52 +00002512 }
halcanary9d524f22016-03-29 09:03:52 -07002513
senorblancoc41e7e12015-12-07 12:51:30 -08002514 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002515
reed4c21dc52015-06-25 12:32:03 -07002516 while (iter.next()) {
2517 iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint());
reed@google.com9987ec32011-09-07 11:57:52 +00002518 }
halcanary9d524f22016-03-29 09:03:52 -07002519
reed4c21dc52015-06-25 12:32:03 -07002520 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002521}
2522
reed41af9662015-01-05 07:49:08 -08002523void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
2524 const SkPaint* paint) {
danakj9881d632014-11-26 12:41:06 -08002525 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawBitmapNine()");
reed@google.com9987ec32011-09-07 11:57:52 +00002526 SkDEBUGCODE(bitmap.validate();)
2527
halcanary96fcdcc2015-08-27 07:41:13 -07002528 if (nullptr == paint || paint->canComputeFastBounds()) {
senorblancoc41e7e12015-12-07 12:51:30 -08002529 SkRect storage;
senorblanco87e066e2015-10-28 11:23:36 -07002530 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2531 return;
2532 }
reed4c21dc52015-06-25 12:32:03 -07002533 }
halcanary9d524f22016-03-29 09:03:52 -07002534
reed4c21dc52015-06-25 12:32:03 -07002535 SkLazyPaint lazy;
halcanary96fcdcc2015-08-27 07:41:13 -07002536 if (nullptr == paint) {
reed4c21dc52015-06-25 12:32:03 -07002537 paint = lazy.init();
2538 }
halcanary9d524f22016-03-29 09:03:52 -07002539
senorblancoc41e7e12015-12-07 12:51:30 -08002540 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
halcanary9d524f22016-03-29 09:03:52 -07002541
reed4c21dc52015-06-25 12:32:03 -07002542 while (iter.next()) {
2543 iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint());
2544 }
halcanary9d524f22016-03-29 09:03:52 -07002545
reed4c21dc52015-06-25 12:32:03 -07002546 LOOPER_END
reed@google.com9987ec32011-09-07 11:57:52 +00002547}
2548
msarett16882062016-08-16 09:31:08 -07002549void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst,
2550 const SkPaint* paint) {
2551 if (nullptr == paint || paint->canComputeFastBounds()) {
2552 SkRect storage;
2553 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2554 return;
2555 }
2556 }
2557
2558 SkLazyPaint lazy;
2559 if (nullptr == paint) {
2560 paint = lazy.init();
2561 }
2562
2563 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2564
2565 while (iter.next()) {
2566 iter.fDevice->drawImageLattice(iter, image, lattice, dst, looper.paint());
2567 }
2568
2569 LOOPER_END
2570}
2571
2572void SkCanvas::onDrawBitmapLattice(const SkBitmap& bitmap, const Lattice& lattice,
2573 const SkRect& dst, const SkPaint* paint) {
2574 if (nullptr == paint || paint->canComputeFastBounds()) {
2575 SkRect storage;
2576 if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) {
2577 return;
2578 }
2579 }
2580
2581 SkLazyPaint lazy;
2582 if (nullptr == paint) {
2583 paint = lazy.init();
2584 }
2585
2586 LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst)
2587
2588 while (iter.next()) {
2589 iter.fDevice->drawBitmapLattice(iter, bitmap, lattice, dst, looper.paint());
2590 }
2591
2592 LOOPER_END
2593}
2594
reed@google.comf67e4cf2011-03-15 20:56:58 +00002595class SkDeviceFilteredPaint {
2596public:
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00002597 SkDeviceFilteredPaint(SkBaseDevice* device, const SkPaint& paint) {
fmalita112e7e22014-11-13 14:05:58 -08002598 uint32_t filteredFlags = device->filterTextFlags(paint);
2599 if (filteredFlags != paint.getFlags()) {
reed@google.coma076e9b2011-04-06 20:17:29 +00002600 SkPaint* newPaint = fLazy.set(paint);
fmalita112e7e22014-11-13 14:05:58 -08002601 newPaint->setFlags(filteredFlags);
reed@google.comf67e4cf2011-03-15 20:56:58 +00002602 fPaint = newPaint;
2603 } else {
2604 fPaint = &paint;
2605 }
2606 }
2607
reed@google.comf67e4cf2011-03-15 20:56:58 +00002608 const SkPaint& paint() const { return *fPaint; }
2609
2610private:
mike@reedtribe.org2c8fc5a2011-04-10 00:35:29 +00002611 const SkPaint* fPaint;
2612 SkLazyPaint fLazy;
reed@google.comf67e4cf2011-03-15 20:56:58 +00002613};
2614
bungeman@google.com52c748b2011-08-22 21:30:43 +00002615void SkCanvas::DrawRect(const SkDraw& draw, const SkPaint& paint,
2616 const SkRect& r, SkScalar textSize) {
epoger@google.com17b78942011-08-26 14:40:38 +00002617 if (paint.getStyle() == SkPaint::kFill_Style) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002618 draw.fDevice->drawRect(draw, r, paint);
2619 } else {
2620 SkPaint p(paint);
epoger@google.com17b78942011-08-26 14:40:38 +00002621 p.setStrokeWidth(SkScalarMul(textSize, paint.getStrokeWidth()));
bungeman@google.com52c748b2011-08-22 21:30:43 +00002622 draw.fDevice->drawRect(draw, r, p);
2623 }
2624}
2625
2626void SkCanvas::DrawTextDecorations(const SkDraw& draw, const SkPaint& paint,
2627 const char text[], size_t byteLength,
2628 SkScalar x, SkScalar y) {
halcanary96fcdcc2015-08-27 07:41:13 -07002629 SkASSERT(byteLength == 0 || text != nullptr);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002630
2631 // nothing to draw
halcanary96fcdcc2015-08-27 07:41:13 -07002632 if (text == nullptr || byteLength == 0 ||
reed1e7f5e72016-04-27 07:49:17 -07002633 draw.fRC->isEmpty() ||
reed374772b2016-10-05 17:33:02 -07002634 (paint.getAlpha() == 0 && paint.isSrcOver())) {
bungeman@google.com52c748b2011-08-22 21:30:43 +00002635 return;
2636 }
2637
2638 SkScalar width = 0;
2639 SkPoint start;
2640
2641 start.set(0, 0); // to avoid warning
2642 if (paint.getFlags() & (SkPaint::kUnderlineText_Flag |
2643 SkPaint::kStrikeThruText_Flag)) {
2644 width = paint.measureText(text, byteLength);
2645
2646 SkScalar offsetX = 0;
2647 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
2648 offsetX = SkScalarHalf(width);
2649 } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
2650 offsetX = width;
2651 }
2652 start.set(x - offsetX, y);
2653 }
2654
2655 if (0 == width) {
2656 return;
2657 }
2658
2659 uint32_t flags = paint.getFlags();
2660
2661 if (flags & (SkPaint::kUnderlineText_Flag |
2662 SkPaint::kStrikeThruText_Flag)) {
2663 SkScalar textSize = paint.getTextSize();
2664 SkScalar height = SkScalarMul(textSize, kStdUnderline_Thickness);
2665 SkRect r;
2666
2667 r.fLeft = start.fX;
2668 r.fRight = start.fX + width;
2669
2670 if (flags & SkPaint::kUnderlineText_Flag) {
2671 SkScalar offset = SkScalarMulAdd(textSize, kStdUnderline_Offset,
2672 start.fY);
2673 r.fTop = offset;
2674 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002675 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002676 }
2677 if (flags & SkPaint::kStrikeThruText_Flag) {
2678 SkScalar offset = SkScalarMulAdd(textSize, kStdStrikeThru_Offset,
2679 start.fY);
2680 r.fTop = offset;
2681 r.fBottom = offset + height;
caryclarkfb562182015-12-21 08:35:51 -08002682 DrawRect(draw, paint, r, 1);
bungeman@google.com52c748b2011-08-22 21:30:43 +00002683 }
2684 }
2685}
2686
reed@google.come0d9ce82014-04-23 04:00:17 +00002687void SkCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2688 const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002689 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
reed@android.com8a1c16f2008-12-17 15:59:43 +00002690
2691 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002692 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
reed@google.comf67e4cf2011-03-15 20:56:58 +00002693 iter.fDevice->drawText(iter, text, byteLength, x, y, dfp.paint());
bungeman@google.com52c748b2011-08-22 21:30:43 +00002694 DrawTextDecorations(iter, dfp.paint(),
2695 static_cast<const char*>(text), byteLength, x, y);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002696 }
2697
reed@google.com4e2b3d32011-04-07 14:18:59 +00002698 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002699}
2700
reed@google.come0d9ce82014-04-23 04:00:17 +00002701void SkCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2702 const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002703 SkPoint textOffset = SkPoint::Make(0, 0);
2704
halcanary96fcdcc2015-08-27 07:41:13 -07002705 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002706
reed@android.com8a1c16f2008-12-17 15:59:43 +00002707 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002708 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002709 iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 2, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002710 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002711 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002712
reed@google.com4e2b3d32011-04-07 14:18:59 +00002713 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002714}
2715
reed@google.come0d9ce82014-04-23 04:00:17 +00002716void SkCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2717 SkScalar constY, const SkPaint& paint) {
fmalita05c4a432014-09-29 06:29:53 -07002718
2719 SkPoint textOffset = SkPoint::Make(0, constY);
2720
halcanary96fcdcc2015-08-27 07:41:13 -07002721 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002722
reed@android.com8a1c16f2008-12-17 15:59:43 +00002723 while (iter.next()) {
reed@google.com4e2b3d32011-04-07 14:18:59 +00002724 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita05c4a432014-09-29 06:29:53 -07002725 iter.fDevice->drawPosText(iter, text, byteLength, xpos, 1, textOffset,
reed@google.comf67e4cf2011-03-15 20:56:58 +00002726 dfp.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002727 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002728
reed@google.com4e2b3d32011-04-07 14:18:59 +00002729 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002730}
2731
reed@google.come0d9ce82014-04-23 04:00:17 +00002732void SkCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2733 const SkMatrix* matrix, const SkPaint& paint) {
halcanary96fcdcc2015-08-27 07:41:13 -07002734 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
commit-bot@chromium.org87fcd952014-04-23 19:10:51 +00002735
reed@android.com8a1c16f2008-12-17 15:59:43 +00002736 while (iter.next()) {
2737 iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002738 matrix, looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002739 }
skia.committer@gmail.comb0430d02014-04-24 03:05:07 +00002740
commit-bot@chromium.org945ec3a2014-04-22 20:07:30 +00002741 LOOPER_END
commit-bot@chromium.org4325d112014-04-22 19:03:02 +00002742}
2743
reed45561a02016-07-07 12:47:17 -07002744void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2745 const SkRect* cullRect, const SkPaint& paint) {
2746 if (cullRect && this->quickReject(*cullRect)) {
2747 return;
2748 }
2749
2750 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
2751
2752 while (iter.next()) {
2753 iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
2754 }
2755
2756 LOOPER_END
2757}
2758
fmalita00d5c2c2014-08-21 08:53:26 -07002759void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2760 const SkPaint& paint) {
fmalita7ba7aa72014-08-29 09:46:36 -07002761
fmalita85d5eb92015-03-04 11:20:12 -08002762 SkRect storage;
halcanary96fcdcc2015-08-27 07:41:13 -07002763 const SkRect* bounds = nullptr;
fmalita19653d12014-10-16 11:53:30 -07002764 if (paint.canComputeFastBounds()) {
fmalita85d5eb92015-03-04 11:20:12 -08002765 storage = blob->bounds().makeOffset(x, y);
senorblanco87e066e2015-10-28 11:23:36 -07002766 SkRect tmp;
2767 if (this->quickReject(paint.computeFastBounds(storage, &tmp))) {
2768 return;
2769 }
2770 bounds = &storage;
fmalita7ba7aa72014-08-29 09:46:36 -07002771 }
2772
fmalita024f9962015-03-03 19:08:17 -08002773 // We cannot filter in the looper as we normally do, because the paint is
2774 // incomplete at this point (text-related attributes are embedded within blob run paints).
2775 SkDrawFilter* drawFilter = fMCRec->fFilter;
halcanary96fcdcc2015-08-27 07:41:13 -07002776 fMCRec->fFilter = nullptr;
fmalita024f9962015-03-03 19:08:17 -08002777
fmalita85d5eb92015-03-04 11:20:12 -08002778 LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, bounds)
fmalita00d5c2c2014-08-21 08:53:26 -07002779
fmalitaaa1b9122014-08-28 14:32:24 -07002780 while (iter.next()) {
2781 SkDeviceFilteredPaint dfp(iter.fDevice, looper.paint());
fmalita024f9962015-03-03 19:08:17 -08002782 iter.fDevice->drawTextBlob(iter, blob, x, y, dfp.paint(), drawFilter);
fmalita00d5c2c2014-08-21 08:53:26 -07002783 }
2784
fmalitaaa1b9122014-08-28 14:32:24 -07002785 LOOPER_END
fmalita024f9962015-03-03 19:08:17 -08002786
2787 fMCRec->fFilter = drawFilter;
fmalita00d5c2c2014-08-21 08:53:26 -07002788}
2789
reed@google.come0d9ce82014-04-23 04:00:17 +00002790// These will become non-virtual, so they always call the (virtual) onDraw... method
2791void SkCanvas::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
2792 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002793 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawText()");
reedac095542016-08-04 15:54:41 -07002794 if (byteLength) {
2795 this->onDrawText(text, byteLength, x, y, paint);
2796 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002797}
2798void SkCanvas::drawPosText(const void* text, size_t byteLength, const SkPoint pos[],
2799 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002800 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosText()");
reedac095542016-08-04 15:54:41 -07002801 if (byteLength) {
2802 this->onDrawPosText(text, byteLength, pos, paint);
2803 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002804}
2805void SkCanvas::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
2806 SkScalar constY, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002807 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPosTextH()");
reedac095542016-08-04 15:54:41 -07002808 if (byteLength) {
2809 this->onDrawPosTextH(text, byteLength, xpos, constY, paint);
2810 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002811}
2812void SkCanvas::drawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
2813 const SkMatrix* matrix, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002814 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
reedac095542016-08-04 15:54:41 -07002815 if (byteLength) {
2816 this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
2817 }
reed@google.come0d9ce82014-04-23 04:00:17 +00002818}
reed45561a02016-07-07 12:47:17 -07002819void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
2820 const SkRect* cullRect, const SkPaint& paint) {
2821 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
2822 if (byteLength) {
2823 this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
2824 }
2825}
fmalita00d5c2c2014-08-21 08:53:26 -07002826void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
2827 const SkPaint& paint) {
reede3b38ce2016-01-08 09:18:44 -08002828 RETURN_ON_NULL(blob);
danakj9881d632014-11-26 12:41:06 -08002829 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextBlob()");
reede3b38ce2016-01-08 09:18:44 -08002830 this->onDrawTextBlob(blob, x, y, paint);
fmalita00d5c2c2014-08-21 08:53:26 -07002831}
reed@google.come0d9ce82014-04-23 04:00:17 +00002832
reed41af9662015-01-05 07:49:08 -08002833void SkCanvas::onDrawVertices(VertexMode vmode, int vertexCount,
2834 const SkPoint verts[], const SkPoint texs[],
2835 const SkColor colors[], SkXfermode* xmode,
2836 const uint16_t indices[], int indexCount,
2837 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002838 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawVertices()");
halcanary96fcdcc2015-08-27 07:41:13 -07002839 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
reed@google.com4b226022011-01-11 18:32:13 +00002840
reed@android.com8a1c16f2008-12-17 15:59:43 +00002841 while (iter.next()) {
2842 iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
reed@google.com4e2b3d32011-04-07 14:18:59 +00002843 colors, xmode, indices, indexCount,
2844 looper.paint());
reed@android.com8a1c16f2008-12-17 15:59:43 +00002845 }
reed@google.com4b226022011-01-11 18:32:13 +00002846
reed@google.com4e2b3d32011-04-07 14:18:59 +00002847 LOOPER_END
reed@android.com8a1c16f2008-12-17 15:59:43 +00002848}
2849
dandovb3c9d1c2014-08-12 08:34:29 -07002850void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4],
2851 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002852 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPatch()");
halcanary96fcdcc2015-08-27 07:41:13 -07002853 if (nullptr == cubics) {
dandovb3c9d1c2014-08-12 08:34:29 -07002854 return;
2855 }
mtklein6cfa73a2014-08-13 13:33:49 -07002856
msarett9340c262016-09-22 05:20:21 -07002857 this->onDrawPatch(cubics, colors, texCoords, xmode, paint);
2858}
2859
2860void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
2861 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
dandovecfff212014-08-04 10:02:00 -07002862 // Since a patch is always within the convex hull of the control points, we discard it when its
2863 // bounding rectangle is completely outside the current clip.
2864 SkRect bounds;
dandovb3c9d1c2014-08-12 08:34:29 -07002865 bounds.set(cubics, SkPatchUtils::kNumCtrlPts);
dandovecfff212014-08-04 10:02:00 -07002866 if (this->quickReject(bounds)) {
2867 return;
2868 }
mtklein6cfa73a2014-08-13 13:33:49 -07002869
halcanary96fcdcc2015-08-27 07:41:13 -07002870 LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, nullptr)
mtklein6cfa73a2014-08-13 13:33:49 -07002871
dandovecfff212014-08-04 10:02:00 -07002872 while (iter.next()) {
dandovb3c9d1c2014-08-12 08:34:29 -07002873 iter.fDevice->drawPatch(iter, cubics, colors, texCoords, xmode, paint);
dandovecfff212014-08-04 10:02:00 -07002874 }
mtklein6cfa73a2014-08-13 13:33:49 -07002875
dandovecfff212014-08-04 10:02:00 -07002876 LOOPER_END
2877}
2878
reeda8db7282015-07-07 10:22:31 -07002879void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) {
reede3b38ce2016-01-08 09:18:44 -08002880 RETURN_ON_NULL(dr);
2881 if (x || y) {
2882 SkMatrix matrix = SkMatrix::MakeTrans(x, y);
2883 this->onDrawDrawable(dr, &matrix);
2884 } else {
2885 this->onDrawDrawable(dr, nullptr);
reed6a070dc2014-11-11 19:36:09 -08002886 }
2887}
2888
reeda8db7282015-07-07 10:22:31 -07002889void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reede3b38ce2016-01-08 09:18:44 -08002890 RETURN_ON_NULL(dr);
2891 if (matrix && matrix->isIdentity()) {
2892 matrix = nullptr;
reeda8db7282015-07-07 10:22:31 -07002893 }
reede3b38ce2016-01-08 09:18:44 -08002894 this->onDrawDrawable(dr, matrix);
reeda8db7282015-07-07 10:22:31 -07002895}
2896
2897void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) {
reed2a62e852016-10-03 10:35:37 -07002898 // drawable bounds are no longer reliable (e.g. android displaylist)
2899 // so don't use them for quick-reject
reeda8db7282015-07-07 10:22:31 -07002900 dr->draw(this, matrix);
reed6a070dc2014-11-11 19:36:09 -08002901}
2902
reed71c3c762015-06-24 10:29:17 -07002903void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[],
2904 const SkColor colors[], int count, SkXfermode::Mode mode,
2905 const SkRect* cull, const SkPaint* paint) {
2906 if (cull && this->quickReject(*cull)) {
2907 return;
2908 }
2909
2910 SkPaint pnt;
2911 if (paint) {
2912 pnt = *paint;
2913 }
halcanary9d524f22016-03-29 09:03:52 -07002914
halcanary96fcdcc2015-08-27 07:41:13 -07002915 LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr)
reed71c3c762015-06-24 10:29:17 -07002916 while (iter.next()) {
2917 iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt);
2918 }
2919 LOOPER_END
2920}
2921
reedf70b5312016-03-04 16:36:20 -08002922void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
2923 SkASSERT(key);
2924
2925 SkPaint paint;
2926 LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr)
2927 while (iter.next()) {
2928 iter.fDevice->drawAnnotation(iter, rect, key, value);
2929 }
2930 LOOPER_END
2931}
2932
reed@android.com8a1c16f2008-12-17 15:59:43 +00002933//////////////////////////////////////////////////////////////////////////////
2934// These methods are NOT virtual, and therefore must call back into virtual
2935// methods, rather than actually drawing themselves.
2936//////////////////////////////////////////////////////////////////////////////
2937
reed374772b2016-10-05 17:33:02 -07002938void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002939 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawARGB()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002940 SkPaint paint;
2941
2942 paint.setARGB(a, r, g, b);
reed374772b2016-10-05 17:33:02 -07002943 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002944 this->drawPaint(paint);
2945}
2946
reed374772b2016-10-05 17:33:02 -07002947void SkCanvas::drawColor(SkColor c, SkBlendMode mode) {
danakj9881d632014-11-26 12:41:06 -08002948 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawColor()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002949 SkPaint paint;
2950
2951 paint.setColor(c);
reed374772b2016-10-05 17:33:02 -07002952 paint.setBlendMode(mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +00002953 this->drawPaint(paint);
2954}
2955
2956void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002957 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkPaint)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002958 SkPoint pt;
reed@google.com4b226022011-01-11 18:32:13 +00002959
reed@android.com8a1c16f2008-12-17 15:59:43 +00002960 pt.set(x, y);
2961 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2962}
2963
2964void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
danakj9881d632014-11-26 12:41:06 -08002965 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPoint(SkColor)");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002966 SkPoint pt;
2967 SkPaint paint;
reed@google.com4b226022011-01-11 18:32:13 +00002968
reed@android.com8a1c16f2008-12-17 15:59:43 +00002969 pt.set(x, y);
2970 paint.setColor(color);
2971 this->drawPoints(kPoints_PointMode, 1, &pt, paint);
2972}
2973
2974void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
2975 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002976 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawLine()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002977 SkPoint pts[2];
reed@google.com4b226022011-01-11 18:32:13 +00002978
reed@android.com8a1c16f2008-12-17 15:59:43 +00002979 pts[0].set(x0, y0);
2980 pts[1].set(x1, y1);
2981 this->drawPoints(kLines_PointMode, 2, pts, paint);
2982}
2983
2984void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
2985 SkScalar right, SkScalar bottom,
2986 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002987 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRectCoords()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002988 SkRect r;
2989
2990 r.set(left, top, right, bottom);
2991 this->drawRect(r, paint);
2992}
2993
2994void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
2995 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08002996 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawCircle()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00002997 if (radius < 0) {
2998 radius = 0;
2999 }
3000
3001 SkRect r;
3002 r.set(cx - radius, cy - radius, cx + radius, cy + radius);
reed@google.com4ed0fb72012-12-12 20:48:18 +00003003 this->drawOval(r, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003004}
3005
3006void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
3007 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003008 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawRoundRect()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003009 if (rx > 0 && ry > 0) {
reed@google.com4ed0fb72012-12-12 20:48:18 +00003010 SkRRect rrect;
3011 rrect.setRectXY(r, rx, ry);
3012 this->drawRRect(rrect, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003013 } else {
3014 this->drawRect(r, paint);
3015 }
3016}
3017
reed@android.com8a1c16f2008-12-17 15:59:43 +00003018void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
3019 SkScalar sweepAngle, bool useCenter,
3020 const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003021 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawArc()");
bsalomon21af9ca2016-08-25 12:29:23 -07003022 if (oval.isEmpty() || !sweepAngle) {
3023 return;
reed@android.com8a1c16f2008-12-17 15:59:43 +00003024 }
bsalomon21af9ca2016-08-25 12:29:23 -07003025 this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003026}
3027
3028void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
3029 const SkPath& path, SkScalar hOffset,
3030 SkScalar vOffset, const SkPaint& paint) {
danakj9881d632014-11-26 12:41:06 -08003031 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPathHV()");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003032 SkMatrix matrix;
reed@google.com4b226022011-01-11 18:32:13 +00003033
reed@android.com8a1c16f2008-12-17 15:59:43 +00003034 matrix.setTranslate(hOffset, vOffset);
3035 this->drawTextOnPath(text, byteLength, path, &matrix, paint);
3036}
3037
reed@android.comf76bacf2009-05-13 14:00:33 +00003038///////////////////////////////////////////////////////////////////////////////
reed1c2c4412015-04-30 13:09:24 -07003039
3040/**
3041 * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture,
3042 * against the playback cost of recursing into the subpicture to get at its actual ops.
3043 *
3044 * For now we pick a conservatively small value, though measurement (and other heuristics like
3045 * the type of ops contained) may justify changing this value.
3046 */
3047#define kMaxPictureOpsToUnrollInsteadOfRef 1
robertphillips9b14f262014-06-04 05:40:44 -07003048
reedd5fa1a42014-08-09 11:08:05 -07003049void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {
reede3b38ce2016-01-08 09:18:44 -08003050 RETURN_ON_NULL(picture);
3051
reed1c2c4412015-04-30 13:09:24 -07003052 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawPicture()");
reede3b38ce2016-01-08 09:18:44 -08003053 if (matrix && matrix->isIdentity()) {
3054 matrix = nullptr;
3055 }
3056 if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) {
3057 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3058 picture->playback(this);
3059 } else {
3060 this->onDrawPicture(picture, matrix, paint);
reedd5fa1a42014-08-09 11:08:05 -07003061 }
3062}
robertphillips9b14f262014-06-04 05:40:44 -07003063
reedd5fa1a42014-08-09 11:08:05 -07003064void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix,
3065 const SkPaint* paint) {
fmalitad0281802015-08-20 12:08:18 -07003066 if (!paint || paint->canComputeFastBounds()) {
3067 SkRect bounds = picture->cullRect();
3068 if (paint) {
3069 paint->computeFastBounds(bounds, &bounds);
3070 }
3071 if (matrix) {
3072 matrix->mapRect(&bounds);
3073 }
3074 if (this->quickReject(bounds)) {
3075 return;
3076 }
3077 }
3078
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003079 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
robertphillipsc5ba71d2014-09-04 08:42:50 -07003080 picture->playback(this);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003081}
3082
vjiaoblack95302da2016-07-21 10:25:54 -07003083#ifdef SK_EXPERIMENTAL_SHADOWING
3084void SkCanvas::drawShadowedPicture(const SkPicture* picture,
3085 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003086 const SkPaint* paint,
3087 const SkShadowParams& params) {
vjiaoblack95302da2016-07-21 10:25:54 -07003088 RETURN_ON_NULL(picture);
3089
3090 TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawShadowedPicture()");
3091
vjiaoblacke6f5d562016-08-25 06:30:23 -07003092 this->onDrawShadowedPicture(picture, matrix, paint, params);
vjiaoblack95302da2016-07-21 10:25:54 -07003093}
3094
3095void SkCanvas::onDrawShadowedPicture(const SkPicture* picture,
3096 const SkMatrix* matrix,
vjiaoblacke6f5d562016-08-25 06:30:23 -07003097 const SkPaint* paint,
3098 const SkShadowParams& params) {
vjiaoblack904527d2016-08-09 09:32:09 -07003099 if (!paint || paint->canComputeFastBounds()) {
3100 SkRect bounds = picture->cullRect();
3101 if (paint) {
3102 paint->computeFastBounds(bounds, &bounds);
3103 }
3104 if (matrix) {
3105 matrix->mapRect(&bounds);
3106 }
3107 if (this->quickReject(bounds)) {
3108 return;
3109 }
3110 }
3111
3112 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
3113
vjiaoblacke6f5d562016-08-25 06:30:23 -07003114 sk_sp<SkImage> povDepthMap;
3115 sk_sp<SkImage> diffuseMap;
3116
vjiaoblack904527d2016-08-09 09:32:09 -07003117 // povDepthMap
3118 {
3119 SkLights::Builder builder;
vjiaoblack772b5ee2016-08-12 11:38:47 -07003120 builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
3121 SkVector3::Make(0.0f, 0.0f, 1.0f)));
vjiaoblack904527d2016-08-09 09:32:09 -07003122 sk_sp<SkLights> povLight = builder.finish();
3123
3124 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3125 picture->cullRect().height(),
3126 kBGRA_8888_SkColorType,
3127 kOpaque_SkAlphaType);
3128
3129 // Create a new surface (that matches the backend of canvas)
3130 // to create the povDepthMap
3131 sk_sp<SkSurface> surf(this->makeSurface(info));
3132
3133 // Wrap another SPFCanvas around the surface
3134 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3135 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3136
3137 // set the depth map canvas to have the light as the user's POV
3138 depthMapCanvas->setLights(std::move(povLight));
3139
3140 depthMapCanvas->drawPicture(picture);
vjiaoblack904527d2016-08-09 09:32:09 -07003141 povDepthMap = surf->makeImageSnapshot();
3142 }
3143
3144 // diffuseMap
3145 {
3146 SkImageInfo info = SkImageInfo::Make(picture->cullRect().width(),
3147 picture->cullRect().height(),
3148 kBGRA_8888_SkColorType,
3149 kOpaque_SkAlphaType);
3150
3151 sk_sp<SkSurface> surf(this->makeSurface(info));
3152 surf->getCanvas()->drawPicture(picture);
3153
3154 diffuseMap = surf->makeImageSnapshot();
3155 }
vjiaoblack904527d2016-08-09 09:32:09 -07003156
3157 sk_sp<SkShader> povDepthShader = povDepthMap->makeShader(SkShader::kClamp_TileMode,
3158 SkShader::kClamp_TileMode);
vjiaoblack904527d2016-08-09 09:32:09 -07003159 sk_sp<SkShader> diffuseShader = diffuseMap->makeShader(SkShader::kClamp_TileMode,
3160 SkShader::kClamp_TileMode);
vjiaoblackb2796fd2016-09-09 09:22:39 -07003161
3162 // TODO: pass the depth to the shader in vertices, or uniforms
3163 // so we don't have to render depth and color separately
3164 for (int i = 0; i < fLights->numLights(); ++i) {
3165 // skip over ambient lights; they don't cast shadows
3166 // lights that have shadow maps do not need updating (because lights are immutable)
3167 sk_sp<SkImage> depthMap;
3168 SkISize shMapSize;
3169
3170 if (fLights->light(i).getShadowMap() != nullptr) {
3171 continue;
3172 }
3173
3174 if (fLights->light(i).isRadial()) {
3175 shMapSize.fHeight = 1;
3176 shMapSize.fWidth = (int) picture->cullRect().width();
3177
3178 SkImageInfo info = SkImageInfo::Make(diffuseMap->width(), 1,
3179 kBGRA_8888_SkColorType,
3180 kOpaque_SkAlphaType);
3181
3182 // Create new surface (that matches the backend of canvas)
3183 // for each shadow map
3184 sk_sp<SkSurface> surf(this->makeSurface(info));
3185
3186 // Wrap another SPFCanvas around the surface
3187 SkCanvas* depthMapCanvas = surf->getCanvas();
3188
3189 SkLights::Builder builder;
3190 builder.add(fLights->light(i));
3191 sk_sp<SkLights> curLight = builder.finish();
3192
3193 sk_sp<SkShader> shadowMapShader;
3194 shadowMapShader = SkRadialShadowMapShader::Make(
3195 povDepthShader, curLight,
3196 (int) picture->cullRect().width(),
3197 (int) picture->cullRect().height());
3198
3199 SkPaint shadowMapPaint;
3200 shadowMapPaint.setShader(std::move(shadowMapShader));
3201
3202 depthMapCanvas->setLights(curLight);
3203
3204 depthMapCanvas->drawRect(SkRect::MakeIWH(diffuseMap->width(),
3205 diffuseMap->height()),
3206 shadowMapPaint);
3207
3208 depthMap = surf->makeImageSnapshot();
3209
3210 } else {
3211 // TODO: compute the correct size of the depth map from the light properties
3212 // TODO: maybe add a kDepth_8_SkColorType
3213 // TODO: find actual max depth of picture
3214 shMapSize = SkShadowPaintFilterCanvas::ComputeDepthMapSize(
3215 fLights->light(i), 255,
3216 (int) picture->cullRect().width(),
3217 (int) picture->cullRect().height());
3218
3219 SkImageInfo info = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3220 kBGRA_8888_SkColorType,
3221 kOpaque_SkAlphaType);
3222
3223 // Create a new surface (that matches the backend of canvas)
3224 // for each shadow map
3225 sk_sp<SkSurface> surf(this->makeSurface(info));
3226
3227 // Wrap another SPFCanvas around the surface
3228 sk_sp<SkShadowPaintFilterCanvas> depthMapCanvas =
3229 sk_make_sp<SkShadowPaintFilterCanvas>(surf->getCanvas());
3230 depthMapCanvas->setShadowParams(params);
3231
3232 // set the depth map canvas to have the light we're drawing.
3233 SkLights::Builder builder;
3234 builder.add(fLights->light(i));
3235 sk_sp<SkLights> curLight = builder.finish();
3236 depthMapCanvas->setLights(std::move(curLight));
3237
3238 depthMapCanvas->drawPicture(picture);
3239 depthMap = surf->makeImageSnapshot();
3240 }
3241
3242 if (params.fType == SkShadowParams::kNoBlur_ShadowType) {
3243 fLights->light(i).setShadowMap(std::move(depthMap));
3244 } else if (params.fType == SkShadowParams::kVariance_ShadowType) {
3245 // we blur the variance map
3246 SkPaint blurPaint;
3247 blurPaint.setImageFilter(SkImageFilter::MakeBlur(params.fShadowRadius,
3248 params.fShadowRadius, nullptr));
3249
3250 SkImageInfo blurInfo = SkImageInfo::Make(shMapSize.fWidth, shMapSize.fHeight,
3251 kBGRA_8888_SkColorType,
3252 kOpaque_SkAlphaType);
3253
3254 sk_sp<SkSurface> blurSurf(this->makeSurface(blurInfo));
3255
3256 blurSurf->getCanvas()->drawImage(std::move(depthMap), 0, 0, &blurPaint);
3257
3258 fLights->light(i).setShadowMap(blurSurf->makeImageSnapshot());
3259 }
3260 }
3261
3262 SkPaint shadowPaint;
vjiaoblack904527d2016-08-09 09:32:09 -07003263 sk_sp<SkShader> shadowShader = SkShadowShader::Make(std::move(povDepthShader),
3264 std::move(diffuseShader),
vjiaoblackb2796fd2016-09-09 09:22:39 -07003265 fLights,
vjiaoblack904527d2016-08-09 09:32:09 -07003266 diffuseMap->width(),
vjiaoblacke6f5d562016-08-25 06:30:23 -07003267 diffuseMap->height(),
3268 params);
vjiaoblack904527d2016-08-09 09:32:09 -07003269
3270 shadowPaint.setShader(shadowShader);
3271
3272 this->drawRect(SkRect::MakeIWH(diffuseMap->width(), diffuseMap->height()), shadowPaint);
vjiaoblack95302da2016-07-21 10:25:54 -07003273}
3274#endif
3275
reed@android.com8a1c16f2008-12-17 15:59:43 +00003276///////////////////////////////////////////////////////////////////////////////
3277///////////////////////////////////////////////////////////////////////////////
3278
reed3aafe112016-08-18 12:45:34 -07003279SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) {
bungeman99fe8222015-08-20 07:57:51 -07003280 static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small");
reed@android.com8a1c16f2008-12-17 15:59:43 +00003281
3282 SkASSERT(canvas);
3283
reed3aafe112016-08-18 12:45:34 -07003284 fImpl = new (fStorage) SkDrawIter(canvas);
reed@android.com8a1c16f2008-12-17 15:59:43 +00003285 fDone = !fImpl->next();
3286}
3287
3288SkCanvas::LayerIter::~LayerIter() {
3289 fImpl->~SkDrawIter();
3290}
3291
3292void SkCanvas::LayerIter::next() {
3293 fDone = !fImpl->next();
3294}
3295
robertphillips@google.com1f2f3382013-08-29 11:54:56 +00003296SkBaseDevice* SkCanvas::LayerIter::device() const {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003297 return fImpl->getDevice();
3298}
3299
3300const SkMatrix& SkCanvas::LayerIter::matrix() const {
3301 return fImpl->getMatrix();
3302}
3303
3304const SkPaint& SkCanvas::LayerIter::paint() const {
3305 const SkPaint* paint = fImpl->getPaint();
halcanary96fcdcc2015-08-27 07:41:13 -07003306 if (nullptr == paint) {
reed@android.com8a1c16f2008-12-17 15:59:43 +00003307 paint = &fDefaultPaint;
3308 }
3309 return *paint;
3310}
3311
reed1e7f5e72016-04-27 07:49:17 -07003312const SkRasterClip& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
reed@android.com8a1c16f2008-12-17 15:59:43 +00003313int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
3314int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
justinlin@google.com20a550c2012-07-27 17:37:12 +00003315
3316///////////////////////////////////////////////////////////////////////////////
3317
fmalitac3b589a2014-06-05 12:40:07 -07003318SkCanvasClipVisitor::~SkCanvasClipVisitor() { }
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003319
3320///////////////////////////////////////////////////////////////////////////////
3321
3322static bool supported_for_raster_canvas(const SkImageInfo& info) {
3323 switch (info.alphaType()) {
3324 case kPremul_SkAlphaType:
3325 case kOpaque_SkAlphaType:
3326 break;
3327 default:
3328 return false;
3329 }
3330
3331 switch (info.colorType()) {
3332 case kAlpha_8_SkColorType:
3333 case kRGB_565_SkColorType:
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +00003334 case kN32_SkColorType:
junov9e3dbdf2016-10-13 13:14:27 -07003335 case kRGBA_F16_SkColorType:
commit-bot@chromium.org3107b6a2014-02-27 20:32:51 +00003336 break;
3337 default:
3338 return false;
3339 }
3340
3341 return true;
3342}
3343
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003344SkCanvas* SkCanvas::NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes) {
3345 if (!supported_for_raster_canvas(info)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003346 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003347 }
skia.committer@gmail.comeb849e52014-03-17 03:02:17 +00003348
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003349 SkBitmap bitmap;
3350 if (!bitmap.installPixels(info, pixels, rowBytes)) {
halcanary96fcdcc2015-08-27 07:41:13 -07003351 return nullptr;
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003352 }
halcanary385fe4d2015-08-26 13:07:48 -07003353 return new SkCanvas(bitmap);
commit-bot@chromium.org42b08932014-03-17 02:13:07 +00003354}
reedd5fa1a42014-08-09 11:08:05 -07003355
3356///////////////////////////////////////////////////////////////////////////////
3357
3358SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003359 const SkPaint* paint, const SkRect& bounds)
reedd5fa1a42014-08-09 11:08:05 -07003360 : fCanvas(canvas)
3361 , fSaveCount(canvas->getSaveCount())
3362{
bsalomon49f085d2014-09-05 13:34:00 -07003363 if (paint) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003364 SkRect newBounds = bounds;
reedd5fa1a42014-08-09 11:08:05 -07003365 if (matrix) {
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003366 matrix->mapRect(&newBounds);
reedd5fa1a42014-08-09 11:08:05 -07003367 }
robertphillipsa8d7f0b2014-08-29 08:03:56 -07003368 canvas->saveLayer(&newBounds, paint);
bsalomon49f085d2014-09-05 13:34:00 -07003369 } else if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003370 canvas->save();
3371 }
mtklein6cfa73a2014-08-13 13:33:49 -07003372
bsalomon49f085d2014-09-05 13:34:00 -07003373 if (matrix) {
reedd5fa1a42014-08-09 11:08:05 -07003374 canvas->concat(*matrix);
3375 }
3376}
3377
3378SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
3379 fCanvas->restoreToCount(fSaveCount);
3380}
reede8f30622016-03-23 18:59:25 -07003381
3382#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API
3383SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) {
3384 return this->makeSurface(info, props).release();
3385}
3386#endif
reed73603f32016-09-20 08:42:38 -07003387
3388/////////////////////////////////
3389
3390const SkCanvas::ClipOp SkCanvas::kDifference_Op;
3391const SkCanvas::ClipOp SkCanvas::kIntersect_Op;
3392const SkCanvas::ClipOp SkCanvas::kUnion_Op;
3393const SkCanvas::ClipOp SkCanvas::kXOR_Op;
3394const SkCanvas::ClipOp SkCanvas::kReverseDifference_Op;
3395const SkCanvas::ClipOp SkCanvas::kReplace_Op;
3396
3397static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "");
3398static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "");
3399static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "");
3400static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
3401static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
3402static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");